felo-ai 0.2.46 → 0.2.47

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.
@@ -119,6 +119,11 @@ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-resource SHORT
119
119
  node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-resource SHORT_ID RESOURCE_ID --snippet "New summary" --thumbnail "https://example.com/thumb.png"
120
120
  ```
121
121
 
122
+ **Update resource content (ai_doc type only — also auto-updates snippet from first 2000 bytes):**
123
+ ```bash
124
+ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-resource-content SHORT_ID RESOURCE_ID --content "New content here"
125
+ ```
126
+
122
127
  ### Semantic Retrieval
123
128
 
124
129
  **Route relevant resources by query:**
@@ -240,6 +245,7 @@ The API returns JSON with this structure:
240
245
  - `short_id` — unique identifier (use this for all operations)
241
246
  - `name` — LiveDoc name
242
247
  - `description` — LiveDoc description
248
+ - `is_shared` — `true` if this LiveDoc was shared with you (not owned by you)
243
249
  - `created_at` / `modified_at` — timestamps
244
250
 
245
251
  **Resource object:**
@@ -95,6 +95,7 @@ function formatLiveDoc(doc) {
95
95
  out += `- ID: \`${doc.short_id}\`\n`;
96
96
  if (doc.description) out += `- Description: ${doc.description}\n`;
97
97
  if (doc.icon) out += `- Icon: ${doc.icon}\n`;
98
+ if (doc.is_shared != null) out += `- Shared: ${doc.is_shared}\n`;
98
99
  if (doc.created_at) out += `- Created: ${doc.created_at}\n`;
99
100
  if (doc.modified_at) out += `- Modified: ${doc.modified_at}\n`;
100
101
  out += '\n';
