Interoperability: Omni Automation and OpenAI

DISCLAIMER: Mention of third-party websites and products is for informational purposes only and constitutes neither an endorsement nor a recommendation. OMNI-AUTOMATION.COM assumes no responsibility with regard to the selection, performance or use of information or products found at third-party websites. OMNI-AUTOMATION.COM provides this only as a convenience to our users. OMNI-AUTOMATION.COM has not tested the information found on these sites and makes no representations regarding its accuracy or reliability. There are risks inherent in the use of any information or products found on the Internet, and OMNI-AUTOMATION.COM assumes no responsibility in this regard. Please understand that a third-party site is independent from OMNI-AUTOMATION.COM and that OMNI-AUTOMATION.COM has no control over the content on that website. Please contact the vendor for additional information.

*OpenAI is an artificial intelligence research laboratory consisting of the for-profit corporation OpenAI LP and its parent company, the non-profit OpenAI Inc. The company was founded in 2015 by a group of entrepreneurs and AI researchers, including Elon Musk, Sam Altman, Greg Brockman, Ilya Sutskever, and John Schulman.”

*“OpenAI is focused on developing advanced AI technologies and ensuring that the benefits of these technologies are shared with everyone. It has developed a range of AI models and applications, including the GPT series of language models, which have been used for natural language processing tasks such as language translation and text generation. OpenAI is also involved in research on robotics, healthcare, and climate change.”

(The OpenAI Charter)

*According to OpenAI

Interoperability with Omni Automation

The flexible and inclusive design of the Omni Automation APIs, especially those classes listed below, allow OpenAI’s tools to be accessed directly from within Omni applications, and the returned data from their queries can be incorporated within the documents of the suite of Omni productivity applications.

The Omni Automation plug-ins detailed here on this webpage:

These plug-ins demonstrate the ability of Omni Automation to enable secure approved access to network services, and are adaptations of some of the use-case examples provided on the OpenAI website.

The Omni Automation OpenAI Plug-Ins

With each of the example plug-ins, the user enters a prompt string into the displayed dialog input field and is returned the first response message from the OpenAI model engine results. Depending on the plug-in used, the responses will be either text, an image URL, or image data encoded in Base64 format. The full result array of messages is logged into the host Omni application’s built-in automation console. In addition, the content of the first message is placed on the clipboard.

These example plug-ins use the following built-in classes to securely store and retrieve plug-in credentials and preferences, as well as classes to integrate RESTful API support, enabling approved encrypted network access to the OpenAI servers:

IMPORTANT: The network communication protocols used by these example plug-ins follow the guidelines detailed in the OpenAI API Reference Guide. These guidelines stipulate the requirement of an approved and verified OpenAI account with both an Organization ID and an API Key.

Once entered by the user into the plug-in interfaces, these tokens are stored in the secure Apple system keychain, provided on the host Apple device (iPad, iPhone, Mac). Any subsequent executions of the plug-ins will access the stored credentials from the keychain and will not require further user interaction.

NOTE: If you choose to activate the plug-in’s logging preference, your tokens will be visible in the application console.

IMPORTANT: Each plug-in’s default request settings are suggested starting points and may be adjusted by you based upon your own testing in consultation with the tools and suggestions found in the OpenAI API Reference Guide.

🔖 <-- copy link

Using the OpenAI: Outline for Essay Plug-In

The plug-in is designed to work with the OmniOutliner application. Based upon the user-provided prompt, a two-level outline is generated and optionally imported into the frontmost document.

OpenAI: Outline for Essay
Using the OpenAI: Outline for Essay plug-in with OmniOutliner
NOTE: (below) The split-view showing the OmniOutliner automation console window is provided to reveal what occurs “in the background” during execution of the plug-in.

⬇️ DOWNLOAD PLUG-IN

Special features:

When first launched, the plug-in will prompt for your OpenAI provided Organization ID and API Key

The dialog for entering your OpenAI Organization ID and API Key

Once entered, the ID and Key will be stored securely in the system keychain, and the plug-in will not prompt for them again.

For subsequent launchings of the plug-in, the query input dialog will be presented:

Input dialog for OmniOutliner OpeAI plug-in

The “Prompt” text is designed to generate an outline. Select the “XXXXX” placeholder and complete the provided phrase with the details of your request:

Create an outline for an essay about the transition from fossil fuels to renewable resources:”

Create an outline for an essay about Nikola Tesla and his contributions to technology:”

The “Temperature” setting (“Temp”) refers to the degree of randomness or creativity in the responses generated by the model. A higher temperature setting will produce more unpredictable and diverse responses, while a lower setting will produce more conservative and predictable responses.

The “Tokens” setting refers to the maximum number of tokens to use to generate the response. (One token is roughly 4 characters for normal English text). The popup menu offers the options: 150, 300, 450, and 600. FYI: Requests can use up to 2,048 or 4,000 tokens shared between prompt and completion. The exact limit varies by model.

