@agnocon/piece-camb-ai 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.MIT-AP +24 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/actions/create-text-to-sound.d.ts +8 -0
- package/dist/lib/actions/create-text-to-sound.d.ts.map +1 -0
- package/dist/lib/actions/create-text-to-sound.js +100 -0
- package/dist/lib/actions/create-text-to-sound.js.map +1 -0
- package/dist/lib/actions/create-text-to-speech.d.ts +11 -0
- package/dist/lib/actions/create-text-to-speech.d.ts.map +1 -0
- package/dist/lib/actions/create-text-to-speech.js +121 -0
- package/dist/lib/actions/create-text-to-speech.js.map +1 -0
- package/dist/lib/actions/create-transcription.d.ts +9 -0
- package/dist/lib/actions/create-transcription.d.ts.map +1 -0
- package/dist/lib/actions/create-transcription.js +134 -0
- package/dist/lib/actions/create-transcription.js.map +1 -0
- package/dist/lib/actions/create-translation.d.ts +10 -0
- package/dist/lib/actions/create-translation.d.ts.map +1 -0
- package/dist/lib/actions/create-translation.js +116 -0
- package/dist/lib/actions/create-translation.js.map +1 -0
- package/dist/lib/auth.d.ts +2 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +41 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/common/index.d.ts +10 -0
- package/dist/lib/common/index.d.ts.map +1 -0
- package/dist/lib/common/index.js +152 -0
- package/dist/lib/common/index.js.map +1 -0
- package/package.json +47 -0
- package/src/index.ts +34 -0
- package/src/lib/actions/create-text-to-sound.ts +104 -0
- package/src/lib/actions/create-text-to-speech.ts +121 -0
- package/src/lib/actions/create-transcription.ts +132 -0
- package/src/lib/actions/create-translation.ts +115 -0
- package/src/lib/auth.ts +34 -0
- package/src/lib/common/index.ts +165 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createAction, Property, ApFile, DynamicPropsValue } from '@agnocon/pieces-framework';
|
|
2
|
+
import { HttpMethod, httpClient, HttpMessageBody, HttpHeaders } from '@agnocon/pieces-common';
|
|
3
|
+
import { cambaiAuth } from '../auth';
|
|
4
|
+
import { API_BASE_URL, listSourceLanguagesDropdown, POLLING_INTERVAL_MS, LONG_MAX_POLLING_ATTEMPTS } from '../common';
|
|
5
|
+
import FormData from 'form-data';
|
|
6
|
+
import { listFoldersDropdown } from '../common';
|
|
7
|
+
|
|
8
|
+
export const createTranscription = createAction({
|
|
9
|
+
auth: cambaiAuth,
|
|
10
|
+
name: 'create_transcription',
|
|
11
|
+
displayName: 'Create Transcription',
|
|
12
|
+
description: 'Creates a task to process speech into readable text.',
|
|
13
|
+
audience: 'both',
|
|
14
|
+
aiMetadata: { description: 'Transcribes speech from an audio/video source into text via Camb.AI, polling until the transcription task completes. The media can be supplied either as an uploaded file (max 20MB) or as a public file URL, selected via the media source mode; you must specify the spoken language. Use to convert recorded speech to text. Not idempotent: each call starts a new transcription task.', idempotent: false },
|
|
15
|
+
props: {
|
|
16
|
+
language: listSourceLanguagesDropdown,
|
|
17
|
+
source_type: Property.StaticDropdown({
|
|
18
|
+
displayName: 'Media Source',
|
|
19
|
+
description: 'Choose whether to upload a file or provide a URL.',
|
|
20
|
+
required: true,
|
|
21
|
+
defaultValue: 'file',
|
|
22
|
+
options: {
|
|
23
|
+
options: [
|
|
24
|
+
{ label: 'Upload File', value: 'file' },
|
|
25
|
+
{ label: 'File URL', value: 'url' },
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
media: Property.DynamicProperties({
|
|
30
|
+
auth: cambaiAuth,
|
|
31
|
+
displayName: 'Media',
|
|
32
|
+
required: true,
|
|
33
|
+
refreshers: ['source_type'],
|
|
34
|
+
props: async (context) => {
|
|
35
|
+
const sourceType = (context['source_type'] as unknown as string);
|
|
36
|
+
const fields: DynamicPropsValue = {};
|
|
37
|
+
if (sourceType === 'file') {
|
|
38
|
+
fields['media_file'] = Property.File({
|
|
39
|
+
displayName: 'Media File',
|
|
40
|
+
description: 'The media file (e.g., MP3, WAV, MP4) to transcribe. Max size: 20MB.',
|
|
41
|
+
required: true,
|
|
42
|
+
});
|
|
43
|
+
} else if (sourceType === 'url') {
|
|
44
|
+
fields['media_url'] = Property.ShortText({
|
|
45
|
+
displayName: 'Media URL',
|
|
46
|
+
description: 'A public URL to the media file to transcribe.',
|
|
47
|
+
required: true,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return fields;
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
project_name: Property.ShortText({
|
|
54
|
+
displayName: 'Project Name',
|
|
55
|
+
description: 'A memorable name for your project to help organize tasks in your Camb.ai workspace.',
|
|
56
|
+
required: false,
|
|
57
|
+
}),
|
|
58
|
+
project_description: Property.LongText({
|
|
59
|
+
displayName: 'Project Description',
|
|
60
|
+
description: 'Provide details about your project\'s goals and specifications for documentation purposes.',
|
|
61
|
+
required: false,
|
|
62
|
+
}),
|
|
63
|
+
folder_id: listFoldersDropdown,
|
|
64
|
+
},
|
|
65
|
+
async run(context) {
|
|
66
|
+
const { auth } = context;
|
|
67
|
+
const { language, source_type, media, project_name, project_description, folder_id } = context.propsValue;
|
|
68
|
+
|
|
69
|
+
const formData = new FormData();
|
|
70
|
+
|
|
71
|
+
formData.append('language', Number(language).toString());
|
|
72
|
+
if (project_name) formData.append('project_name', project_name);
|
|
73
|
+
if (project_description) formData.append('project_description', project_description);
|
|
74
|
+
if (folder_id) formData.append('folder_id', folder_id.toString());
|
|
75
|
+
|
|
76
|
+
if (source_type === 'url') {
|
|
77
|
+
if (!media['media_url']) throw new Error("Media URL is required when source is 'File URL'.");
|
|
78
|
+
formData.append('media_url', media['media_url'] as string);
|
|
79
|
+
} else {
|
|
80
|
+
if (!media['media_file']) throw new Error("Media File is required when source is 'Upload File'.");
|
|
81
|
+
const fileData = media['media_file'] as ApFile;
|
|
82
|
+
formData.append('media_file', fileData.data, fileData.filename);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
const requestBody = await formData.getBuffer();
|
|
87
|
+
const headers: HttpHeaders = {
|
|
88
|
+
'x-api-key': auth.secret_text,
|
|
89
|
+
...formData.getHeaders(),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const initialResponse = await httpClient.sendRequest<{ task_id: string }>({
|
|
93
|
+
method: HttpMethod.POST,
|
|
94
|
+
url: `${API_BASE_URL}/transcribe`,
|
|
95
|
+
headers: headers,
|
|
96
|
+
body: requestBody,
|
|
97
|
+
});
|
|
98
|
+
const taskId = initialResponse.body.task_id;
|
|
99
|
+
let run_id: string | null = null;
|
|
100
|
+
|
|
101
|
+
let attempts = 0;
|
|
102
|
+
while (attempts < LONG_MAX_POLLING_ATTEMPTS) {
|
|
103
|
+
const statusResponse = await httpClient.sendRequest<{ status: string; run_id?: string }>({
|
|
104
|
+
method: HttpMethod.GET,
|
|
105
|
+
url: `${API_BASE_URL}/transcribe/${taskId}`,
|
|
106
|
+
headers: { 'x-api-key': auth.secret_text },
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (statusResponse.body.status === 'SUCCESS') {
|
|
110
|
+
run_id = statusResponse.body.run_id ?? null;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (statusResponse.body.status === 'FAILED') {
|
|
114
|
+
throw new Error(`Transcription task failed: ${JSON.stringify(statusResponse.body)}`);
|
|
115
|
+
}
|
|
116
|
+
await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL_MS));
|
|
117
|
+
attempts++;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!run_id) {
|
|
121
|
+
throw new Error("Transcription task timed out or failed to return a task_id.");
|
|
122
|
+
}
|
|
123
|
+
const resultResponse = await httpClient.sendRequest<{ transcriptions: string[] }>({
|
|
124
|
+
method: HttpMethod.GET,
|
|
125
|
+
url: `${API_BASE_URL}/transcription-result/${run_id}`,
|
|
126
|
+
headers: { 'x-api-key': auth.secret_text },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return resultResponse.body;
|
|
130
|
+
|
|
131
|
+
},
|
|
132
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { HttpMethod, httpClient } from '@agnocon/pieces-common';
|
|
3
|
+
import { cambaiAuth } from '../auth';
|
|
4
|
+
import { API_BASE_URL, listSourceLanguagesDropdown, listTargetLanguagesDropdown ,POLLING_INTERVAL_MS,MAX_POLLING_ATTEMPTS} from '../common';
|
|
5
|
+
|
|
6
|
+
export const createTranslation = createAction({
|
|
7
|
+
auth: cambaiAuth,
|
|
8
|
+
name: 'create_translation',
|
|
9
|
+
displayName: 'Create Translation',
|
|
10
|
+
description: 'Translate text from a source language to a target language.',
|
|
11
|
+
audience: 'both',
|
|
12
|
+
aiMetadata: { description: 'Translates text from a source language to a target language via Camb.AI, polling until the translation task completes. Each input line is treated as a separate segment to translate, and both languages are chosen from Camb.AI dropdowns; optionally tune formality, gender, and target audience age. Use for text translation only (not audio — use Create Transcription for speech-to-text). Not idempotent: each call starts a new translation task.', idempotent: false },
|
|
13
|
+
props: {
|
|
14
|
+
texts: Property.LongText({
|
|
15
|
+
displayName: 'Text to Translate',
|
|
16
|
+
description: 'The text to be translated. You can enter multiple lines; each line will be treated as a separate text segment.',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
source_language: listSourceLanguagesDropdown,
|
|
20
|
+
target_language: listTargetLanguagesDropdown,
|
|
21
|
+
formality: Property.StaticDropdown({
|
|
22
|
+
displayName: 'Formality',
|
|
23
|
+
description: 'Adjust the formality level to match your context.',
|
|
24
|
+
required: false,
|
|
25
|
+
options: {
|
|
26
|
+
options: [
|
|
27
|
+
{ label: 'Formal', value: 1 },
|
|
28
|
+
{ label: 'Informal', value: 2 },
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}),
|
|
32
|
+
gender: Property.StaticDropdown({
|
|
33
|
+
displayName: 'Gender',
|
|
34
|
+
description: 'Specify grammatical gender preferences when relevant in the target language.',
|
|
35
|
+
required: false,
|
|
36
|
+
options: {
|
|
37
|
+
options: [
|
|
38
|
+
{ label: 'Male', value: 1 },
|
|
39
|
+
{ label: 'Female', value: 2 },
|
|
40
|
+
{ label: 'Neutral', value: 0 },
|
|
41
|
+
{ label: 'Unspecified', value: 9 },
|
|
42
|
+
],
|
|
43
|
+
}
|
|
44
|
+
}),
|
|
45
|
+
age: Property.Number({
|
|
46
|
+
displayName: 'Audience Age',
|
|
47
|
+
description: 'Helps adjust vocabulary and expressions to be age-appropriate.',
|
|
48
|
+
required: false,
|
|
49
|
+
}),
|
|
50
|
+
project_name: Property.ShortText({
|
|
51
|
+
displayName: 'Project Name',
|
|
52
|
+
description: 'A memorable name for your project to help organize tasks in your Camb.ai workspace.',
|
|
53
|
+
required: false,
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
async run(context) {
|
|
57
|
+
const { auth } = context;
|
|
58
|
+
const { texts, source_language, target_language, formality, gender, age, project_name } = context.propsValue;
|
|
59
|
+
|
|
60
|
+
const payload: Record<string, unknown> = {
|
|
61
|
+
texts: texts.split('\n').filter(line => line.trim().length > 0),
|
|
62
|
+
source_language: Number(source_language),
|
|
63
|
+
target_language: Number(target_language),
|
|
64
|
+
};
|
|
65
|
+
if (formality !== undefined) payload['formality'] = formality;
|
|
66
|
+
if (gender !== undefined) payload['gender'] = gender;
|
|
67
|
+
if (age) payload['age'] = age;
|
|
68
|
+
if (project_name) payload['project_name'] = project_name;
|
|
69
|
+
|
|
70
|
+
const initialResponse = await httpClient.sendRequest<{ task_id: string }>({
|
|
71
|
+
method: HttpMethod.POST,
|
|
72
|
+
url: `${API_BASE_URL}/translate`,
|
|
73
|
+
headers: { 'x-api-key': auth.secret_text, 'Content-Type': 'application/json' },
|
|
74
|
+
body: payload,
|
|
75
|
+
});
|
|
76
|
+
const taskId = initialResponse.body.task_id;
|
|
77
|
+
let run_id: string | null = null;
|
|
78
|
+
|
|
79
|
+
let attempts = 0;
|
|
80
|
+
while (attempts < MAX_POLLING_ATTEMPTS) {
|
|
81
|
+
const statusResponse = await httpClient.sendRequest<{ status: string; run_id?: string }>({
|
|
82
|
+
method: HttpMethod.GET,
|
|
83
|
+
url: `${API_BASE_URL}/translate/${taskId}`,
|
|
84
|
+
headers: { 'x-api-key': auth.secret_text },
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (statusResponse.body.status === 'SUCCESS') {
|
|
88
|
+
|
|
89
|
+
run_id = statusResponse.body.run_id ?? null;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
if (statusResponse.body.status === 'ERROR' || statusResponse.body.status === 'FAILED') {
|
|
93
|
+
|
|
94
|
+
throw new Error(`Translation task failed: ${JSON.stringify(statusResponse.body)}`);
|
|
95
|
+
}
|
|
96
|
+
await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL_MS));
|
|
97
|
+
attempts++;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if (!run_id) {
|
|
102
|
+
throw new Error("Translation task timed out or failed to return a task_id.");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const resultResponse = await httpClient.sendRequest<{ translations: string[] }>({
|
|
106
|
+
method: HttpMethod.GET,
|
|
107
|
+
url: `${API_BASE_URL}/translation-result/${run_id}`,
|
|
108
|
+
headers: { 'x-api-key': auth.secret_text },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return resultResponse.body;
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
},
|
|
115
|
+
});
|
package/src/lib/auth.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PieceAuth } from '@agnocon/pieces-framework';
|
|
2
|
+
import { httpClient, HttpMethod } from '@agnocon/pieces-common';
|
|
3
|
+
import { API_BASE_URL } from './common';
|
|
4
|
+
|
|
5
|
+
export const cambaiAuth = PieceAuth.SecretText({
|
|
6
|
+
displayName: "API Key",
|
|
7
|
+
description: `
|
|
8
|
+
To get your API key, please follow these steps:
|
|
9
|
+
1. Log in to your [CAMB.AI Studio](https://camb.ai/studio/) account.
|
|
10
|
+
2. Navigate to your workspace's API Keys dashboard.
|
|
11
|
+
3. Create a new key if you haven't already.
|
|
12
|
+
4. Copy the API key and paste it here.
|
|
13
|
+
`,
|
|
14
|
+
required: true,
|
|
15
|
+
validate: async ({ auth }) => {
|
|
16
|
+
try {
|
|
17
|
+
await httpClient.sendRequest({
|
|
18
|
+
method: HttpMethod.GET,
|
|
19
|
+
url: `${API_BASE_URL}/source-languages`,
|
|
20
|
+
headers: {
|
|
21
|
+
'x-api-key': auth,
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
valid: true,
|
|
26
|
+
};
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return {
|
|
29
|
+
valid: false,
|
|
30
|
+
error: 'Invalid API Key.',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Property } from "@agnocon/pieces-framework";
|
|
2
|
+
import { HttpMethod, httpClient } from "@agnocon/pieces-common";
|
|
3
|
+
import { cambaiAuth } from '../auth';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const API_BASE_URL = "https://client.camb.ai/apis";
|
|
7
|
+
export const POLLING_INTERVAL_MS = 5000;
|
|
8
|
+
export const LONG_POLLING_INTERVAL_MS = 10000;
|
|
9
|
+
export const MAX_POLLING_ATTEMPTS = 10;
|
|
10
|
+
export const LONG_MAX_POLLING_ATTEMPTS = 120;
|
|
11
|
+
|
|
12
|
+
type Voice = {
|
|
13
|
+
id: number;
|
|
14
|
+
voice_name: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type Folder = {
|
|
18
|
+
folder_id: number;
|
|
19
|
+
folder_name: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Language = {
|
|
23
|
+
id: number;
|
|
24
|
+
language: string;
|
|
25
|
+
short_name: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export const listVoicesDropdown = Property.Dropdown({
|
|
30
|
+
auth: cambaiAuth,
|
|
31
|
+
displayName: 'Voice',
|
|
32
|
+
description: 'Select the voice to generate the speech.',
|
|
33
|
+
required: true,
|
|
34
|
+
refreshers: [],
|
|
35
|
+
options: async ({ auth }) => {
|
|
36
|
+
if (!auth) {
|
|
37
|
+
return {
|
|
38
|
+
disabled: true,
|
|
39
|
+
options: [],
|
|
40
|
+
placeholder: 'Please authenticate first',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const response = await httpClient.sendRequest<Voice[]>({
|
|
44
|
+
method: HttpMethod.GET,
|
|
45
|
+
url: `${API_BASE_URL}/list-voices`,
|
|
46
|
+
headers: {
|
|
47
|
+
'x-api-key': auth.secret_text,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const voices = response.body ?? [];
|
|
51
|
+
return {
|
|
52
|
+
disabled: false,
|
|
53
|
+
options: voices.map((voice) => ({
|
|
54
|
+
label: voice.voice_name,
|
|
55
|
+
value: voice.id,
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
export const listSourceLanguagesDropdown = Property.Dropdown({
|
|
63
|
+
auth: cambaiAuth,
|
|
64
|
+
displayName: 'Source Language',
|
|
65
|
+
description: 'Select the original language of the input text.',
|
|
66
|
+
required: true,
|
|
67
|
+
refreshers: [],
|
|
68
|
+
options: async ({ auth }) => {
|
|
69
|
+
if (!auth) {
|
|
70
|
+
return {
|
|
71
|
+
disabled: true,
|
|
72
|
+
options: [],
|
|
73
|
+
placeholder: 'Please authenticate first',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const response = await httpClient.sendRequest<Language[]>({
|
|
77
|
+
method: HttpMethod.GET,
|
|
78
|
+
url: `${API_BASE_URL}/source-languages`,
|
|
79
|
+
headers: {
|
|
80
|
+
'x-api-key': auth.secret_text,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const languages = response.body ?? [];
|
|
84
|
+
return {
|
|
85
|
+
disabled: false,
|
|
86
|
+
options: languages.map((lang) => ({
|
|
87
|
+
label: `${lang.language} (${lang.short_name})`,
|
|
88
|
+
value: lang.id,
|
|
89
|
+
})),
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
export const listTargetLanguagesDropdown = Property.Dropdown({
|
|
96
|
+
displayName: 'Target Language',
|
|
97
|
+
description: 'Select the language to translate the text into.',
|
|
98
|
+
auth: cambaiAuth,
|
|
99
|
+
required: true,
|
|
100
|
+
refreshers: [],
|
|
101
|
+
options: async ({ auth }) => {
|
|
102
|
+
if (!auth) {
|
|
103
|
+
return {
|
|
104
|
+
disabled: true,
|
|
105
|
+
options: [],
|
|
106
|
+
placeholder: 'Please authenticate first',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const response = await httpClient.sendRequest<Language[]>({
|
|
110
|
+
method: HttpMethod.GET,
|
|
111
|
+
url: `${API_BASE_URL}/target-languages`,
|
|
112
|
+
headers: {
|
|
113
|
+
'x-api-key': auth.secret_text,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const languages = response.body ?? [];
|
|
117
|
+
return {
|
|
118
|
+
disabled: false,
|
|
119
|
+
options: languages.map((lang) => ({
|
|
120
|
+
label: `${lang.language} (${lang.short_name})`,
|
|
121
|
+
value: lang.id,
|
|
122
|
+
})),
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export const listFoldersDropdown = Property.Dropdown({
|
|
128
|
+
displayName: 'Folder',
|
|
129
|
+
auth: cambaiAuth,
|
|
130
|
+
description: 'Select the folder to save the task in.',
|
|
131
|
+
required: false,
|
|
132
|
+
refreshers: [],
|
|
133
|
+
options: async ({ auth }) => {
|
|
134
|
+
if (!auth) {
|
|
135
|
+
return {
|
|
136
|
+
disabled: true,
|
|
137
|
+
options: [],
|
|
138
|
+
placeholder: 'Please authenticate first',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const response = await httpClient.sendRequest<Folder[]>({
|
|
143
|
+
method: HttpMethod.GET,
|
|
144
|
+
url: `${API_BASE_URL}/folders`,
|
|
145
|
+
headers: {
|
|
146
|
+
'x-api-key': auth.secret_text,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
const folders = response.body ?? [];
|
|
150
|
+
return {
|
|
151
|
+
disabled: false,
|
|
152
|
+
options: folders.map((folder) => ({
|
|
153
|
+
label: folder.folder_name,
|
|
154
|
+
value: folder.folder_id,
|
|
155
|
+
})),
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
disabled: true,
|
|
160
|
+
options: [],
|
|
161
|
+
placeholder: "Could not load folders."
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
});
|