@@ -167,6 +168,7 @@ function usage() {
167
168
  ' upload <short_id> Upload file (--file required, --convert optional)',
168
169
  ' remove-resource <short_id> <resource_id> Delete a resource',
169
170
  ' update-resource <short_id> <resource_id> Update resource title/snippet/thumbnail',
171
+ ' update-resource-content <short_id> <resource_id> Update ai_doc content (--content required)',
170
172
  ' retrieve <short_id> Semantic search (--query required, --resource-ids optional)',
171
173
  ' route <short_id> Route relevant resources by query (--query required)',
172
174
  ' download <short_id> <resource_id> Download source file to disk',
@@ -432,6 +434,16 @@ async function main() {
432
434
  code = 0;
433
435
  break;
434
436
  }
437
+ case 'update-resource-content': {
438
+ if (!shortId || !resourceId) { console.error('ERROR: short_id and resource_id are required'); break; }
439
+ if (!args.content) { console.error('ERROR: --content is required'); break; }
440
+ spinnerId = startSpinner('Updating resource content');
441
+ const payload = await apiRequest('PUT', `/livedocs/${shortId}/resources/${resourceId}/content`, { content: args.content }, apiKey, apiBase, timeoutMs);
442
+ if (json) { console.log(JSON.stringify(payload, null, 2)); }
443
+ else { process.stdout.write('Resource content updated.\n'); }
444
+ code = 0;
445
+ break;
446
+ }
435
447
  case 'retrieve': {
436
448
  if (!shortId) { console.error('ERROR: short_id is required'); break; }
437
449
  if (!args.query) { console.error('ERROR: --query is required'); break; }
@@ -90,6 +90,7 @@ Script behavior:
90
90
 
91
91
  - Creates task via `POST https://openapi.felo.ai/v2/ppts`
92
92
  - Supports optional `--theme <id>` to apply a PPT theme (sends `ppt_config.ai_theme_id`)
93
+ - Supports optional `--livedoc-id <id>` to reuse an existing LiveDoc instead of auto-creating a new one
93
94
  - Supports optional `--task-id <id>` to resume polling an existing task (skips creation)
94
95
  - Polls via `GET https://openapi.felo.ai/v2/tasks/{task_id}/historical`
95
96
  - Treats `COMPLETED`/`SUCCESS` as success terminal (case-insensitive)
@@ -15,6 +15,7 @@ function usage() {
15
15
  ' --query <text> PPT prompt (required unless --task-id is given)',
16
16
  ' --task-id <id> Resume polling an existing task (skip creation)',
17
17
  ' --theme <id> PPT theme ID (from ppt-themes)',
18
+ ' --livedoc-id <id> Reuse an existing LiveDoc instead of auto-creating one',
18
19
  ' --interval <seconds> Poll interval, default 10',
19
20
  ' --max-wait <seconds> Max wait time, default 1800',
20
21
  ' --timeout <seconds> Request timeout, default 60',
@@ -30,6 +31,7 @@ function parseArgs(argv) {
30
31
  query: '',
31
32
  taskId: '',
32
33
  theme: '',
34
+ livedocId: '',
33
35
  intervalSec: DEFAULT_INTERVAL_SEC,
34
36
  maxWaitSec: DEFAULT_MAX_WAIT_SEC,
35
37
  timeoutSec: DEFAULT_TIMEOUT_SEC,
@@ -54,6 +56,9 @@ function parseArgs(argv) {
54
56
  } else if (a === '--theme') {
55
57
  out.theme = argv[i + 1] ?? '';
56
58
  i += 1;
59
+ } else if (a === '--livedoc-id') {
60
+ out.livedocId = argv[i + 1] ?? '';
61
+ i += 1;
57
62
  } else if (a === '--interval') {
58
63
  out.intervalSec = Number.parseInt(argv[i + 1] ?? '', 10);
59
64
  i += 1;
@@ -138,11 +143,14 @@ function extractTaskUrls(historicalData, createData) {
138
143
  };
139
144
  }
140
145
 
141
- async function createTask(apiKey, apiBase, query, timeoutMs, theme) {
146
+ async function createTask(apiKey, apiBase, query, timeoutMs, theme, livedocId) {
142
147
  const reqBody = { query };
143
148
  if (theme) {
144
149
  reqBody.ppt_config = { ai_theme_id: theme };
145
150
  }
151
+ if (livedocId) {
152
+ reqBody.livedoc_short_id = livedocId;
153
+ }
146
154
  const payload = await fetchJson(
147
155
  `${apiBase}/v2/ppts`,
148
156
  {
@@ -211,7 +219,7 @@ async function main() {
211
219
  }
212
220
  } else {
213
221
  // Create a new task
214
- createData = await createTask(apiKey, apiBase, args.query, timeoutMs, args.theme);
222
+ createData = await createTask(apiKey, apiBase, args.query, timeoutMs, args.theme, args.livedocId || undefined);
215
223
  taskId = createData.task_id;
216
224
  if (args.verbose) {
217
225
  console.error(`Task ID: ${taskId}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "felo-ai",
3
- "version": "0.2.46",
3
+ "version": "0.2.47",
4
4
  "description": "Felo AI CLI - real-time search, PPT generation, SuperAgent conversation, LiveDoc management, web fetch, YouTube subtitles, LiveDoc knowledge base, and X (Twitter) search from the terminal",
5
5
  "type": "module",
6
6
  "main": "src/cli.js",
package/src/cli.js CHANGED
@@ -89,6 +89,7 @@ program
89
89
  )
90
90
  .option("--theme <id>", "PPT theme ID (from ppt-themes command)")
91
91
  .option("--task-id <id>", "resume polling an existing task (skip creation)")
92
+ .option("--livedoc-id <id>", "reuse an existing LiveDoc short_id instead of auto-creating a new one")
92
93
  .action(async (query, opts) => {
93
94
  if (!query && !opts.taskId) {
94
95
  console.error("Error: provide a <query> or --task-id to resume an existing task");
@@ -105,6 +106,7 @@ program
105
106
  pollTimeoutMs: Number.isNaN(pollTimeoutMs) ? 1_200_000 : pollTimeoutMs,
106
107
  pptConfig,
107
108
  taskId: opts.taskId,
109
+ livedocShortId: opts.livedocId || undefined,
108
110
  });
109
111
  process.exitCode = code;
110
112
  flushStdioThenExit(code);
@@ -774,6 +776,23 @@ livedocCmd
774
776
  flushStdioThenExit(code);
775
777
  });
776
778
 
779
+ livedocCmd
780
+ .command("update-resource-content <short_id> <resource_id>")
781
+ .description("Update the content of an ai_doc resource (also auto-updates snippet)")
782
+ .requiredOption("--content <text>", "new content for the resource")
783
+ .option("-j, --json", "output raw JSON")
784
+ .option("-t, --timeout <seconds>", "request timeout in seconds", "60")
785
+ .action(async (shortId, resourceId, opts) => {
786
+ const timeoutMs = parseInt(opts.timeout, 10) * 1000;
787
+ const code = await livedoc.updateResourceContent(shortId, resourceId, {
788
+ content: opts.content,
789
+ json: opts.json,
790
+ timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
791
+ });
792
+ process.exitCode = code;
793
+ flushStdioThenExit(code);
794
+ });
795
+
777
796
  livedocCmd
778
797
  .command("content <short_id> <resource_id>")
779
798
  .description("Get extracted text content of a resource")
package/src/livedoc.js CHANGED
@@ -91,6 +91,7 @@ function formatLiveDoc(doc) {
91
91
  out += `- ID: \`${doc.short_id}\`\n`;
92
92
  if (doc.description) out += `- Description: ${doc.description}\n`;
93
93
  if (doc.icon) out += `- Icon: ${doc.icon}\n`;
94
+ if (doc.is_shared != null) out += `- Shared: ${doc.is_shared}\n`;
94
95
  if (doc.created_at) out += `- Created: ${doc.created_at}\n`;
95
96
  if (doc.modified_at) out += `- Modified: ${doc.modified_at}\n`;
96
97
  out += '\n';
@@ -823,6 +824,28 @@ export async function createTaskComment(shortId, taskId, opts = {}) {
823
824
  } finally { stopSpinner(spinnerId); }
824
825
  }
825
826
 
827
+ export async function updateResourceContent(shortId, resourceId, opts = {}) {
828
+ const apiKey = await getApiKey();
829
+ if (!apiKey) { console.error(NO_KEY_MESSAGE.trim()); return 1; }
830
+ if (!shortId) { process.stderr.write('ERROR: short_id is required.\n'); return 1; }
831
+ if (!resourceId) { process.stderr.write('ERROR: resource_id is required.\n'); return 1; }
832
+ if (!opts.content) { process.stderr.write('ERROR: --content is required.\n'); return 1; }
833
+
834
+ const apiBase = await getApiBase();
835
+ const timeoutMs = opts.timeoutMs || DEFAULT_TIMEOUT_MS;
836
+ const spinnerId = startSpinner('Updating resource content');
837
+
838
+ try {
839
+ const payload = await apiRequest('PUT', `/livedocs/${shortId}/resources/${resourceId}/content`, { content: opts.content }, apiKey, apiBase, timeoutMs);
840
+ if (opts.json) { console.log(JSON.stringify(payload, null, 2)); return 0; }
841
+ process.stdout.write('Resource content updated.\n');
842
+ return 0;
843
+ } catch (err) {
844
+ process.stderr.write(`Failed to update resource content: ${err?.message || err}\n`);
845
+ return 1;
846
+ } finally { stopSpinner(spinnerId); }
847
+ }
848
+
826
849
  export async function pptRetrieve(shortId, opts = {}) {
827
850
  const apiKey = await getApiKey();
828
851
  if (!apiKey) { console.error(NO_KEY_MESSAGE.trim()); return 1; }
package/src/slides.js CHANGED
@@ -53,13 +53,17 @@ function normalizeTaskStatus(status) {
53
53
  * @param {number} timeoutMs
54
54
  * @param {string} apiBase
55
55
  * @param {{ ai_theme_id?: string }} [pptConfig]
56
+ * @param {string} [livedocShortId]
56
57
  */
57
- async function createPptTask(apiKey, query, timeoutMs, apiBase, pptConfig) {
58
+ async function createPptTask(apiKey, query, timeoutMs, apiBase, pptConfig, livedocShortId) {
58
59
  const url = `${apiBase}/v2/ppts`;
59
60
  const body = { query: query.trim() };
60
61
  if (pptConfig && Object.keys(pptConfig).length > 0) {
61
62
  body.ppt_config = pptConfig;
62
63
  }
64
+ if (livedocShortId) {
65
+ body.livedoc_short_id = livedocShortId;
66
+ }
63
67
  const res = await fetchWithTimeoutAndRetry(
64
68
  url,
65
69
  {
@@ -176,7 +180,8 @@ export async function slides(query, options = {}) {
176
180
  query,
177
181
  requestTimeoutMs,
178
182
  apiBase,
179
- options.pptConfig
183
+ options.pptConfig,
184
+ options.livedocShortId
180
185
  );
181
186
  taskId = createResult.task_id;
182
187