If the query is successful, an alert will post, providing an option to import the cleaned outline into the frontmost document. The full OpenAI request results are logged in the console and the first resulting message is also copied to the clipboard automatically for you.

The success alert in OmniOutliner

The query results are also logged in the host Omni application’s console:

The console window displaying the request results
🔖 <-- copy link

Using the OpenAI: Ordered List Plug-In

The plug-in is designed to work with the OmniOutliner application. Based upon the user-provided prompt, an ordered list is generated and optionally imported into the frontmost document.

OpenAI: Ordered List
Using the OpenAI: Ordered List plug-in with OmniOutliner
NOTE: (below) The split-view showing the OmniOutliner automation console window is provided to reveal what occurs “in the background” during execution of the plug-in.

⬇️ DOWNLOAD PLUG-IN

Special features:

When first launched, the plug-in will prompt for your OpenAI provided Organization ID and API Key

The dialog for entering your OpenAI Organization ID and API Key

Once entered, the ID and Key will be stored securely in the system keychain, and the plug-in will not prompt for them again.

For subsequent launchings of the plug-in, the query input dialog will be presented:

The topic input dialog for the ordered list plug-in

The “Prompt” text is designed to generate an ordered list. Select the “XXXXX” placeholder and complete the provided phrase with the details of your request:

List the steps for adding solar power to a home:”

Or completely replace the default prompt with a question or request that implies an ordered list as a result:

“What are the top 10 grossing movies of all time?”

The “Temperature” setting (“Temp”) refers to the degree of randomness or creativity in the responses generated by the model. A higher temperature setting will produce more unpredictable and diverse responses, while a lower setting will produce more conservative and predictable responses.

The “Tokens” setting refers to the maximum number of tokens to use to generate the response. (One token is roughly 4 characters for normal English text). The popup menu offers the options: 150, 300, 450, and 600. FYI: Requests can use up to 2,048 or 4,000 tokens shared between prompt and completion. The exact limit varies by model.

If the query is successful, an alert will post, providing an option to import the cleaned outline into the frontmost document. The full OpenAI request results are logged in the console and the first resulting message is also copied to the clipboard automatically for you.

The success alert with an option to import items

The query results are also logged in the host Omni application’s console:

The console showing the results of an ordered list request
🔖 <-- copy link

Using the OpenAI: Generate Image Plug-In

This plug-in enables the ability to generate an image based-upon the user-provided description. The plug-in is designed to work with all of the Omni applications but offers an import option when used within OmniGraffle.

OpenAI: Generate Image
Using an OpenAI plug-in with OmniGraffle

⬇️ DOWNLOAD PLUG-IN

Special features:

When the plug-in is activated, the query input dialog will be presented:

The input dialog for the OpenAI Image plug-in

Enter the description of the image to be generated in the Query text input field. For example: “A drawing of the Sun with a happy face”

Select an image size from the Size popup menu. Options include: 1024x1024, 512x512, and 256x256

Query results can be returned as a URL to the image file (default), or as a stream of Base64 data. Select this checkbox to return the image as Base64 data.

If the query is successful, an alert will be presented and the full response will be logged to the automation console.

The automation console displaying resulting URL:

automation console diaplaying resulting URL

NOTE: If the image data (Base64) option is chosen, the option to import the image to the current canvas with be provided (left illustration), otherwise an option to open the returned URL in the default browser will be provided. (right illustration)

Success alert with import option chatgpt image success alert with open url option
🔖 <-- copy link

Using the OpenAI: Color for Description Plug-In

This plug-in will generate the RGB values for a color based upon the user’s description.

OpenAI: Generate Color Swatches
An OpenAI OmniGraffle plug-in creates swatches based upon color descriptions

⬇️ DOWNLOAD PLUG-IN

Special features:

When the plug-in is activated, the prompt input dialog will be presented. Select the “XXXXX” placeholder and complete the provided phrase with the description of your desired color:

The color description input dialog

For example:

Once the OpenAI response is processed, a response alert appears:

The successful query response alert showing that there are multiple options logged in the console

The CSS color code is automatically placed on the clipboard for you.

If the “Paste Color” button is activated:

In addition, each selected or generated graphic will have metadata added to it detailing the description, RGB values, and CSS (hex) color code.

The OmniGraffle console displaying the results with mutliple options
🔖 <-- copy link

Using the OpenAI: Color Palette Plug-In

This plug-in creates an RGB color palette of swatches, whose colors are derived from the user description.

OpenAI: Color Palette
Creates an RGB color palette of swatches, whose colors are derived from the user description.

⬇️ DOWNLOAD PLUG-IN

Special features:

When the plug-in is activated, the prompt input dialog will be presented. Confirm the notice to sent the query to OpenAI:

The input for describing a color palette

