@socialseal/cli 0.1.1 → 0.1.2
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/CHANGELOG.md +5 -0
- package/README.md +27 -0
- package/package.json +1 -1
- package/src/index.js +514 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.2 - 2026-03-18
|
|
6
|
+
- Add `search-journey-run` async CLI ergonomics: `--async` starts the backend async mode, polling is on by default, and `--no-poll` returns the initial `runId` immediately.
|
|
7
|
+
- Add `--poll-interval <ms>` for async `search-journey-run` status polling.
|
|
8
|
+
- Treat terminal async `search-journey-run` failures as non-zero CLI exits instead of silent `200` JSON output.
|
|
9
|
+
|
|
5
10
|
## 0.1.1 - 2026-03-13
|
|
6
11
|
- Document public base URL and CLI error output.
|
|
7
12
|
- Add request timeouts, verbose error output, and OSS-safe tool discovery behavior.
|
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
Environment variables:
|
|
10
10
|
- `SOCIALSEAL_API_KEY`
|
|
11
11
|
- `SOCIALSEAL_API_BASE` (default `https://api.socialseal.co`)
|
|
12
|
+
- `SOCIALSEAL_WORKSPACE_ID` (optional workspace override; takes precedence over config)
|
|
12
13
|
- `SOCIALSEAL_TIMEOUT_MS` (optional request timeout override)
|
|
13
14
|
- `SOCIALSEAL_AGENT_IDLE_TIMEOUT_MS` (optional agent WebSocket inactivity timeout override; default 300000)
|
|
14
15
|
|
|
@@ -19,14 +20,22 @@ Optional config file:
|
|
|
19
20
|
{
|
|
20
21
|
"apiKey": "ss_cli_...",
|
|
21
22
|
"apiBase": "https://api.socialseal.co",
|
|
23
|
+
"workspaceId": "00000000-0000-0000-0000-000000000000",
|
|
22
24
|
"timeoutMs": 30000,
|
|
23
25
|
"agentIdleTimeoutMs": 300000
|
|
24
26
|
}
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
## Commands
|
|
30
|
+
- Workspace discovery/defaults:
|
|
31
|
+
- `socialseal workspace list`
|
|
32
|
+
- `socialseal workspace current`
|
|
33
|
+
- `socialseal workspace use <workspace-id|slug|exact-name>`
|
|
34
|
+
- `socialseal workspace clear`
|
|
35
|
+
|
|
28
36
|
- Agent (non-interactive, streaming):
|
|
29
37
|
- `socialseal agent run --message "..." --api-base https://api.socialseal.co --api-key <key> [--workspace-id <uuid>]`
|
|
38
|
+
- `socialseal agent run --message "..." --continue <token>`
|
|
30
39
|
- `socialseal agent run --message "..." --timeout 60000`
|
|
31
40
|
- `socialseal agent run --message "..." --idle-timeout 300000 --verbose`
|
|
32
41
|
|
|
@@ -37,6 +46,8 @@ Optional config file:
|
|
|
37
46
|
- Tools (direct edge function call):
|
|
38
47
|
- `socialseal tools call --function <tool> --body @payload.json --api-base https://api.socialseal.co --api-key <key>`
|
|
39
48
|
- `socialseal tools call --function <tool> --body @payload.json --json`
|
|
49
|
+
- `socialseal tools call --function search-journey-run --body @payload.json --async --workspace-id <uuid>`
|
|
50
|
+
- `socialseal tools call --function search-journey-run --body @payload.json --async --no-poll --workspace-id <uuid>`
|
|
40
51
|
|
|
41
52
|
- Data exports (provisional):
|
|
42
53
|
- `socialseal data export-tracking --group-id 123 --time-period 30d --out out.csv`
|
|
@@ -46,12 +57,28 @@ Optional config file:
|
|
|
46
57
|
- `export-report` and `export_tracking_data` are provisional until CLI export specs are finalized.
|
|
47
58
|
- `tools list` ships a stable built-in registry of supported direct-call function targets. It is not live backend enumeration.
|
|
48
59
|
- `--timeout <ms>` controls HTTP request timeouts. Agent runs default to a 5-minute WebSocket inactivity timeout unless you set `--idle-timeout <ms>` (or the matching env/config value).
|
|
60
|
+
- `search-journey-run` supports CLI-managed async polling: `--async` starts backend async mode, polling is on by default, `--no-poll` returns the initial `runId`, and `--poll-interval <ms>` controls the status polling cadence.
|
|
61
|
+
- `socialseal agent run` now defaults to a fresh conversation. The CLI prints a continuation token to `stderr`; pass it back with `--continue <token>` to resume the same agent conversation explicitly.
|
|
62
|
+
- Effective workspace precedence is: `--workspace-id` → `SOCIALSEAL_WORKSPACE_ID` → config `workspaceId` → backend personal-workspace fallback.
|
|
63
|
+
- `socialseal workspace use ...` writes a local default workspace into `~/.config/socialseal/config.json`, which the CLI reuses for `agent`, `tools`, and `data` commands.
|
|
64
|
+
- `socialseal workspace list` discovers the workspaces accessible to the current CLI key and marks the active/suggested default.
|
|
65
|
+
- If a scoped CLI key cannot safely infer a workspace, `agent run` now fails closed and tells you to set `--workspace-id` or configure a local default first.
|
|
49
66
|
|
|
50
67
|
## Errors and exit codes
|
|
51
68
|
- Exit codes: `2` (usage), `3` (auth), `4` (not found), `5` (server), `1` (unknown)
|
|
52
69
|
- Add `--json` to `tools call` or `data` commands to emit machine-readable errors.
|
|
53
70
|
- Add `--verbose` to print error details plus agent session/tool progress diagnostics.
|
|
54
71
|
|
|
72
|
+
## Troubleshooting
|
|
73
|
+
- `SUPABASE_ANON_KEY not configured`
|
|
74
|
+
- This comes from the CLI gateway, not the local CLI install.
|
|
75
|
+
- The deployed gateway is missing its `SUPABASE_ANON_KEY` secret, so `/cli/tools/*` cannot proxy to Supabase Edge Functions.
|
|
76
|
+
- Fix on the server side with `wrangler secret put SUPABASE_ANON_KEY --env <staging|production>` for the gateway Worker, then re-run a `socialseal tools call ...` smoke test.
|
|
77
|
+
- `AI_UNSUPPORTED_LOCATION` or `The live agent is unavailable in this region right now.`
|
|
78
|
+
- This is raised when the upstream Gemini API rejects the worker egress location.
|
|
79
|
+
- The agent worker currently uses Google Gemini directly from Cloudflare Workers; there is no SocialSeal-side region allowlist in the CLI.
|
|
80
|
+
- If this reproduces from a supported Google AI region, treat it as an infrastructure/runtime issue. Practical workarounds are to run the agent from a worker placement/egress region that Google accepts, or switch the agent runtime to Vertex AI for server-side calls.
|
|
81
|
+
|
|
55
82
|
## Smoke Test (manual)
|
|
56
83
|
1. `SOCIALSEAL_API_KEY=... socialseal agent run --message "ping"`
|
|
57
84
|
2. `SOCIALSEAL_API_KEY=... socialseal tools call --function <tool> --body @payload.json`
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const CLI_KEY_HEADER = 'X-CLI-Key';
|
|
|
12
12
|
const WORKSPACE_HEADER = 'X-Workspace-Id';
|
|
13
13
|
const DEFAULT_TIMEOUT_MS = 30000;
|
|
14
14
|
const DEFAULT_AGENT_IDLE_TIMEOUT_MS = 300000;
|
|
15
|
+
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
15
16
|
const MAX_TIMEOUT_MS = 900000;
|
|
16
17
|
const LEGACY_ENABLED = process.env.SOCIALSEAL_ENABLE_LEGACY === '1';
|
|
17
18
|
const EXIT_CODES = {
|
|
@@ -58,8 +59,12 @@ const KNOWN_TOOLS = [
|
|
|
58
59
|
{ name: 'vnext-topics-auto-tag', category: 'vnext', description: 'Auto-tag keyword and topic assignments with Gemini-assisted review.' },
|
|
59
60
|
];
|
|
60
61
|
|
|
62
|
+
function getConfigPath() {
|
|
63
|
+
return process.env.SOCIALSEAL_CONFIG || DEFAULT_CONFIG_PATH;
|
|
64
|
+
}
|
|
65
|
+
|
|
61
66
|
function loadConfig() {
|
|
62
|
-
const configPath =
|
|
67
|
+
const configPath = getConfigPath();
|
|
63
68
|
try {
|
|
64
69
|
if (!fs.existsSync(configPath)) return {};
|
|
65
70
|
const raw = fs.readFileSync(configPath, 'utf8');
|
|
@@ -70,6 +75,15 @@ function loadConfig() {
|
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
77
|
|
|
78
|
+
function saveConfig(config) {
|
|
79
|
+
const configPath = getConfigPath();
|
|
80
|
+
const normalizedConfig = Object.fromEntries(
|
|
81
|
+
Object.entries(config || {}).filter(([, value]) => value !== undefined),
|
|
82
|
+
);
|
|
83
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
84
|
+
fs.writeFileSync(configPath, `${JSON.stringify(normalizedConfig, null, 2)}\n`);
|
|
85
|
+
}
|
|
86
|
+
|
|
73
87
|
function resolveApiKey(opts, config) {
|
|
74
88
|
return opts.apiKey || process.env.SOCIALSEAL_API_KEY || config.apiKey;
|
|
75
89
|
}
|
|
@@ -86,6 +100,19 @@ function resolveSupabaseUrl(opts, config) {
|
|
|
86
100
|
return opts.supabaseUrl || process.env.SOCIALSEAL_SUPABASE_URL || config.supabaseUrl;
|
|
87
101
|
}
|
|
88
102
|
|
|
103
|
+
function resolveWorkspaceSelection(opts, config) {
|
|
104
|
+
if (typeof opts.workspaceId === 'string' && opts.workspaceId.trim().length > 0) {
|
|
105
|
+
return { workspaceId: opts.workspaceId.trim(), source: 'flag' };
|
|
106
|
+
}
|
|
107
|
+
if (typeof process.env.SOCIALSEAL_WORKSPACE_ID === 'string' && process.env.SOCIALSEAL_WORKSPACE_ID.trim().length > 0) {
|
|
108
|
+
return { workspaceId: process.env.SOCIALSEAL_WORKSPACE_ID.trim(), source: 'env' };
|
|
109
|
+
}
|
|
110
|
+
if (typeof config.workspaceId === 'string' && config.workspaceId.trim().length > 0) {
|
|
111
|
+
return { workspaceId: config.workspaceId.trim(), source: 'config' };
|
|
112
|
+
}
|
|
113
|
+
return { workspaceId: null, source: null };
|
|
114
|
+
}
|
|
115
|
+
|
|
89
116
|
class CliError extends Error {
|
|
90
117
|
constructor(message, { code = 'CLI_ERROR', exitCode = EXIT_CODES.UNKNOWN, status, hint, details } = {}) {
|
|
91
118
|
super(message);
|
|
@@ -252,6 +279,150 @@ function ensureJsonObject(value, label) {
|
|
|
252
279
|
return value;
|
|
253
280
|
}
|
|
254
281
|
|
|
282
|
+
function mergeWorkspaceIdIntoPayload(payload, workspaceId) {
|
|
283
|
+
if (!workspaceId) return payload;
|
|
284
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
285
|
+
return payload;
|
|
286
|
+
}
|
|
287
|
+
if (typeof payload.workspaceId === 'string' && payload.workspaceId.trim().length > 0) {
|
|
288
|
+
return payload;
|
|
289
|
+
}
|
|
290
|
+
return { ...payload, workspaceId };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function isJsonObject(value) {
|
|
294
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function sleep(ms) {
|
|
298
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function resolvePollIntervalMs(opts) {
|
|
302
|
+
const raw = opts.pollInterval ?? process.env.SOCIALSEAL_POLL_INTERVAL_MS;
|
|
303
|
+
return parseTimeoutMs(raw, { defaultValue: DEFAULT_POLL_INTERVAL_MS, label: 'poll interval' });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function shouldHandleSearchJourneyRunAsync(functionName, method, payload, opts) {
|
|
307
|
+
if (String(functionName || '').trim() !== 'search-journey-run') return false;
|
|
308
|
+
if (method !== 'POST') return false;
|
|
309
|
+
if (!isJsonObject(payload)) return false;
|
|
310
|
+
if (payload.action === 'status') return false;
|
|
311
|
+
return opts.async === true || payload.executionMode === 'async';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function applySearchJourneyRunAsyncStart(payload, opts) {
|
|
315
|
+
if (!shouldHandleSearchJourneyRunAsync(opts.function, normalizeMethod(opts.method), payload, opts)) {
|
|
316
|
+
return payload;
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
...payload,
|
|
320
|
+
executionMode: 'async',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function formatJsonOutput(value, pretty) {
|
|
325
|
+
return JSON.stringify(value, null, pretty ? 2 : 0);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function emitJsonOutput(value, pretty) {
|
|
329
|
+
process.stdout.write(formatJsonOutput(value, pretty) + '\n');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function buildSearchJourneyRunFailure(data) {
|
|
333
|
+
const message = isJsonObject(data) && typeof data.error === 'string' && data.error.trim().length > 0
|
|
334
|
+
? data.error
|
|
335
|
+
: 'search-journey-run failed';
|
|
336
|
+
return new CliError(message, {
|
|
337
|
+
code: 'ASYNC_RUN_FAILED',
|
|
338
|
+
exitCode: EXIT_CODES.SERVER,
|
|
339
|
+
details: truncateDetails(data),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function pollSearchJourneyRun({
|
|
344
|
+
apiBase,
|
|
345
|
+
apiKey,
|
|
346
|
+
path,
|
|
347
|
+
workspaceId,
|
|
348
|
+
timeoutMs,
|
|
349
|
+
pollIntervalMs,
|
|
350
|
+
runId,
|
|
351
|
+
opts,
|
|
352
|
+
}) {
|
|
353
|
+
if (!workspaceId) {
|
|
354
|
+
throw new CliError('Async search-journey-run polling requires a workspace id.', {
|
|
355
|
+
code: 'WORKSPACE_REQUIRED',
|
|
356
|
+
exitCode: EXIT_CODES.USAGE,
|
|
357
|
+
hint: 'Pass --workspace-id, set SOCIALSEAL_WORKSPACE_ID, or configure a default workspace.',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const deadline = Date.now() + timeoutMs;
|
|
362
|
+
let lastStatus = null;
|
|
363
|
+
|
|
364
|
+
for (;;) {
|
|
365
|
+
const remainingMs = deadline - Date.now();
|
|
366
|
+
if (remainingMs <= 0) {
|
|
367
|
+
throw new CliError('Timed out waiting for search-journey-run async completion.', {
|
|
368
|
+
code: 'ASYNC_WAIT_TIMEOUT',
|
|
369
|
+
exitCode: EXIT_CODES.SERVER,
|
|
370
|
+
hint: 'Increase --timeout <ms> or use --no-poll to return the run id immediately.',
|
|
371
|
+
details: truncateDetails({ runId, workspaceId, lastStatus }),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
await sleep(Math.min(pollIntervalMs, remainingMs));
|
|
376
|
+
|
|
377
|
+
const res = await callApi({
|
|
378
|
+
apiBase,
|
|
379
|
+
apiKey,
|
|
380
|
+
path,
|
|
381
|
+
method: 'POST',
|
|
382
|
+
body: {
|
|
383
|
+
action: 'status',
|
|
384
|
+
workspaceId,
|
|
385
|
+
runId,
|
|
386
|
+
},
|
|
387
|
+
workspaceId,
|
|
388
|
+
timeoutMs: remainingMs,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
if (!res.ok) {
|
|
392
|
+
throw await buildHttpError(res, {
|
|
393
|
+
label: 'Tool status poll',
|
|
394
|
+
functionName: 'search-journey-run',
|
|
395
|
+
method: 'POST',
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const contentType = res.headers.get('content-type') || '';
|
|
400
|
+
if (!contentType.includes('application/json')) {
|
|
401
|
+
throw new CliError('search-journey-run status poll returned a non-JSON response.', {
|
|
402
|
+
code: 'INVALID_STATUS_RESPONSE',
|
|
403
|
+
exitCode: EXIT_CODES.SERVER,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const data = await res.json();
|
|
408
|
+
const status = isJsonObject(data) && typeof data.status === 'string' ? data.status : null;
|
|
409
|
+
if (status && status !== lastStatus) {
|
|
410
|
+
emitInfo(opts, `search-journey-run status: ${status}`);
|
|
411
|
+
lastStatus = status;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (status === 'completed') return data;
|
|
415
|
+
if (status === 'failed') throw buildSearchJourneyRunFailure(data);
|
|
416
|
+
if (status === 'pending' || status === 'processing') continue;
|
|
417
|
+
|
|
418
|
+
throw new CliError('search-journey-run status poll returned an unexpected payload.', {
|
|
419
|
+
code: 'INVALID_STATUS_RESPONSE',
|
|
420
|
+
exitCode: EXIT_CODES.SERVER,
|
|
421
|
+
details: truncateDetails(data),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
255
426
|
function mapStatusToExitCode(status) {
|
|
256
427
|
if (status === 401 || status === 403) return EXIT_CODES.AUTH;
|
|
257
428
|
if (status === 404) return EXIT_CODES.NOT_FOUND;
|
|
@@ -437,6 +608,68 @@ async function callApi({ apiBase, apiKey, path, method = 'POST', body, workspace
|
|
|
437
608
|
return res;
|
|
438
609
|
}
|
|
439
610
|
|
|
611
|
+
async function fetchWorkspaceDirectory({ apiBase, apiKey, timeoutMs }) {
|
|
612
|
+
const res = await callApi({
|
|
613
|
+
apiBase,
|
|
614
|
+
apiKey,
|
|
615
|
+
path: '/cli/workspaces',
|
|
616
|
+
method: 'GET',
|
|
617
|
+
timeoutMs,
|
|
618
|
+
});
|
|
619
|
+
if (!res.ok) {
|
|
620
|
+
throw await buildHttpError(res, { label: 'Workspace discovery' });
|
|
621
|
+
}
|
|
622
|
+
const payload = await res.json();
|
|
623
|
+
return payload?.data || {};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function matchWorkspaceIdentifier(workspaces, identifier) {
|
|
627
|
+
const normalized = String(identifier || '').trim();
|
|
628
|
+
if (!normalized) {
|
|
629
|
+
throw new CliError('Missing workspace identifier.', {
|
|
630
|
+
code: 'MISSING_ARGUMENT',
|
|
631
|
+
exitCode: EXIT_CODES.USAGE,
|
|
632
|
+
hint: 'Use a workspace id, slug, or exact name from `socialseal workspace list`.',
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const exactId = workspaces.find((workspace) => workspace.id === normalized);
|
|
637
|
+
if (exactId) return exactId;
|
|
638
|
+
|
|
639
|
+
const exactSlug = workspaces.find((workspace) => workspace.slug === normalized);
|
|
640
|
+
if (exactSlug) return exactSlug;
|
|
641
|
+
|
|
642
|
+
const exactNameMatches = workspaces.filter(
|
|
643
|
+
(workspace) => typeof workspace.name === 'string' && workspace.name.trim().toLowerCase() === normalized.toLowerCase(),
|
|
644
|
+
);
|
|
645
|
+
if (exactNameMatches.length === 1) {
|
|
646
|
+
return exactNameMatches[0];
|
|
647
|
+
}
|
|
648
|
+
if (exactNameMatches.length > 1) {
|
|
649
|
+
throw new CliError(`Workspace name "${normalized}" is ambiguous.`, {
|
|
650
|
+
code: 'AMBIGUOUS_WORKSPACE',
|
|
651
|
+
exitCode: EXIT_CODES.USAGE,
|
|
652
|
+
hint: 'Use the workspace id or slug from `socialseal workspace list`.',
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
throw new CliError(`Workspace "${normalized}" was not found.`, {
|
|
657
|
+
code: 'WORKSPACE_NOT_FOUND',
|
|
658
|
+
exitCode: EXIT_CODES.NOT_FOUND,
|
|
659
|
+
hint: 'Run `socialseal workspace list` to discover available workspaces.',
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function formatWorkspaceLine(workspace, { isEffective = false, source = null, isSuggested = false } = {}) {
|
|
664
|
+
const tags = [];
|
|
665
|
+
if (workspace.isPersonalWorkspace) tags.push('personal');
|
|
666
|
+
if (isEffective) tags.push(source === 'config' ? 'default' : `active:${source}`);
|
|
667
|
+
if (isSuggested) tags.push('suggested');
|
|
668
|
+
const tagText = tags.length > 0 ? ` [${tags.join(', ')}]` : '';
|
|
669
|
+
const slugText = workspace.slug ? ` slug=${workspace.slug}` : '';
|
|
670
|
+
return `- ${workspace.name} (${workspace.id}) role=${workspace.role}${slugText}${tagText}`;
|
|
671
|
+
}
|
|
672
|
+
|
|
440
673
|
async function handleAgentRun(opts) {
|
|
441
674
|
const config = loadConfig();
|
|
442
675
|
const apiKey = requireApiKey(opts, config);
|
|
@@ -445,12 +678,33 @@ async function handleAgentRun(opts) {
|
|
|
445
678
|
const { resolvedApiBase, legacyUrl } = resolveApiTarget({ apiBase, legacyUrl: agentUrl });
|
|
446
679
|
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
447
680
|
const idleTimeoutMs = resolveAgentIdleTimeoutMs(opts, config, timeoutMs);
|
|
681
|
+
const continuationToken = typeof opts.continue === 'string' ? opts.continue.trim() : '';
|
|
682
|
+
const { workspaceId: resolvedWorkspaceIdInput } = resolveWorkspaceSelection(opts, config);
|
|
683
|
+
|
|
684
|
+
if (continuationToken && opts.conversationId) {
|
|
685
|
+
throw new CliError('Use either --continue or --conversation-id, not both.', {
|
|
686
|
+
code: 'INVALID_ARGUMENTS',
|
|
687
|
+
exitCode: EXIT_CODES.USAGE,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (continuationToken && opts.createNew) {
|
|
691
|
+
throw new CliError('Use either --continue or --create-new, not both.', {
|
|
692
|
+
code: 'INVALID_ARGUMENTS',
|
|
693
|
+
exitCode: EXIT_CODES.USAGE,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if (opts.conversationId && opts.createNew) {
|
|
697
|
+
throw new CliError('Use either --conversation-id or --create-new, not both.', {
|
|
698
|
+
code: 'INVALID_ARGUMENTS',
|
|
699
|
+
exitCode: EXIT_CODES.USAGE,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
448
702
|
|
|
449
703
|
const headers = {
|
|
450
704
|
'Content-Type': 'application/json',
|
|
451
705
|
[CLI_KEY_HEADER]: apiKey,
|
|
452
706
|
};
|
|
453
|
-
if (
|
|
707
|
+
if (resolvedWorkspaceIdInput) headers[WORKSPACE_HEADER] = resolvedWorkspaceIdInput;
|
|
454
708
|
|
|
455
709
|
const sessionUrl = resolvedApiBase
|
|
456
710
|
? `${resolvedApiBase.replace(/\/$/, '')}/cli/agent/session`
|
|
@@ -460,8 +714,9 @@ async function handleAgentRun(opts) {
|
|
|
460
714
|
method: 'POST',
|
|
461
715
|
headers,
|
|
462
716
|
body: JSON.stringify({
|
|
463
|
-
|
|
464
|
-
|
|
717
|
+
continuationToken: continuationToken || undefined,
|
|
718
|
+
conversationId: continuationToken ? undefined : (opts.conversationId || undefined),
|
|
719
|
+
createNew: continuationToken || opts.conversationId ? undefined : true,
|
|
465
720
|
}),
|
|
466
721
|
}, timeoutMs);
|
|
467
722
|
|
|
@@ -472,6 +727,8 @@ async function handleAgentRun(opts) {
|
|
|
472
727
|
const sessionData = await sessionRes.json();
|
|
473
728
|
const sessionId = sessionData?.data?.sessionId || null;
|
|
474
729
|
const initialConversationId = sessionData?.data?.activeConversationId || opts.conversationId || null;
|
|
730
|
+
const resolvedWorkspaceId = sessionData?.data?.workspaceId || resolvedWorkspaceIdInput || null;
|
|
731
|
+
const nextContinuationToken = sessionData?.data?.continuationToken || null;
|
|
475
732
|
const wsUrl = sessionData?.data?.websocketUrl;
|
|
476
733
|
if (!wsUrl) {
|
|
477
734
|
throw new CliError('Missing websocketUrl in session response.', {
|
|
@@ -483,6 +740,19 @@ async function handleAgentRun(opts) {
|
|
|
483
740
|
opts,
|
|
484
741
|
`Agent session created${sessionId ? ` (session ${sessionId})` : ''}${initialConversationId ? ` for conversation ${initialConversationId}` : ''}.`,
|
|
485
742
|
);
|
|
743
|
+
if (opts.json) {
|
|
744
|
+
process.stdout.write(JSON.stringify({
|
|
745
|
+
type: 'session_bootstrap',
|
|
746
|
+
payload: {
|
|
747
|
+
sessionId,
|
|
748
|
+
conversationId: initialConversationId,
|
|
749
|
+
workspaceId: resolvedWorkspaceId,
|
|
750
|
+
continuationToken: nextContinuationToken,
|
|
751
|
+
},
|
|
752
|
+
}) + '\n');
|
|
753
|
+
} else if (nextContinuationToken) {
|
|
754
|
+
process.stderr.write(`[socialseal] Continuation token: ${nextContinuationToken}\n`);
|
|
755
|
+
}
|
|
486
756
|
|
|
487
757
|
const context = parseJsonInput(opts.context, { label: 'context', allowString: true });
|
|
488
758
|
const message = opts.message;
|
|
@@ -721,16 +991,19 @@ async function handleToolsCall(opts) {
|
|
|
721
991
|
const supabaseUrl = resolveLegacyUrl(resolveSupabaseUrl(opts, config), 'SOCIALSEAL_SUPABASE_URL');
|
|
722
992
|
const { resolvedApiBase, legacyUrl, useGateway } = resolveApiTarget({ apiBase, legacyUrl: supabaseUrl });
|
|
723
993
|
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
994
|
+
const { workspaceId: resolvedWorkspaceId } = resolveWorkspaceSelection(opts, config);
|
|
724
995
|
|
|
725
|
-
const
|
|
996
|
+
const parsedPayload = parseJsonInput(opts.body, { label: 'body' }) ?? {};
|
|
997
|
+
const mergedPayload = mergeWorkspaceIdIntoPayload(parsedPayload, resolvedWorkspaceId);
|
|
726
998
|
const method = normalizeMethod(opts.method);
|
|
999
|
+
const payload = applySearchJourneyRunAsyncStart(mergedPayload, { ...opts, method });
|
|
727
1000
|
const res = await callApi({
|
|
728
1001
|
apiBase: useGateway ? resolvedApiBase : legacyUrl,
|
|
729
1002
|
apiKey,
|
|
730
1003
|
path: useGateway ? `/cli/tools/${opts.function}` : `/functions/v1/${opts.function}`,
|
|
731
1004
|
method,
|
|
732
1005
|
body: payload,
|
|
733
|
-
workspaceId:
|
|
1006
|
+
workspaceId: resolvedWorkspaceId,
|
|
734
1007
|
timeoutMs,
|
|
735
1008
|
});
|
|
736
1009
|
|
|
@@ -745,7 +1018,45 @@ async function handleToolsCall(opts) {
|
|
|
745
1018
|
const contentType = res.headers.get('content-type') || '';
|
|
746
1019
|
if (contentType.includes('application/json')) {
|
|
747
1020
|
const data = await res.json();
|
|
748
|
-
|
|
1021
|
+
const shouldPoll = shouldHandleSearchJourneyRunAsync(opts.function, method, payload, opts) && opts.poll !== false;
|
|
1022
|
+
if (!shouldPoll) {
|
|
1023
|
+
emitJsonOutput(data, opts.pretty);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
const startStatus = isJsonObject(data) && typeof data.status === 'string' ? data.status : null;
|
|
1028
|
+
if (startStatus === 'failed') {
|
|
1029
|
+
throw buildSearchJourneyRunFailure(data);
|
|
1030
|
+
}
|
|
1031
|
+
if (startStatus === 'completed') {
|
|
1032
|
+
emitJsonOutput(data, opts.pretty);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const runId = isJsonObject(data) && typeof data.runId === 'string' ? data.runId : null;
|
|
1037
|
+
if (!runId) {
|
|
1038
|
+
throw new CliError('Async search-journey-run start response did not include a runId.', {
|
|
1039
|
+
code: 'INVALID_START_RESPONSE',
|
|
1040
|
+
exitCode: EXIT_CODES.SERVER,
|
|
1041
|
+
details: truncateDetails(data),
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
emitInfo(opts, `search-journey-run async run started: ${runId}`);
|
|
1046
|
+
const finalData = await pollSearchJourneyRun({
|
|
1047
|
+
apiBase: useGateway ? resolvedApiBase : legacyUrl,
|
|
1048
|
+
apiKey,
|
|
1049
|
+
path: useGateway ? `/cli/tools/${opts.function}` : `/functions/v1/${opts.function}`,
|
|
1050
|
+
workspaceId: isJsonObject(payload) && typeof payload.workspaceId === 'string'
|
|
1051
|
+
? payload.workspaceId
|
|
1052
|
+
: resolvedWorkspaceId,
|
|
1053
|
+
timeoutMs,
|
|
1054
|
+
pollIntervalMs: resolvePollIntervalMs(opts),
|
|
1055
|
+
runId,
|
|
1056
|
+
opts,
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
emitJsonOutput(finalData, opts.pretty);
|
|
749
1060
|
return;
|
|
750
1061
|
}
|
|
751
1062
|
|
|
@@ -787,6 +1098,7 @@ async function handleDataExportTracking(opts) {
|
|
|
787
1098
|
const supabaseUrl = resolveLegacyUrl(resolveSupabaseUrl(opts, config), 'SOCIALSEAL_SUPABASE_URL');
|
|
788
1099
|
const { resolvedApiBase, legacyUrl, useGateway } = resolveApiTarget({ apiBase, legacyUrl: supabaseUrl });
|
|
789
1100
|
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
1101
|
+
const { workspaceId: resolvedWorkspaceId } = resolveWorkspaceSelection(opts, config);
|
|
790
1102
|
|
|
791
1103
|
if (!opts.groupId && !opts.itemId) {
|
|
792
1104
|
throw new CliError('Provide --group-id or --item-id.', {
|
|
@@ -807,7 +1119,7 @@ async function handleDataExportTracking(opts) {
|
|
|
807
1119
|
path: useGateway ? '/cli/tools/export_tracking_data' : '/functions/v1/export_tracking_data',
|
|
808
1120
|
method: 'POST',
|
|
809
1121
|
body: payload,
|
|
810
|
-
workspaceId:
|
|
1122
|
+
workspaceId: resolvedWorkspaceId,
|
|
811
1123
|
timeoutMs,
|
|
812
1124
|
});
|
|
813
1125
|
|
|
@@ -838,6 +1150,7 @@ async function handleDataExportReport(opts) {
|
|
|
838
1150
|
const supabaseUrl = resolveLegacyUrl(resolveSupabaseUrl(opts, config), 'SOCIALSEAL_SUPABASE_URL');
|
|
839
1151
|
const { resolvedApiBase, legacyUrl, useGateway } = resolveApiTarget({ apiBase, legacyUrl: supabaseUrl });
|
|
840
1152
|
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
1153
|
+
const { workspaceId: resolvedWorkspaceId } = resolveWorkspaceSelection(opts, config);
|
|
841
1154
|
|
|
842
1155
|
const payload = ensureJsonObject(parseJsonInput(opts.payload, { label: 'payload' }), 'payload');
|
|
843
1156
|
|
|
@@ -851,7 +1164,7 @@ async function handleDataExportReport(opts) {
|
|
|
851
1164
|
format: opts.format,
|
|
852
1165
|
payload,
|
|
853
1166
|
},
|
|
854
|
-
workspaceId:
|
|
1167
|
+
workspaceId: resolvedWorkspaceId,
|
|
855
1168
|
timeoutMs,
|
|
856
1169
|
});
|
|
857
1170
|
|
|
@@ -893,6 +1206,150 @@ async function handleDataExportReport(opts) {
|
|
|
893
1206
|
process.stdout.write(JSON.stringify(json, null, opts.pretty ? 2 : 0) + '\n');
|
|
894
1207
|
}
|
|
895
1208
|
|
|
1209
|
+
async function handleWorkspaceList(opts) {
|
|
1210
|
+
const config = loadConfig();
|
|
1211
|
+
const apiKey = requireApiKey(opts, config);
|
|
1212
|
+
const apiBase = resolveApiBase(opts, config);
|
|
1213
|
+
const { resolvedApiBase } = resolveApiTarget({ apiBase, legacyUrl: null });
|
|
1214
|
+
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
1215
|
+
const directory = await fetchWorkspaceDirectory({
|
|
1216
|
+
apiBase: resolvedApiBase,
|
|
1217
|
+
apiKey,
|
|
1218
|
+
timeoutMs,
|
|
1219
|
+
});
|
|
1220
|
+
const selection = resolveWorkspaceSelection({}, config);
|
|
1221
|
+
const workspaces = Array.isArray(directory.workspaces) ? directory.workspaces : [];
|
|
1222
|
+
const payload = {
|
|
1223
|
+
...directory,
|
|
1224
|
+
effectiveWorkspaceId: selection.workspaceId,
|
|
1225
|
+
effectiveWorkspaceSource: selection.source,
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
if (opts.json) {
|
|
1229
|
+
process.stdout.write(JSON.stringify(payload, null, opts.pretty ? 2 : 0) + '\n');
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
process.stdout.write('[socialseal] Available workspaces\n');
|
|
1234
|
+
if (workspaces.length === 0) {
|
|
1235
|
+
process.stdout.write('[socialseal] No accessible workspaces were returned for this key.\n');
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
for (const workspace of workspaces) {
|
|
1240
|
+
const isEffective = selection.workspaceId === workspace.id;
|
|
1241
|
+
const isSuggested = !selection.workspaceId && directory.defaultWorkspaceId === workspace.id;
|
|
1242
|
+
process.stdout.write(`${formatWorkspaceLine(workspace, { isEffective, source: selection.source, isSuggested })}\n`);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (!selection.workspaceId && directory.defaultWorkspaceId) {
|
|
1246
|
+
process.stdout.write('\n[socialseal] No local default is configured. Set one with: socialseal workspace use <id>\n');
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
async function handleWorkspaceCurrent(opts) {
|
|
1251
|
+
const config = loadConfig();
|
|
1252
|
+
const apiKey = requireApiKey(opts, config);
|
|
1253
|
+
const apiBase = resolveApiBase(opts, config);
|
|
1254
|
+
const { resolvedApiBase } = resolveApiTarget({ apiBase, legacyUrl: null });
|
|
1255
|
+
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
1256
|
+
const directory = await fetchWorkspaceDirectory({
|
|
1257
|
+
apiBase: resolvedApiBase,
|
|
1258
|
+
apiKey,
|
|
1259
|
+
timeoutMs,
|
|
1260
|
+
});
|
|
1261
|
+
const selection = resolveWorkspaceSelection({}, config);
|
|
1262
|
+
const workspaces = Array.isArray(directory.workspaces) ? directory.workspaces : [];
|
|
1263
|
+
const effectiveWorkspace = selection.workspaceId
|
|
1264
|
+
? workspaces.find((workspace) => workspace.id === selection.workspaceId) || null
|
|
1265
|
+
: null;
|
|
1266
|
+
|
|
1267
|
+
if (selection.workspaceId && !effectiveWorkspace) {
|
|
1268
|
+
throw new CliError(`Configured workspace "${selection.workspaceId}" is not accessible with this CLI key.`, {
|
|
1269
|
+
code: 'WORKSPACE_NOT_ACCESSIBLE',
|
|
1270
|
+
exitCode: EXIT_CODES.NOT_FOUND,
|
|
1271
|
+
hint: 'Run `socialseal workspace list` to pick a valid workspace or `socialseal workspace clear` to unset the default.',
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
const payload = {
|
|
1276
|
+
effectiveWorkspaceId: selection.workspaceId,
|
|
1277
|
+
effectiveWorkspaceSource: selection.source,
|
|
1278
|
+
workspace: effectiveWorkspace,
|
|
1279
|
+
defaultWorkspaceId: directory.defaultWorkspaceId || null,
|
|
1280
|
+
personalWorkspaceId: directory.personalWorkspaceId || null,
|
|
1281
|
+
};
|
|
1282
|
+
if (opts.json) {
|
|
1283
|
+
process.stdout.write(JSON.stringify(payload, null, opts.pretty ? 2 : 0) + '\n');
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
if (effectiveWorkspace) {
|
|
1288
|
+
process.stdout.write(`[socialseal] Effective workspace: ${effectiveWorkspace.name} (${effectiveWorkspace.id}) via ${selection.source}\n`);
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
if (directory.defaultWorkspaceId) {
|
|
1293
|
+
const suggestedWorkspace = workspaces.find((workspace) => workspace.id === directory.defaultWorkspaceId) || null;
|
|
1294
|
+
if (suggestedWorkspace) {
|
|
1295
|
+
process.stdout.write(`[socialseal] No local default workspace is configured. Suggested workspace: ${suggestedWorkspace.name} (${suggestedWorkspace.id})\n`);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
process.stdout.write('[socialseal] No default workspace is configured and no accessible workspace suggestion is available.\n');
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
async function handleWorkspaceUse(opts) {
|
|
1304
|
+
const config = loadConfig();
|
|
1305
|
+
const apiKey = requireApiKey(opts, config);
|
|
1306
|
+
const apiBase = resolveApiBase(opts, config);
|
|
1307
|
+
const { resolvedApiBase } = resolveApiTarget({ apiBase, legacyUrl: null });
|
|
1308
|
+
const timeoutMs = resolveTimeoutMs(opts, config);
|
|
1309
|
+
const directory = await fetchWorkspaceDirectory({
|
|
1310
|
+
apiBase: resolvedApiBase,
|
|
1311
|
+
apiKey,
|
|
1312
|
+
timeoutMs,
|
|
1313
|
+
});
|
|
1314
|
+
const workspaces = Array.isArray(directory.workspaces) ? directory.workspaces : [];
|
|
1315
|
+
const workspace = matchWorkspaceIdentifier(workspaces, opts.identifier);
|
|
1316
|
+
saveConfig({
|
|
1317
|
+
...config,
|
|
1318
|
+
workspaceId: workspace.id,
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
const payload = {
|
|
1322
|
+
success: true,
|
|
1323
|
+
workspaceId: workspace.id,
|
|
1324
|
+
workspace,
|
|
1325
|
+
configPath: getConfigPath(),
|
|
1326
|
+
};
|
|
1327
|
+
if (opts.json) {
|
|
1328
|
+
process.stdout.write(JSON.stringify(payload, null, opts.pretty ? 2 : 0) + '\n');
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
process.stdout.write(`[socialseal] Default workspace set to ${workspace.name} (${workspace.id})\n`);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function handleWorkspaceClear(opts) {
|
|
1336
|
+
const config = loadConfig();
|
|
1337
|
+
const nextConfig = { ...config };
|
|
1338
|
+
delete nextConfig.workspaceId;
|
|
1339
|
+
saveConfig(nextConfig);
|
|
1340
|
+
|
|
1341
|
+
const payload = {
|
|
1342
|
+
success: true,
|
|
1343
|
+
configPath: getConfigPath(),
|
|
1344
|
+
};
|
|
1345
|
+
if (opts.json) {
|
|
1346
|
+
process.stdout.write(JSON.stringify(payload, null, opts.pretty ? 2 : 0) + '\n');
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
process.stdout.write('[socialseal] Default workspace cleared.\n');
|
|
1351
|
+
}
|
|
1352
|
+
|
|
896
1353
|
const program = new Command();
|
|
897
1354
|
program
|
|
898
1355
|
.name('socialseal')
|
|
@@ -905,7 +1362,7 @@ if (typeof program.showHelpAfterError === 'function') {
|
|
|
905
1362
|
if (typeof program.showSuggestionAfterError === 'function') {
|
|
906
1363
|
program.showSuggestionAfterError(true);
|
|
907
1364
|
}
|
|
908
|
-
program.addHelpText('after', `\nExamples:\n socialseal agent run --message \"ping\"\n socialseal tools list\n socialseal tools call --function <tool> --body @payload.json\n socialseal data export-tracking --group-id 123 --time-period 30d\n`);
|
|
1365
|
+
program.addHelpText('after', `\nExamples:\n socialseal workspace list\n socialseal workspace use <workspace-id>\n socialseal agent run --message \"ping\"\n socialseal tools list\n socialseal tools call --function <tool> --body @payload.json\n socialseal tools call --function search-journey-run --body @payload.json --async --workspace-id <uuid>\n socialseal data export-tracking --group-id 123 --time-period 30d\n`);
|
|
909
1366
|
|
|
910
1367
|
program
|
|
911
1368
|
.command('agent')
|
|
@@ -916,6 +1373,7 @@ program
|
|
|
916
1373
|
.option('--api-base <url>', 'API base URL (default https://api.socialseal.co)')
|
|
917
1374
|
.option('--api-key <key>', 'CLI API key')
|
|
918
1375
|
.option('--workspace-id <id>', 'Workspace id (for scoped keys)')
|
|
1376
|
+
.option('--continue <token>', 'Continuation token from a previous agent run')
|
|
919
1377
|
.option('--conversation-id <id>', 'Conversation id to resume')
|
|
920
1378
|
.option('--create-new', 'Create a new conversation')
|
|
921
1379
|
.option('--json', 'Emit NDJSON events')
|
|
@@ -924,6 +1382,49 @@ program
|
|
|
924
1382
|
.option('--verbose', 'Show error details')
|
|
925
1383
|
.action((opts) => runCommand(handleAgentRun, opts));
|
|
926
1384
|
|
|
1385
|
+
const workspace = program.command('workspace').description('Discover and manage the default workspace');
|
|
1386
|
+
|
|
1387
|
+
workspace
|
|
1388
|
+
.command('list')
|
|
1389
|
+
.description('List accessible workspaces for this CLI key')
|
|
1390
|
+
.option('--api-base <url>', 'API base URL (default https://api.socialseal.co)')
|
|
1391
|
+
.option('--api-key <key>', 'CLI API key')
|
|
1392
|
+
.option('--json', 'Emit machine-readable output')
|
|
1393
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
1394
|
+
.option('--timeout <ms>', 'Request timeout in milliseconds')
|
|
1395
|
+
.option('--verbose', 'Show error details')
|
|
1396
|
+
.action((opts) => runCommand(handleWorkspaceList, opts));
|
|
1397
|
+
|
|
1398
|
+
workspace
|
|
1399
|
+
.command('current')
|
|
1400
|
+
.description('Show the effective default workspace')
|
|
1401
|
+
.option('--api-base <url>', 'API base URL (default https://api.socialseal.co)')
|
|
1402
|
+
.option('--api-key <key>', 'CLI API key')
|
|
1403
|
+
.option('--json', 'Emit machine-readable output')
|
|
1404
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
1405
|
+
.option('--timeout <ms>', 'Request timeout in milliseconds')
|
|
1406
|
+
.option('--verbose', 'Show error details')
|
|
1407
|
+
.action((opts) => runCommand(handleWorkspaceCurrent, opts));
|
|
1408
|
+
|
|
1409
|
+
workspace
|
|
1410
|
+
.command('use <identifier>')
|
|
1411
|
+
.description('Persist a default workspace by id, slug, or exact name')
|
|
1412
|
+
.option('--api-base <url>', 'API base URL (default https://api.socialseal.co)')
|
|
1413
|
+
.option('--api-key <key>', 'CLI API key')
|
|
1414
|
+
.option('--json', 'Emit machine-readable output')
|
|
1415
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
1416
|
+
.option('--timeout <ms>', 'Request timeout in milliseconds')
|
|
1417
|
+
.option('--verbose', 'Show error details')
|
|
1418
|
+
.action((identifier, opts) => runCommand(handleWorkspaceUse, { ...opts, identifier }));
|
|
1419
|
+
|
|
1420
|
+
workspace
|
|
1421
|
+
.command('clear')
|
|
1422
|
+
.description('Clear the locally configured default workspace')
|
|
1423
|
+
.option('--json', 'Emit machine-readable output')
|
|
1424
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
1425
|
+
.option('--verbose', 'Show error details')
|
|
1426
|
+
.action((opts) => runCommand(handleWorkspaceClear, opts));
|
|
1427
|
+
|
|
927
1428
|
const tools = program.command('tools').description('Call edge functions directly (tool backends)');
|
|
928
1429
|
|
|
929
1430
|
tools
|
|
@@ -939,6 +1440,9 @@ tools
|
|
|
939
1440
|
.requiredOption('--function <name>', 'Tool name (see official docs)')
|
|
940
1441
|
.option('--method <method>', 'HTTP method', 'POST')
|
|
941
1442
|
.option('--body <jsonOrFile>', 'JSON body or @file.json')
|
|
1443
|
+
.option('--async', 'Request async execution for supported tool backends')
|
|
1444
|
+
.option('--no-poll', 'Return immediately after async start instead of polling to completion')
|
|
1445
|
+
.option('--poll-interval <ms>', 'Polling interval in milliseconds for supported async tool calls')
|
|
942
1446
|
.option('--api-base <url>', 'API base URL (default https://api.socialseal.co)')
|
|
943
1447
|
.option('--api-key <key>', 'CLI API key')
|
|
944
1448
|
.option('--workspace-id <id>', 'Workspace id (for scoped keys)')
|