The response is automatically logged to the automation console:

The console showing the results of the query
🔖 <-- copy link

Using the OpenAI: Question/Answer Plug-In

This plug-in will speak the answer from a factual question regarding general knowledge. The response will be logged to the console and placed on the clipboard.

OpenAI: Question/Answer
Plug-in will speak the answer for a factual question regarding general knowledge

⬇️ DOWNLOAD PLUG-IN

Special features:

When the plug-in is activated, the prompt input dialog will be presented. enter the question which has a factual answer:

question/answer input dialog

For example:

Once the OpenAI response is processed, a response alert appears:

The question/answer response alert

The answer response is automatically logged to the automation console and placed on the clipboard for you.

The question/asnwer console log
🔖 <-- copy link

Using the OpenAI: Suggest Project Actions

This plug-in will use the title and note of the selected OmniFocus project to request suggested actions from the OpenAI GPT service. If the query is successful, the option to insert the actions into the selected project will be provided. In addition, the query response will be logged to the console and placed on the clipboard.

OpenAI: Suggest Actions
Plug-in will use the title and note of the selected project to request suggested actions.

⬇️ DOWNLOAD PLUG-IN

Special features:

When the plug-in is activated, the prompt dialog will be presented, informing that data concerning the selected project (title and note) will be sent to a third-party network service.

The initial prompt alert in OmniFocus

Once the OpenAI response is processed, a response alert appears, offering the option to append the returned list of actions into the selected project:

The success alert dialog with option to insert actions into selected project

The answer response is automatically logged to the automation console and placed on the clipboard for you.

The console window showing query results
🔖 <-- copy link

Using the OpenAI: Top-10-Heat-Map

Using Voice Control and this plug-in to create a heat map in OmniGraffle (using the default U.S. Map stencil) showing the Top-10 States of the United States in terms of a provided query.

OpenAI: Top-10 Heat Map
Using Voice Control and a plug-in to create a heat map (using the default U.S. Map stencil) showing the Top-10 States of the United States in terms of a provided query.

⬇️ DOWNLOAD PLUG-IN

Special features:

When the plug-in is activated, the prompt dialog will be presented, in which you enter the query and choose the color for the heat map.

For example, a query could be: “percentage of citizens over 60”

Prompt dialog for the Heat Map plug-in

Once the OpenAI response is processed, a response alert appears, offering the option to read the top-10 list. In addition, individual state results are written into the metadata of each states and can be viewed by placing the cursor over the state.

The Top-10 States Het Map

The answer response is automatically logged to the automation console and placed on the clipboard for you. In addition, the query result content is written into the metadata field for the current canvas.

Console results for U.S. Heat Map
OpenAI: Outline for Essay Plug-In
 

/*{ "type": "action", "targets": ["omnioutliner"], "author": "Otto Automator", "identifier": "com.omni-automation.oo.open-ai-essay-outline", "version": "1.0", "description": "This plug-in uses the Credentials class to create and retrieve log-in pairs. NOTE: to clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu. The “temperature”; setting refers to the degree of randomness or creativity in the responses generated by the model. A higher temperature setting will produce more unpredictable and diverse responses, while a lower setting will produce more conservative and predictable responses.", "label": "OpenAI: Outline for Essay", "shortLabel": "Essay Outline", "paletteLabel": "Essay Outline", "image": "wand.and.stars" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/completions" var serviceModel = "text-davinci-003" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) alert = new Alert("Successful Query", "The response has been logged in the console.") alert.addOption("Import Outline") alert.addOption("Done") alert.show(buttonIndex => { //responseStr = json.choices[0].message.content responseStr = json.choices[0].text Pasteboard.general.string = responseStr if (buttonIndex === 0){ text = Pasteboard.general.string sections = text.split("\n\n") console.log(sections.length) sections.forEach(section => { if(section.length !== 0){ items = section.split("\n") if(shouldLog){console.log("SECTION:", items)} // items[0] = section head sectionHead = items[0] if(shouldLog){console.log("SECTION HEAD:", sectionHead)} index = sectionHead.indexOf(".") sectionTopic = sectionHead.substring(index + 2) if(shouldLog){console.log("SECTION TOPIC:", sectionTopic)} sectionItem = rootItem.addChild( null, function(child){ child.topic = sectionTopic } ) items.shift() if(shouldLog){console.log("ITEMS:", items)} items.forEach(item => { index = item.indexOf(".") itemTopic = item.substring(index + 2) if(shouldLog){console.log("ITEM TOPIC:", itemTopic)} sectionItem.addChild( null, function(child){ child.topic = itemTopic } ) }) } }) nodes = document.editors[0].rootNode.children nodes.forEach(node => { if(node.canExpand){node.expand(true)} }) } }) } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ // CREATE TEXT FIELD OBJECT defaultQueryString = "Create an outline for an essay about XXXXX:" textInputField = new Form.Field.String( "textInput", "Prompt", defaultQueryString, null ) temperatureValues = [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0] displayValues = ["100", "90", "80", "70", "60", "50", "40", "30", "20", "10", "0"] temperatureMenu = new Form.Field.Option( "temperatureSetting", "“Temp”", temperatureValues, displayValues, 30 ) tokenValues = [150, 300, 450, 600] displayTokenValues = ["150", "300", "450", "600"] tokensMenu = new Form.Field.Option( "maxTokens", "Tokens", tokenValues, displayTokenValues, 300 ) // CREATE NEW FORM AND ADD FIELD var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(temperatureMenu) inputForm.addField(tokensMenu) // VALIDATE USER INPUT inputForm.validate = function(formObject){ textInput = formObject.values["textInput"] if (!textInput){return false} return true } // DISPLAY THE FORM var formPrompt = "Enter the topic prompt:" var buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // RETRIVE FORM INPUT textInput = formObject.values["textInput"] temperatureSetting = formObject.values["temperatureSetting"] temperatureValue = Math.round((temperatureSetting * 0.01) * 10) / 10 maxTokens = formObject.values["maxTokens"] // LOG THE PROMPT console.log("# PROMPT:", textInput) escapedText = encodeURIComponent(textInput) requestObj = { "model": serviceModel, "prompt": escapedText, "temperature": temperatureValue, "max_tokens": maxTokens, "top_p": 1, "frequency_penalty": 0.2, "presence_penalty": 0 } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();
OpenAI: Generate Image Plug-In
 

/*{ "type": "action", "targets": ["omnigraffle"], "author": "Otto Automator", "identifier": "com.omni-automation.all.openai-generate-image", "version": "1.0", "description": "This plug-in uses the Credentials class to create and retrieve log-in pairs. NOTE: to clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu.", "label": "OpenAI: Generate Image", "shortLabel": "OpenAI: Generate Image", "paletteLabel": "OpenAI: Generate Image", "image": "photo" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/images/generations" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { imageFormat = requestObj["response_format"] organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) alert = new Alert("Successful Query", "The response has been logged in the console.") if(imageFormat === "url"){alert.addOption("Open Image URL")} if(imageFormat === "b64_json"){ alert.addOption("Import Image") } alert.addOption("Done") alert.show(buttonIndex => { if(imageFormat === "url"){ responseURL = json.data[0].url Pasteboard.general.string = responseURL if (buttonIndex === 0){ URL.fromString(responseURL).open() } } else { responseData = json.data[0].b64_json Pasteboard.general.string = responseData if(buttonIndex === 0){ imageData = Data.fromBase64(responseData) cnvs = document.windows[0].selection.canvas solid = cnvs.newShape() solid.strokeThickness = 0 solid.shadowColor = null solid.fillColor = null solid.image = addImage(imageData) newGeometry = solid.geometry newGeometry.size = solid.image.originalSize solid.imageSizing = ImageSizing.Stretched solid.geometry = newGeometry } } }) } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ // CREATE TEXT FIELD OBJECT textInputField = new Form.Field.String( "textInput", "Prompt", null, null ) menuIndex = preferences.readNumber("imageSizeIndex") if(menuIndex === null){ menuIndex = 1 } sizeIndexes = [0, 1, 2] sizeValues = ["1024x1024", "512x512", "256x256"] imageSizeMenu = new Form.Field.Option( "imageSize", "Size", sizeIndexes, sizeValues, menuIndex ) booleanValue = preferences.readBoolean("shouldReturnAsBase64") if(!booleanValue){ booleanValue = false } shouldReturnAsBase64 = new Form.Field.Checkbox( "shouldReturnAsBase64", "Return image data instead of URL", booleanValue ) // CREATE NEW FORM AND ADD FIELD var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(imageSizeMenu) inputForm.addField(shouldReturnAsBase64) // VALIDATE USER INPUT inputForm.validate = function(formObject){ textInput = formObject.values["textInput"] if (!textInput){return false} return true } // DISPLAY THE FORM var formPrompt = "Image Description and Size:" var buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // RETRIVE FORM INPUT textInput = formObject.values["textInput"] imageSizeIndex = formObject.values["imageSize"] console.log("imageSizeIndex", imageSizeIndex) imageSize = sizeValues[imageSizeIndex] shouldReturnAsBase64 = formObject.values["shouldReturnAsBase64"] imageFormat = (shouldReturnAsBase64) ? "b64_json" : "url" preferences.write("shouldReturnAsBase64", shouldReturnAsBase64) preferences.write("imageSizeIndex", imageSizeIndex) // LOG PROMPT console.log("# PROMPT", textInput) escapedText = encodeURIComponent(textInput) requestObj = { "prompt": escapedText, "n": 1, "size": imageSize, "response_format": imageFormat } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();
OpenAI: Color for Description
 

/*{ "type": "action", "targets": ["omnigraffle"], "author": "Otto Automator", "identifier": "com.omni-automation.og.rgb-color-values-for-description", "version": "1.0", "description": "Returns the RGB color values derived from the user description. This plug-in uses the Credentials class to create and retrieve log-in pairs. NOTE: to clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu.", "label": "OpenAI: Color for Description", "shortLabel": "Color for Description", "paletteLabel": "Color for Description", "image": "wand.and.stars" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/completions" var serviceModel = "text-davinci-003" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog var promptString function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) responseStr = json.choices[0].text valueStringArray = responseStr.match(/\d+/g) var RGBStr, alertString if(valueStringArray.length > 3){ valueStringArray = [ valueStringArray[0], valueStringArray[1], valueStringArray[2] ] RGBStr = valueStringArray.join(", ") alertString = "Multiple value sets (see console)," alertString += "\nfirst RGB values are: " + RGBStr } else { RGBStr = valueStringArray.join(", ") alertString = "The RGB values are: " + RGBStr } Pasteboard.general.string = `(${RGBStr})` alert = new Alert("Successful Query", alertString) alert.addOption("Paste Color") alert.addOption("Done") alert.show(buttonIndex => { valueStringArray = RGBStr.split(", ") rVal = parseInt(valueStringArray[0]) / 255 gVal = parseInt(valueStringArray[1]) / 255 bVal = parseInt(valueStringArray[2]) / 255 colorObj = Color.RGB(rVal, gVal, bVal, 1) console.log("RED VALUE:", rVal) console.log("GREEN VALUE:", gVal) console.log("BLUE VALUE:", bVal) if (buttonIndex === 0){ var graphics = document.windows[0].selection.graphics if(graphics.length === 0){ cnvs = document.windows[0].selection.canvas solid = cnvs.newShape() solid.shape = 'Rectangle' solid.geometry = new Rect(0, 0, 200, 200) graphics = [solid] } graphics.forEach(graphic => { graphic.fillColor = colorObj graphic.setUserData("Description", promptString) graphic.setUserData("RGB", RGBStr) }) } }) } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ console.error(err.name, err.message) new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ // CREATE TEXT FIELD OBJECT defaultQueryString = "The RGB values for a color like XXXXX:" textInputField = new Form.Field.String( "textInput", "Prompt", defaultQueryString, null ) // CREATE NEW FORM AND ADD FIELD var inputForm = new Form() inputForm.addField(textInputField) // VALIDATE USER INPUT inputForm.validate = function(formObject){ textInput = formObject.values["textInput"] if (!textInput){return false} return true } // DISPLAY THE FORM var formPrompt = "Complete the description:" var buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // RETRIVE FORM INPUT textInput = formObject.values["textInput"] // LOG THE PROMPT console.log("# PROMPT:", textInput) promptString = textInput escapedText = encodeURIComponent(textInput) requestObj = { "model": serviceModel, "prompt": escapedText, "temperature": 0, "max_tokens": 100, "top_p": 1.0, "frequency_penalty": 0.0, "presence_penalty": 0.0, "stop": [";"] } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();
OpenAI: Ordered List
 

/*{ "type": "action", "targets": ["omnioutliner"], "author": "Otto Automator", "identifier": "com.omni-automation.oo.open-ai-ordered-list", "version": "1.0", "description": "This plug-in uses the Credentials class to create and retrieve log-in pairs. NOTE: to clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu. The “temperature”; setting refers to the degree of randomness or creativity in the responses generated by the model. A higher temperature setting will produce more unpredictable and diverse responses, while a lower setting will produce more conservative and predictable responses.", "label": "OpenAI: Ordered List", "shortLabel": "Ordered List", "paletteLabel": "Ordered List", "image": "wand.and.stars" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/completions" var serviceModel = "text-davinci-003" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) alert = new Alert("Successful Query", "The response has been logged in the console.") alert.addOption("Import List Items") alert.addOption("Done") alert.show(buttonIndex => { responseStr = json.choices[0].text Pasteboard.general.string = responseStr if (buttonIndex === 0){ graphs = responseStr.split("\n") listItems = new Array() numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] graphs.forEach(graph => { firstChar = graph.charAt(0) if(numbers.includes(firstChar)){ listItem = graph.replace(/^[0-9]+. /g, '') listItems.push(listItem) } }) listItems.forEach(item => { rootItem.addChild( null, function(child){ child.topic = item } ) }) } }) } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ // CREATE TEXT FIELD OBJECT defaultQueryString = "List the steps for XXXXX:" textInputField = new Form.Field.String( "textInput", "Prompt", defaultQueryString, null ) temperatureValues = [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0] displayValues = ["100", "90", "80", "70", "60", "50", "40", "30", "20", "10", "0"] temperatureMenu = new Form.Field.Option( "temperatureSetting", "“Temp”", temperatureValues, displayValues, 30 ) tokenValues = [150, 300, 450, 600] displayTokenValues = ["150", "300", "450", "600"] tokensMenu = new Form.Field.Option( "maxTokens", "Tokens", tokenValues, displayTokenValues, 150 ) // CREATE NEW FORM AND ADD FIELD var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(temperatureMenu) inputForm.addField(tokensMenu) // VALIDATE USER INPUT inputForm.validate = function(formObject){ textInput = formObject.values["textInput"] if (!textInput){return false} return true } // DISPLAY THE FORM var formPrompt = "Complete this topic prompt:" var buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // RETRIVE FORM INPUT textInput = formObject.values["textInput"] temperatureSetting = formObject.values["temperatureSetting"] temperatureValue = Math.round((temperatureSetting * 0.01) * 10) / 10 maxTokens = formObject.values["maxTokens"] // LOG THE PROMPT console.log("# PROMPT:", textInput) escapedText = encodeURIComponent(textInput) requestObj = { "model": serviceModel, "prompt": escapedText, "temperature": temperatureValue, "max_tokens": maxTokens, "top_p": 1, "frequency_penalty": 0.2, "presence_penalty": 0 } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();
OpenAI: Question Answer
 

/*{ "type": "action", "targets": ["omnigraffle","omnioutliner","omniplan","omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.all.question-answer", "version": "1.0", "description": "Speaks the answer to the user-provided question. The answer is also copied to the clipboard. This plug-in uses the Credentials class to create and retrieve log-in pairs. NOTE: to clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu.", "label": "OpenAI: Question/Answer", "shortLabel": "Question/Answer", "paletteLabel": "Question/Answer", "image": "wand.and.stars" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/completions" var serviceModel = "text-davinci-003" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog var promptString function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) responseStr = json.choices[0].text responseStr = responseStr.trim() Pasteboard.general.string = responseStr utterance = createUtterance(responseStr) synthesizer = new Speech.Synthesizer() synthesizer.speakUtterance(utterance) alertMessage = "The response has been logged to the console " alertMessage += "and placed on the clipboard." alert = new Alert("Successful Query", alertMessage) alert.addOption("Stop Speaking") alert.show(buttonIndex => { if (buttonIndex === 0){ synthesizer.stopSpeaking(Speech.Boundary.Word) } }) } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ console.error(err.name, err.message) new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ // CREATE TEXT FIELD OBJECT defaultQueryString = null textInputField = new Form.Field.String( "textInput", "Prompt", defaultQueryString, null ) // CREATE NEW FORM AND ADD FIELD var inputForm = new Form() inputForm.addField(textInputField) // VALIDATE USER INPUT inputForm.validate = function(formObject){ textInput = formObject.values["textInput"] if (!textInput){return false} return true } // DISPLAY THE FORM var formPrompt = "Enter the question:" var buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // RETRIVE FORM INPUT textInput = formObject.values["textInput"] // LOG THE PROMPT console.log("# PROMPT:", textInput) promptString = textInput escapedText = encodeURIComponent(textInput) requestObj = { "model": serviceModel, "prompt": escapedText, "temperature": 0, "max_tokens": 100, "top_p": 1.0, "frequency_penalty": 0.0, "presence_penalty": 0.0, "stop": [";"] } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();
OpenAI: Color Palette
 

/*{ "type": "action", "targets": ["omnigraffle"], "author": "Otto Automator", "identifier": "com.omni-automation.og.rgb-color-palette-for-description", "version": "1.0", "description": "Creates an RGB color palette of swatches, whose colors are derived from the user description. This plug-in uses the Credentials class to create and retrieve log-in pairs. NOTE: to clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu.", "label": "OpenAI: Color Palette", "shortLabel": "Color Palette", "paletteLabel": "Color Palette", "image": "wand.and.stars" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/completions" var serviceModel = "text-davinci-003" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog var promptString function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) responseStr = json.choices[0].text lineStarts = ["1","2","3","4","5","6","7","8","9","0","-"] colorDataObjs = [] graphs = responseStr.split("\n") graphs.forEach(graph => { firstChar = graph.charAt(0) if(lineStarts.includes(firstChar)){ indexA = graph.indexOf(" ") indexZ = graph.indexOf(":") colorName = graph.substring(indexA, indexZ) colorName = colorName.trim() console.log(colorName) remainStr = graph.substring(indexZ) valueStringArray = remainStr.match(/\d+/g) console.log(JSON.stringify(valueStringArray)) colorObj = new Object() colorObj.name = colorName colorObj.RGB = valueStringArray colorDataObjs.push(colorObj) } }) cnvs = document.windows[0].selection.canvas swatchWidth = 150 swatchHeight = 150 for (i = 0; i < colorDataObjs.length; i++){ dataObj = colorDataObjs[i] // console.log("Item " + String(i), JSON.stringify(dataObj)) rowFactor = i / 4 if(i !== 0 && Number.isInteger(rowFactor) === true){ var hOffset = 0 var columnIndex = 0 var rowIndex = (rowIndex + 1) } else if(i === 0){ var hOffset = 0 var vOffset = 0 var columnIndex = 0 var rowIndex = 0 } else { var hOffset = (swatchWidth * columnIndex) var columnIndex = (columnIndex + 1) var hOffset = (hOffset + swatchWidth) } var vOffset = (rowIndex * swatchHeight) solid = cnvs.newShape() solid.shape = 'Rectangle' solid.geometry = new Rect(hOffset, vOffset, swatchWidth, swatchHeight) colorName = dataObj.name RGBvals = dataObj.RGB rVal = parseInt(RGBvals[0]) gVal = parseInt(RGBvals[1]) bVal = parseInt(RGBvals[2]) rValPCT = rVal / 255 gValPCT = gVal / 255 bValPCT = bVal / 255 darknessVal = rVal + gVal + bVal fontColor = (darknessVal <= 384) ? Color.white : Color.black colorObj = Color.RGB(rValPCT, gValPCT, bValPCT, 1) solid.fillColor = colorObj solid.text = colorName + "\n" + "(" + RGBvals.join(", ") + ")" solid.textColor = fontColor RGBPCTstr = `(${rValPCT}, ${gValPCT}, ${bValPCT})` solid.setUserData("Color Name", colorName) solid.setUserData("RGB", RGBPCTstr) solid.name = "Color Swatch: " + colorName } } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ console.error(err.name, err.message) new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ // CREATE TEXT FIELD OBJECT defaultQueryString = null textInputField = new Form.Field.String( "textInput", null, defaultQueryString, null ) // CREATE NEW FORM AND ADD FIELD var inputForm = new Form() inputForm.addField(textInputField) // VALIDATE USER INPUT inputForm.validate = function(formObject){ textInput = formObject.values["textInput"] if (!textInput){return false} return true } // DISPLAY THE FORM var formPrompt = "A color palette for:" var buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // RETRIVE FORM INPUT textInput = formObject.values["textInput"] textInput = "A color palette with RGB values for " + textInput // LOG THE PROMPT console.log("# PROMPT:", textInput) promptString = textInput escapedText = encodeURIComponent(textInput) requestObj = { "model": serviceModel, "prompt": escapedText, "temperature": 0, "max_tokens": 450, "top_p": 1.0, "frequency_penalty": 0.0, "presence_penalty": 0.0, "stop": [";"] } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();
OpenAI: Suggest Actions
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.openai-gpt-suggest-actions", "version": "1.0", "description": "This plug-in will use the title and note of the selected project to ask the OpenAI GPT service for suggested actions, and provide the option to insert them into the selected project. In addition, the query results will be placed on the clipboard. NOTE: This plug-in uses the Credentials class to create and retrieve log-in pairs. To clear stored credentials, hold down Control modifier key when selecting plug-in from Automation menu. To set logging status, hold down the Option key when selecting plug-in from Automation menu.", "label": "OpenAI: Suggest Actions", "shortLabel": "OpenAI: Suggest Actions", "paletteLabel": "OpenAI: Suggest Actions", "image": "wand.and.stars" }*/ (() => { /* DOCUMENTATION: https://platform.openai.com/docs/api-reference/introduction */ var serviceTitle = "OpenAI"; var serviceURLString = "https://api.openai.com/v1/chat/completions" var credentials = new Credentials() var preferences = new Preferences() // NO ID = PLUG-IN ID var shouldLog function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } const requestCredentials = async () => { try { // CREATE FORM FOR GATHERING CREDENTIALS inputForm = new Form() // CREATE TEXT FIELDS orgIDField = new Form.Field.String( "organizationID", "Org ID", null ) APIKeyField = new Form.Field.String( "APIkey", "API Key", null ) // ADD THE FIELDS TO THE FORM inputForm.addField(orgIDField) inputForm.addField(APIKeyField) // VALIDATE THE USER INPUT inputForm.validate = formObject => { organizationID = formObject.values["organizationID"] orgIDStatus = (organizationID && organizationID.length > 0) ? true:false APIkey = formObject.values["APIkey"] APIKeyStatus = (APIkey && APIkey.length > 0) ? true:false validation = (orgIDStatus && APIKeyStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER formPrompt = "Enter OpenAI Organization ID and API Key:" formObject = await inputForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES organizationID = formObject.values["organizationID"] APIkey = formObject.values["APIkey"] // STORE THE VALUES credentials.write(serviceTitle, organizationID, APIkey) if(shouldLog){console.log("# CREDENTIALS STORED IN SYSTEM KEYCHAIN")} } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async (credentialsObj, requestObj) => { try { organizationID = credentialsObj["user"] APIkey = credentialsObj["password"] if(shouldLog){console.log("# ORGANIZATION-ID:", organizationID)} if(shouldLog){console.log("# API-KEY:", APIkey)} if(shouldLog){console.log("# CONSTRUCTING URL.FetchRequest()")} if(shouldLog){console.log("# SERVICE URL STRING:", serviceURLString)} request = URL.FetchRequest.fromString(serviceURLString) request.method = 'POST' request.cache = "no-cache" if(shouldLog){console.log("# CONSTRUCTING REQUEST HEADERS")} authorizationStr = "Bearer" + " " + APIkey headerObj = { "Content-Type": "application/json", "Authorization": authorizationStr, "OpenAI-Organization": organizationID } if(shouldLog){console.log("# HEADER OBJECT", JSON.stringify(headerObj, null, 4))} request.headers = headerObj if(shouldLog){console.log("# REQUEST OBJECT", JSON.stringify(requestObj, null, 4))} request.bodyString = JSON.stringify(requestObj) // EXECUTE REQUEST response = await request.fetch() // PROCESS REQUEST RESULT responseCode = response.statusCode if(shouldLog){console.log("# RESPONSE CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ // PROCESS RETRIEVED DATA json = JSON.parse(response.bodyString) console.log(JSON.stringify(json, null, 4)) alertTitle = "Successful Query" alertMessage = "The response has been logged to the console, and placed on the clipboard." alert = new Alert(alertTitle, alertMessage) alert.addOption("Add Suggested Actions") alert.addOption("Done") alert.show(buttonIndex => { responseStr = json.choices[0].message.content Pasteboard.general.string = responseStr if (buttonIndex === 0){ graphs = responseStr.split("\n") listItems = new Array() numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] graphs.forEach(graph => { firstChar = graph.charAt(0) if(numbers.includes(firstChar)){ listItem = graph.replace(/^[0-9]+. /g, '') listItems.push(listItem.trim()) } }) project = document.windows[0].selection.projects[0] listItems.forEach(item => { new Task(item, project.ending) }) } }) } else if (responseCode === 401){ alertMessage = "Problem authenticating account with server." alert = new Alert(String(responseCode), alertMessage) alert.show().then(() => {requestCredentials()}) } else { new Alert(String(responseCode), "An error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // READ PREFERENCES booleanValue = preferences.readBoolean("shouldLog") if(!booleanValue){ shouldLog = false preferences.write("shouldLog", false) } if(shouldLog){console.clear()} loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) if (app.controlKeyDown){ // TO REMOVE CREDENTIALS HOLD DOWN CONTROL KEY WHEN SELECTING PLUG-IN credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ alertMessage = "There are no stored credentials to remove." new Alert("Missing Resource", alertMessage).show() } else { alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ console.log(`Removing Service “${serviceTitle}”`) credentials.remove(serviceTitle) console.log(`Service “${serviceTitle}” Removed`) } }) } } else if (app.optionKeyDown){ alertMessage = (shouldLog) ? "Logging is on." : "Logging is off." alert = new Alert("Logging Status", alertMessage) alert.addOption("Turn On") alert.addOption("Turn Off") alert.show(buttonIndex => { if (buttonIndex === 0){ preferences.write("shouldLog", true) shouldLog = true } else { preferences.write("shouldLog", false) shouldLog = false } loggingMsg = (shouldLog) ? "# LOGGING IS ON":"# LOGGING IS OFF" console.log(loggingMsg) }) } else { credentialsObj = credentials.read(serviceTitle) if (credentialsObj){ project = selection.projects[0] projectTitle = project.name projectNote = project.note promptString = "Given the following project title and description, " promptString += "what are the next steps?" promptString += "\n" promptString += "Project Title: " + projectTitle promptString += "\n" promptString += "Project Description: " + projectNote alertTitle = "Requesting Project Actions" alertMessage = "IMPORTANT: Data about the selected project " alertMessage += "will be sent to a third-party network service." alert = new Alert(alertTitle, alertMessage) alert.addOption("Proceed") alert.addOption("Stop") buttonIndex = await alert.show() if(buttonIndex === 1){ throw { name: "Cancel", message: "User cancelled send." } } console.log("# PROMPT", promptString) escapedText = encodeURIComponent(promptString) requestObj = { "model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": escapedText}] } if(shouldLog){console.log("# PASSING CREDENTIALS AND REQUEST TO fetchData()")} fetchData(credentialsObj, requestObj) } else { if(shouldLog){console.log("# NO CREDENTIALS RETRIEVED, REQUESTING CREDENTIALS.")} requestCredentials() } } } catch(err){console.error(err.name, err.message)} }); action.validate = function(selection, sender){ // validation code return (selection.projects.length === 1) }; return action; })();