@trydreamstate/cli 1.2.0 → 1.3.0
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/README.md +11 -1
- package/agent-plugin/.agents/plugins/marketplace.json +20 -0
- package/agent-plugin/.claude-plugin/marketplace.json +16 -0
- package/agent-plugin/.grok-plugin/marketplace.json +16 -0
- package/agent-plugin/plugins/dreamstate/.claude-plugin/plugin.json +21 -0
- package/agent-plugin/plugins/dreamstate/.codex-plugin/plugin.json +41 -0
- package/agent-plugin/plugins/dreamstate/.mcp.json +8 -0
- package/agent-plugin/plugins/dreamstate/skills/crm/SKILL.md +75 -0
- package/agent-plugin/plugins/dreamstate/skills/diagnostics/SKILL.md +72 -0
- package/agent-plugin/plugins/dreamstate/skills/dreamstate/SKILL.md +16 -0
- package/agent-plugin/plugins/dreamstate/skills/first-win/SKILL.md +57 -0
- package/agent-plugin/plugins/dreamstate/skills/inbox-replies/SKILL.md +61 -0
- package/agent-plugin/plugins/dreamstate/skills/knowledge/SKILL.md +48 -0
- package/agent-plugin/plugins/dreamstate/skills/linkedin-posts/SKILL.md +79 -0
- package/agent-plugin/plugins/dreamstate/skills/linkedin-warm-signals/SKILL.md +65 -0
- package/agent-plugin/plugins/dreamstate/skills/list-building/SKILL.md +105 -0
- package/agent-plugin/plugins/dreamstate/skills/list-building/column-authoring.md +37 -0
- package/agent-plugin/plugins/dreamstate/skills/list-building/readback.md +15 -0
- package/agent-plugin/plugins/dreamstate/skills/list-building/sourcing.md +31 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/SKILL.md +77 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/channels.md +5 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/deliverability-and-caps.md +17 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/enrollment.md +17 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/message-copy.md +41 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/replies.md +5 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/steps-and-timing.md +13 -0
- package/agent-plugin/plugins/dreamstate/skills/outreach/stop-and-suppression.md +7 -0
- package/agent-plugin/plugins/dreamstate/skills/plays/SKILL.md +81 -0
- package/agent-plugin/plugins/dreamstate/skills/reddit-posts/SKILL.md +65 -0
- package/agent-plugin/plugins/dreamstate/skills/skill-authoring/SKILL.md +65 -0
- package/agent-plugin/plugins/dreamstate/skills/social/SKILL.md +69 -0
- package/agent-plugin/plugins/dreamstate/skills/visibility/SKILL.md +60 -0
- package/agent-plugin/plugins/dreamstate/skills/visibility/probes.md +15 -0
- package/agent-plugin/plugins/dreamstate/skills/visibility/query-sets.md +17 -0
- package/agent-plugin/plugins/dreamstate/skills/visibility/search-evidence.md +15 -0
- package/agent-plugin/plugins/dreamstate/skills/visibility/technical.md +16 -0
- package/agent-plugin/plugins/dreamstate/skills/workflows/SKILL.md +43 -0
- package/agent-plugin/plugins/dreamstate/skills/workflows/durable-runs.md +19 -0
- package/agent-plugin/plugins/dreamstate/skills/workflows/graph-authoring.md +9 -0
- package/agent-plugin/plugins/dreamstate/skills/workflows/lifecycle-and-enrollment.md +11 -0
- package/agent-plugin/plugins/dreamstate/skills/workflows/live-replies.md +5 -0
- package/agent-plugin/plugins/dreamstate/skills/workflows/recipe-routing.md +5 -0
- package/agent-plugin/plugins/dreamstate/skills/worksheet-tidy/SKILL.md +61 -0
- package/agent-plugin/plugins/dreamstate/skills/writing/SKILL.md +84 -0
- package/agent-plugin/plugins/dreamstate/skills/writing/briefs.md +35 -0
- package/agent-plugin/plugins/dreamstate/skills/writing/drafting.md +39 -0
- package/agent-plugin/plugins/dreamstate/skills/writing/publishing.md +74 -0
- package/agent-plugin/plugins/dreamstate/skills/writing/revision-and-voice.md +31 -0
- package/agent-plugin/plugins/dreamstate/skills/x-posts/SKILL.md +73 -0
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +126 -41
- package/dist/commands/sequences.d.ts +1 -1
- package/dist/commands/sequences.d.ts.map +1 -1
- package/dist/commands/sequences.js +3 -3
- package/dist/commands/shared.d.ts +2 -1
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/shared.js +98 -20
- package/dist/commands/workflows.d.ts +1 -1
- package/dist/commands/workflows.d.ts.map +1 -1
- package/dist/commands/workflows.js +9 -9
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +95 -2
- package/dist/index.d.ts +51 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +421 -67
- package/dist/pluginInstaller.d.ts +22 -0
- package/dist/pluginInstaller.d.ts.map +1 -0
- package/dist/pluginInstaller.js +122 -0
- package/dist/runWait.d.ts +9 -0
- package/dist/runWait.d.ts.map +1 -0
- package/dist/runWait.js +76 -0
- package/dist/skillInstaller.d.ts +1 -0
- package/dist/skillInstaller.d.ts.map +1 -1
- package/dist/skillInstaller.js +18 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.CliUsageError = exports.CLI_EXIT = void 0;
|
|
5
5
|
exports.parseCliArgs = parseCliArgs;
|
|
6
6
|
exports.toolTurnToken = toolTurnToken;
|
|
7
|
+
exports.omitsToolTurnForInlineTableRead = omitsToolTurnForInlineTableRead;
|
|
8
|
+
exports.capabilityIdempotencyKey = capabilityIdempotencyKey;
|
|
7
9
|
exports.refusalEnvelope = refusalEnvelope;
|
|
8
10
|
exports.openWebUrl = openWebUrl;
|
|
9
11
|
exports.exitCodeForError = exitCodeForError;
|
|
@@ -14,9 +16,12 @@ const node_crypto_1 = require("node:crypto");
|
|
|
14
16
|
const promises_1 = require("node:fs/promises");
|
|
15
17
|
const tableFilePreview_1 = require("./tableFilePreview");
|
|
16
18
|
const node_os_1 = require("node:os");
|
|
19
|
+
const node_path_1 = require("node:path");
|
|
17
20
|
const node_util_1 = require("node:util");
|
|
18
21
|
const config_1 = require("./config");
|
|
22
|
+
const pluginInstaller_1 = require("./pluginInstaller");
|
|
19
23
|
const turnCache_1 = require("./turnCache");
|
|
24
|
+
const runWait_1 = require("./runWait");
|
|
20
25
|
const client_1 = require("./client");
|
|
21
26
|
const skillInstaller_1 = require("./skillInstaller");
|
|
22
27
|
exports.CLI_EXIT = { SUCCESS: 0, UNCLASSIFIED: 1, USAGE: 2, AUTH: 3, NOT_FOUND: 4, SERVER: 5, RATE_LIMITED: 6, APPROVAL: 7, TIMEOUT: 8, REFUSED: 9, TRANSPORT: 10 };
|
|
@@ -26,13 +31,16 @@ const USAGE = `Dreamstate CLI
|
|
|
26
31
|
Usage:
|
|
27
32
|
dreamstate --help
|
|
28
33
|
dreamstate --version
|
|
29
|
-
dreamstate login
|
|
30
|
-
dreamstate login --token <ds_user_...|ds_live_...>
|
|
34
|
+
dreamstate login [--no-skills]
|
|
35
|
+
dreamstate login [--profile <name>] --token <ds_user_...|ds_live_...> [--no-skills]
|
|
31
36
|
dreamstate logout
|
|
37
|
+
dreamstate install [--agents <claude codex grok>] [--json-output|--json]
|
|
38
|
+
dreamstate status [--json-output|--json]
|
|
39
|
+
dreamstate profiles <list|use|current|remove|env> [name] [--json-output|--json]
|
|
32
40
|
dreamstate whoami [--json-output|--json]
|
|
33
41
|
dreamstate workspaces list [--json-output|--json]
|
|
34
42
|
dreamstate workspaces use <id|slug>
|
|
35
|
-
dreamstate auth login [--api-key-file <path>]
|
|
43
|
+
dreamstate auth login [--api-key-file <path>] [--no-skills]
|
|
36
44
|
dreamstate auth whoami [--json-output|--json]
|
|
37
45
|
dreamstate packs list [--json-output|--json]
|
|
38
46
|
dreamstate packs get <pack_id> [--json-output|--json]
|
|
@@ -45,18 +53,19 @@ Usage:
|
|
|
45
53
|
dreamstate commands search <term> [--json-output|--json]
|
|
46
54
|
dreamstate tools search --goal <goal> [--terms <csv>] [--limit <n>] [--toolset <pack_id>] [--json-output|--json]
|
|
47
55
|
dreamstate tools get <capability_id> [--json-output|--json]
|
|
48
|
-
dreamstate tools run <capability_id> [--params <object>|--json <body>] [--turn-token <token>] (--live | --dry-run | --mode <smoke_test|dry_run|live>) [--agent-session-id <uuid>] [--approved] [--approved-effect-unknown] [--max-credits <n>] [--idempotency-key <key>] [--open] [--json-output|--json]
|
|
56
|
+
dreamstate tools run <capability_id> [--params <object>|--json <body>] [--turn-token <token>] (--live | --dry-run | --mode <smoke_test|dry_run|live>) [--agent-session-id <uuid>] [--approved] [--approved-effect-unknown] [--max-credits <n>] [--idempotency-key <key>] [--wait-timeout <seconds>] [--no-wait] [--open] [--json-output|--json]
|
|
49
57
|
dreamstate sequences <verb> [args] [flags]
|
|
50
58
|
dreamstate workflows <verb> [args] [flags]
|
|
51
59
|
dreamstate tables import --table-id <uuid> --file <path> [--format <csv|json|jsonl|xlsx>] [--preview] [--upsert-key <header>] [--background] [--json-output|--json]
|
|
52
|
-
dreamstate runs list [--json <query>] [--json-output|--json]
|
|
60
|
+
dreamstate runs list [--limit <n>] [--cursor <cursor>] [--json <query>] [--json-output|--json]
|
|
53
61
|
dreamstate runs get <run_id> [--json-output|--json]
|
|
54
62
|
dreamstate runs resume <run_id> --state-version <n> [--reason <text>] [--idempotency-key <key>] [--open] [--json-output|--json]
|
|
55
63
|
dreamstate runs events <run_id> [--json-output|--json]
|
|
56
|
-
dreamstate runs cancel <run_id> --json <body> [--idempotency-key <key>] [--json-output|--json]
|
|
64
|
+
dreamstate runs cancel <run_id> [--state-version <n>] [--reason <text>] [--json <body>] [--idempotency-key <key>] [--json-output|--json]
|
|
57
65
|
|
|
58
66
|
All catalog discovery reads live API records. Set DREAMSTATE_API_URL and DREAMSTATE_API_KEY.
|
|
59
|
-
|
|
67
|
+
Run responses wait for terminal run status by default, timeout default is 120 seconds, --no-wait returns immediate response. Timeout prints latest run envelope and exits ${runWait_1.RUN_WAIT_TIMEOUT_EXIT_CODE}.
|
|
68
|
+
login opens browser approval and installs Dreamstate skills for detected agents; --no-skills skips installation. login --token stores a ds_user_ session or ds_live_ key (for SSH or remote machines) after verifying it with whoami.
|
|
60
69
|
Global flag --workspace <id|slug> (any command) or DREAMSTATE_WORKSPACE picks the workspace for one call; workspaces use stores a default. Flag beats env beats stored. A ds_live_ key is bound to one workspace and cannot switch.
|
|
61
70
|
logout deletes the stored credentials file. --agents takes space-separated, comma-separated or repeated values.
|
|
62
71
|
`;
|
|
@@ -106,6 +115,22 @@ function nonNegativeInteger(raw, flag) {
|
|
|
106
115
|
throw new CliUsageError(`${flag} must be a non-negative integer`);
|
|
107
116
|
return value;
|
|
108
117
|
}
|
|
118
|
+
// runs.get returns { data: { run: { state_version } } }. Cancel posts
|
|
119
|
+
// expected_state_version, and a missing read must not invent 0.
|
|
120
|
+
function stateVersionFromRunRead(payload) {
|
|
121
|
+
const record = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload : null;
|
|
122
|
+
const data = record && record.data && typeof record.data === 'object' && !Array.isArray(record.data)
|
|
123
|
+
? record.data
|
|
124
|
+
: null;
|
|
125
|
+
const run = data && data.run && typeof data.run === 'object' && !Array.isArray(data.run)
|
|
126
|
+
? data.run
|
|
127
|
+
: null;
|
|
128
|
+
const version = run?.state_version;
|
|
129
|
+
if (typeof version !== 'number' || !Number.isSafeInteger(version) || version < 0) {
|
|
130
|
+
throw new client_1.CliError('runs cancel could not read state_version from the run. Pass --state-version <n>.', 'invalid_response', 0);
|
|
131
|
+
}
|
|
132
|
+
return version;
|
|
133
|
+
}
|
|
109
134
|
function parsePacks(argv) {
|
|
110
135
|
const { values, positionals } = (0, node_util_1.parseArgs)({ args: argv, options: { goal: { type: 'string' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
111
136
|
if (values.help)
|
|
@@ -215,7 +240,7 @@ function parseTools(argv) {
|
|
|
215
240
|
'idempotency-key': { type: 'string' }, 'json-output': { type: 'boolean' }, open: { type: 'boolean' },
|
|
216
241
|
'dry-run': { type: 'boolean' }, live: { type: 'boolean' }, mode: { type: 'string' },
|
|
217
242
|
'agent-session-id': { type: 'string' }, approved: { type: 'boolean' }, 'approved-effect-unknown': { type: 'boolean' },
|
|
218
|
-
'max-credits': { type: 'string' }, 'turn-token': { type: 'string' }, help: { type: 'boolean', short: 'h' },
|
|
243
|
+
'max-credits': { type: 'string' }, 'turn-token': { type: 'string' }, 'wait-timeout': { type: 'string' }, 'no-wait': { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
219
244
|
}, allowPositionals: true, strict: true });
|
|
220
245
|
if (values.help)
|
|
221
246
|
return { command: 'help' };
|
|
@@ -268,20 +293,21 @@ function parseTools(argv) {
|
|
|
268
293
|
// run without one. What was missing was any way to ASK for live.
|
|
269
294
|
if (body.mode !== undefined)
|
|
270
295
|
requireToolsRunMode(body);
|
|
271
|
-
|
|
296
|
+
const waitTimeout = values['wait-timeout'] === undefined ? 120 : nonNegativeNumber(values['wait-timeout'], '--wait-timeout');
|
|
297
|
+
return { command: 'tools_run', capabilityId, body, waitTimeout, ...(values['no-wait'] ? { noWait: true } : {}), ...(values['turn-token'] ? { turnToken: values['turn-token'] } : {}), ...(values['idempotency-key'] ? { idempotencyKey: values['idempotency-key'] } : {}), ...(values.open ? { open: true } : {}), ...output };
|
|
272
298
|
}
|
|
273
299
|
throw new CliUsageError('Usage: dreamstate tools <search|get|run>');
|
|
274
300
|
}
|
|
275
301
|
function parseTables(argv) {
|
|
276
302
|
const { values, positionals } = (0, node_util_1.parseArgs)({ args: argv, options: {
|
|
277
303
|
'table-id': { type: 'string' }, file: { type: 'string' }, format: { type: 'string' }, background: { type: 'boolean' },
|
|
278
|
-
preview: { type: 'boolean' }, 'upsert-key': { type: 'string' },
|
|
304
|
+
preview: { type: 'boolean' }, 'upsert-key': { type: 'string' }, 'row-kind': { type: 'string' }, 'keep-unmapped-columns': { type: 'string' },
|
|
279
305
|
'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
280
306
|
}, allowPositionals: true, strict: true });
|
|
281
307
|
if (values.help)
|
|
282
308
|
return { command: 'help' };
|
|
283
309
|
if (positionals.length !== 1 || positionals[0] !== 'import' || !values['table-id'] || !values.file) {
|
|
284
|
-
throw new CliUsageError('Usage: dreamstate tables import --table-id <uuid> --file <path> [--format <csv|json|jsonl|xlsx>] [--preview] [--upsert-key <header>] [--background]');
|
|
310
|
+
throw new CliUsageError('Usage: dreamstate tables import --table-id <uuid> --file <path> [--format <csv|json|jsonl|xlsx>] [--preview] [--upsert-key <header>] [--row-kind <person|company>] [--keep-unmapped-columns <true|false>] [--background]');
|
|
285
311
|
}
|
|
286
312
|
// --format is an override, not a requirement. Requiring it made the caller restate
|
|
287
313
|
// what the file path already says, and an omitted flag produced a usage error rather
|
|
@@ -292,6 +318,14 @@ function parseTables(argv) {
|
|
|
292
318
|
throw new CliUsageError('--format must be one of csv, json, jsonl, xlsx');
|
|
293
319
|
}
|
|
294
320
|
const upsertKey = values['upsert-key']?.trim();
|
|
321
|
+
const rowKind = values['row-kind'];
|
|
322
|
+
if (rowKind !== undefined && rowKind !== 'person' && rowKind !== 'company') {
|
|
323
|
+
throw new CliUsageError('--row-kind must be person or company');
|
|
324
|
+
}
|
|
325
|
+
const keepUnmappedColumns = values['keep-unmapped-columns'];
|
|
326
|
+
if (keepUnmappedColumns !== undefined && keepUnmappedColumns !== 'true' && keepUnmappedColumns !== 'false') {
|
|
327
|
+
throw new CliUsageError('--keep-unmapped-columns must be true or false');
|
|
328
|
+
}
|
|
295
329
|
return {
|
|
296
330
|
command: 'tables_import',
|
|
297
331
|
tableId: values['table-id'],
|
|
@@ -299,17 +333,38 @@ function parseTables(argv) {
|
|
|
299
333
|
...(format ? { format: format } : {}),
|
|
300
334
|
...(values.preview ? { preview: true } : {}),
|
|
301
335
|
...(upsertKey ? { upsertKey } : {}),
|
|
336
|
+
...(rowKind ? { rowKind } : {}),
|
|
337
|
+
...(keepUnmappedColumns === 'false' ? { keepUnmappedColumns: false } : {}),
|
|
302
338
|
background: Boolean(values.background),
|
|
303
339
|
...outputOption(values),
|
|
304
340
|
};
|
|
305
341
|
}
|
|
342
|
+
function runsListLimit(raw) {
|
|
343
|
+
const value = nonNegativeInteger(raw, '--limit');
|
|
344
|
+
if (value < 1 || value > 500)
|
|
345
|
+
throw new CliUsageError('--limit must be an integer from 1 to 500');
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
306
348
|
function parseRuns(argv) {
|
|
307
|
-
const { values, positionals } = (0, node_util_1.parseArgs)({ args: argv, options: { json: { type: 'string' }, reason: { type: 'string' }, 'state-version': { type: 'string' }, 'idempotency-key': { type: 'string' }, open: { type: 'boolean' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
349
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: argv, options: { json: { type: 'string' }, limit: { type: 'string' }, cursor: { type: 'string' }, reason: { type: 'string' }, 'state-version': { type: 'string' }, 'idempotency-key': { type: 'string' }, open: { type: 'boolean' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
308
350
|
if (values.help)
|
|
309
351
|
return { command: 'help' };
|
|
310
352
|
const output = outputOption(values);
|
|
311
|
-
if (positionals.length === 1 && positionals[0] === 'list')
|
|
312
|
-
|
|
353
|
+
if (positionals.length === 1 && positionals[0] === 'list') {
|
|
354
|
+
const query = values.json ? parseJsonObject(values.json) : {};
|
|
355
|
+
if (values.limit !== undefined)
|
|
356
|
+
query.limit = runsListLimit(values.limit);
|
|
357
|
+
if (values.cursor !== undefined) {
|
|
358
|
+
const cursor = values.cursor.trim();
|
|
359
|
+
if (!cursor)
|
|
360
|
+
throw new CliUsageError('--cursor must be a non-empty string');
|
|
361
|
+
query.cursor = cursor;
|
|
362
|
+
}
|
|
363
|
+
return { command: 'runs_list', query, ...output };
|
|
364
|
+
}
|
|
365
|
+
if (values.limit !== undefined || values.cursor !== undefined) {
|
|
366
|
+
throw new CliUsageError('--limit and --cursor are only valid on runs list');
|
|
367
|
+
}
|
|
313
368
|
const runId = requireOne(positionals, `dreamstate runs ${positionals[0] ?? '<get|events|cancel>'} <run_id>`);
|
|
314
369
|
if (positionals[0] === 'get')
|
|
315
370
|
return { command: 'runs_get', runId, ...output };
|
|
@@ -323,9 +378,20 @@ function parseRuns(argv) {
|
|
|
323
378
|
return { command: 'runs_resume', runId, stateVersion: nonNegativeInteger(values['state-version'], '--state-version'), ...(values.reason ? { reason: values.reason } : {}), ...(values['idempotency-key'] ? { idempotencyKey: values['idempotency-key'] } : {}), ...(values.open ? { open: true } : {}), ...output };
|
|
324
379
|
}
|
|
325
380
|
if (positionals[0] === 'cancel') {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
381
|
+
const idempotency = values['idempotency-key'] ? { idempotencyKey: values['idempotency-key'] } : {};
|
|
382
|
+
if (values.json && (values['state-version'] !== undefined || values.reason)) {
|
|
383
|
+
throw new CliUsageError('Use either --json <body> or --state-version and --reason with runs cancel, not both.');
|
|
384
|
+
}
|
|
385
|
+
if (values.json)
|
|
386
|
+
return { command: 'runs_cancel', runId, body: parseJsonObject(values.json), ...idempotency, ...output };
|
|
387
|
+
return {
|
|
388
|
+
command: 'runs_cancel',
|
|
389
|
+
runId,
|
|
390
|
+
...(values['state-version'] !== undefined ? { stateVersion: nonNegativeInteger(values['state-version'], '--state-version') } : {}),
|
|
391
|
+
...(values.reason ? { reason: values.reason } : {}),
|
|
392
|
+
...idempotency,
|
|
393
|
+
...output,
|
|
394
|
+
};
|
|
329
395
|
}
|
|
330
396
|
throw new CliUsageError('Usage: dreamstate runs <get|events|cancel> <run_id>');
|
|
331
397
|
}
|
|
@@ -342,14 +408,26 @@ function parseCliArgs(argv) {
|
|
|
342
408
|
return { command: 'version' };
|
|
343
409
|
const [group, ...rest] = argv;
|
|
344
410
|
if (group === 'login') {
|
|
345
|
-
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { token: { type: 'string' }, 'api-key-file': { type: 'string' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
411
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { token: { type: 'string' }, profile: { type: 'string' }, 'api-key-file': { type: 'string' }, 'no-skills': { type: 'boolean' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
346
412
|
if (values.help)
|
|
347
413
|
return { command: 'help' };
|
|
348
414
|
if (positionals.length)
|
|
349
415
|
throw new CliUsageError('Usage: dreamstate login [--token <ds_user_...|ds_live_...>]');
|
|
350
416
|
if (values.token !== undefined && values['api-key-file'])
|
|
351
417
|
throw new CliUsageError('Use either --token or --api-key-file, not both.');
|
|
352
|
-
return { command: 'login', ...(values.token !== undefined ? { token: values.token } : {}), ...(values['api-key-file'] ? { apiKeyFile: values['api-key-file'] } : {}), ...outputOption(values) };
|
|
418
|
+
return { command: 'login', ...(values.token !== undefined ? { token: values.token } : {}), ...(values['api-key-file'] ? { apiKeyFile: values['api-key-file'] } : {}), ...(values.profile ? { profile: (0, config_1.validateProfileName)(values.profile) } : {}), ...(values['no-skills'] ? { noSkills: true } : {}), ...outputOption(values) };
|
|
419
|
+
}
|
|
420
|
+
if (group === 'install') {
|
|
421
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: spaceSeparatedAgents(rest), options: { agents: { type: 'string', multiple: true }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
422
|
+
if (values.help)
|
|
423
|
+
return { command: 'help' };
|
|
424
|
+
if (positionals.length)
|
|
425
|
+
throw new CliUsageError('Usage: dreamstate install [--agents <claude codex grok>]');
|
|
426
|
+
const requested = values.agents ? [...new Set(values.agents.flatMap((raw) => csv(raw, '--agents')))] : [...pluginInstaller_1.PLUGIN_AGENTS];
|
|
427
|
+
const invalid = requested.filter((agent) => !pluginInstaller_1.PLUGIN_AGENTS.includes(agent));
|
|
428
|
+
if (invalid.length)
|
|
429
|
+
throw new CliUsageError(`--agents values must be one of: ${pluginInstaller_1.PLUGIN_AGENTS.join(', ')} (got: ${invalid.join(', ')})`);
|
|
430
|
+
return { command: 'install', agents: requested, ...outputOption(values) };
|
|
353
431
|
}
|
|
354
432
|
if (group === 'logout') {
|
|
355
433
|
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
@@ -359,6 +437,27 @@ function parseCliArgs(argv) {
|
|
|
359
437
|
throw new CliUsageError('Usage: dreamstate logout');
|
|
360
438
|
return { command: 'logout', ...outputOption(values) };
|
|
361
439
|
}
|
|
440
|
+
if (group === 'status') {
|
|
441
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'json-output': { type: 'boolean' } }, allowPositionals: true, strict: true });
|
|
442
|
+
if (!positionals.length)
|
|
443
|
+
return { command: 'status', ...outputOption(values) };
|
|
444
|
+
throw new CliUsageError('Usage: dreamstate status [--json-output]');
|
|
445
|
+
}
|
|
446
|
+
if (group === 'profiles') {
|
|
447
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'json-output': { type: 'boolean' } }, allowPositionals: true, strict: true });
|
|
448
|
+
const output = outputOption(values);
|
|
449
|
+
if (positionals.length === 1 && positionals[0] === 'list')
|
|
450
|
+
return { command: 'profiles_list', ...output };
|
|
451
|
+
if (positionals.length === 1 && positionals[0] === 'current')
|
|
452
|
+
return { command: 'profiles_current', ...output };
|
|
453
|
+
if (positionals.length === 2 && positionals[0] === 'use')
|
|
454
|
+
return { command: 'profiles_use', name: (0, config_1.validateProfileName)(positionals[1]), ...output };
|
|
455
|
+
if (positionals.length === 2 && positionals[0] === 'remove')
|
|
456
|
+
return { command: 'profiles_remove', name: (0, config_1.validateProfileName)(positionals[1]), ...output };
|
|
457
|
+
if (positionals.length === 2 && positionals[0] === 'env')
|
|
458
|
+
return { command: 'profiles_env', name: (0, config_1.validateProfileName)(positionals[1]), ...output };
|
|
459
|
+
throw new CliUsageError('Usage: dreamstate profiles <list|use <name>|current|remove <name>|env <name>>');
|
|
460
|
+
}
|
|
362
461
|
if (group === 'whoami') {
|
|
363
462
|
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'json-output': { type: 'boolean' } }, allowPositionals: true, strict: true });
|
|
364
463
|
if (positionals.length === 0)
|
|
@@ -376,9 +475,9 @@ function parseCliArgs(argv) {
|
|
|
376
475
|
throw new CliUsageError('Usage: dreamstate workspaces <list|use <id|slug>>');
|
|
377
476
|
}
|
|
378
477
|
if (group === 'auth') {
|
|
379
|
-
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'api-key-file': { type: 'string' }, 'json-output': { type: 'boolean' } }, allowPositionals: true, strict: true });
|
|
478
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'api-key-file': { type: 'string' }, 'no-skills': { type: 'boolean' }, 'json-output': { type: 'boolean' } }, allowPositionals: true, strict: true });
|
|
380
479
|
if (positionals.length === 1 && positionals[0] === 'login')
|
|
381
|
-
return { command: 'login', ...(values['api-key-file'] ? { apiKeyFile: values['api-key-file'] } : {}), ...outputOption(values) };
|
|
480
|
+
return { command: 'login', ...(values['api-key-file'] ? { apiKeyFile: values['api-key-file'] } : {}), ...(values['no-skills'] ? { noSkills: true } : {}), ...outputOption(values) };
|
|
382
481
|
if (positionals.length === 1 && positionals[0] === 'whoami')
|
|
383
482
|
return { command: 'whoami', ...outputOption(values) };
|
|
384
483
|
throw new CliUsageError('Usage: dreamstate auth <login|whoami>');
|
|
@@ -438,6 +537,76 @@ function toolTurnToken(value) {
|
|
|
438
537
|
return data.tool_turn_token;
|
|
439
538
|
throw new client_1.CliError('tools get did not return a tool_turn_token.', 'invalid_tool_turn_token', 0);
|
|
440
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Free sync reads. The server runs these on the inline read path when
|
|
542
|
+
* the body has no tool_turn_token and the request has no Idempotency-Key
|
|
543
|
+
* (isInlineReadEligible). A tools get before the run is a second HTTP the
|
|
544
|
+
* read does not need. Measured 2026-10-04: tables.workbooks.get full live CLI
|
|
545
|
+
* 0.726s against whoami 0.455s, about 0.27s of tools get per harness read-back.
|
|
546
|
+
* knowledge.brain.context.get is the same class (catalog: credit_class free,
|
|
547
|
+
* async_policy.mode sync, mutates not set). Live 2026-10-05 a standalone
|
|
548
|
+
* tools get of that id was 0.400s including node startup, next to whoami
|
|
549
|
+
* 0.422s, so the same-process skip is one HTTP, not a second process.
|
|
550
|
+
* The set name is historical. Writes are not in this set: they stay fetch-first.
|
|
551
|
+
*/
|
|
552
|
+
const INLINE_READ_CAPABILITIES = new Set([
|
|
553
|
+
'tables.workbooks.get',
|
|
554
|
+
'tables.workbooks.overview',
|
|
555
|
+
'tables.workbooks.list',
|
|
556
|
+
'tables.columns.list',
|
|
557
|
+
'tables.columns.stats',
|
|
558
|
+
'tables.rows.query',
|
|
559
|
+
'tables.rows.get',
|
|
560
|
+
'tables.rows.count',
|
|
561
|
+
'knowledge.brain.context.get',
|
|
562
|
+
// Both are stored, free, synchronous inbox reads. Keeping either behind a
|
|
563
|
+
// tools_get turns one list or thread open into two HTTP requests, while the
|
|
564
|
+
// canonical inline-read gate already validates their read-only contract.
|
|
565
|
+
'unibox.inbox.unified_list',
|
|
566
|
+
'unibox.inbox.conversation_get',
|
|
567
|
+
]);
|
|
568
|
+
function omitsToolTurnForInlineTableRead(capabilityId) {
|
|
569
|
+
return INLINE_READ_CAPABILITIES.has(capabilityId);
|
|
570
|
+
}
|
|
571
|
+
function readIdempotencyContract(fetched) {
|
|
572
|
+
if (!fetched || typeof fetched !== 'object' || Array.isArray(fetched))
|
|
573
|
+
return null;
|
|
574
|
+
const root = fetched;
|
|
575
|
+
const data = root.data && typeof root.data === 'object' && !Array.isArray(root.data)
|
|
576
|
+
? root.data
|
|
577
|
+
: null;
|
|
578
|
+
const candidates = [data?.capability, root.capability, data, root];
|
|
579
|
+
for (const candidate of candidates) {
|
|
580
|
+
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate))
|
|
581
|
+
continue;
|
|
582
|
+
if ('mutates' in candidate || 'idempotency_required' in candidate) {
|
|
583
|
+
return candidate;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Header value, or null when the request must not carry Idempotency-Key.
|
|
590
|
+
* Any key forces a read onto the durable run path (isInlineReadEligible).
|
|
591
|
+
* Send one only for an explicit key, or when the tools get contract says
|
|
592
|
+
* mutates or idempotency_required. Missing flags omit the key: a read whose
|
|
593
|
+
* contract does not set mutates must not be treated as a write.
|
|
594
|
+
*/
|
|
595
|
+
function capabilityIdempotencyKey(fetched, explicit) {
|
|
596
|
+
if (typeof explicit === 'string' && explicit.length > 0)
|
|
597
|
+
return explicit;
|
|
598
|
+
const contract = readIdempotencyContract(fetched);
|
|
599
|
+
if (contract?.mutates === true || contract?.idempotency_required === true)
|
|
600
|
+
return (0, node_crypto_1.randomUUID)();
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
function parameterIdempotencyKey(body) {
|
|
604
|
+
const params = body.params;
|
|
605
|
+
if (!params || typeof params !== 'object' || Array.isArray(params))
|
|
606
|
+
return undefined;
|
|
607
|
+
const key = params.idempotency_key;
|
|
608
|
+
return typeof key === 'string' && key.length > 0 ? key : undefined;
|
|
609
|
+
}
|
|
441
610
|
function code(error) {
|
|
442
611
|
return error && typeof error === 'object' && typeof error.code === 'string'
|
|
443
612
|
? error.code
|
|
@@ -617,14 +786,97 @@ function whoamiData(value) {
|
|
|
617
786
|
const data = value && typeof value === 'object' ? value.data : undefined;
|
|
618
787
|
return data && typeof data === 'object' ? data : {};
|
|
619
788
|
}
|
|
789
|
+
function statusData(whoami, health) {
|
|
790
|
+
const meta = whoami && typeof whoami === 'object' ? whoami.meta : null;
|
|
791
|
+
const serverVersion = meta && typeof meta === 'object' ? meta.server_version : null;
|
|
792
|
+
const minimumCliVersion = meta && typeof meta === 'object' ? meta.minimum_cli_version : null;
|
|
793
|
+
const healthData = health && typeof health === 'object' ? health.data : null;
|
|
794
|
+
const healthVersion = healthData && typeof healthData === 'object' ? healthData.version : null;
|
|
795
|
+
const version = typeof serverVersion === 'string' ? serverVersion : typeof healthVersion === 'string' ? healthVersion : null;
|
|
796
|
+
const minimum = typeof minimumCliVersion === 'string' ? minimumCliVersion : null;
|
|
797
|
+
const parts = (value) => value.replace(/^v/, '').split('-')[0].split('.').map((part) => Number(part));
|
|
798
|
+
const [minimumMajor = 0, minimumMinor = 0, minimumPatch = 0] = minimum ? parts(minimum) : [];
|
|
799
|
+
const [cliMajor = 0, cliMinor = 0, cliPatch = 0] = parts(client_1.CLI_VERSION);
|
|
800
|
+
const compatible = !minimum || minimumMajor < cliMajor || (minimumMajor === cliMajor && (minimumMinor < cliMinor || (minimumMinor === cliMinor && minimumPatch <= cliPatch)));
|
|
801
|
+
return {
|
|
802
|
+
cli_version: client_1.CLI_VERSION,
|
|
803
|
+
api_version: version,
|
|
804
|
+
minimum_cli_version: minimum,
|
|
805
|
+
minimum_cli_version_published: minimum !== null,
|
|
806
|
+
compatible,
|
|
807
|
+
upgrade_hint: compatible ? null : 'Run: npm install -g @trydreamstate/cli@latest',
|
|
808
|
+
health,
|
|
809
|
+
whoami: whoamiData(whoami),
|
|
810
|
+
};
|
|
811
|
+
}
|
|
620
812
|
function activeWorkspace(data) {
|
|
621
|
-
const activeId = data.workspace_id
|
|
813
|
+
const activeId = data.workspace_id;
|
|
622
814
|
return activeId ? data.workspaces?.find((workspace) => workspace.id === activeId) : undefined;
|
|
623
815
|
}
|
|
624
816
|
function wait(milliseconds) {
|
|
625
817
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
626
818
|
}
|
|
627
|
-
async function
|
|
819
|
+
async function installHostedSkills(client, agents, runtime, options = {}) {
|
|
820
|
+
const listed = await client.listAllSkills();
|
|
821
|
+
const entries = listed && typeof listed === 'object' && listed.data?.skills;
|
|
822
|
+
const ids = Array.isArray(entries) ? entries.map((skill) => skill && typeof skill === 'object' ? skill.id : undefined).filter((id) => typeof id === 'string') : [];
|
|
823
|
+
if (!ids.length)
|
|
824
|
+
throw new client_1.CliError('Dreamstate API returned no installable skills.', 'invalid_response', 200);
|
|
825
|
+
if (ids.some((id) => !/^[a-z0-9][a-z0-9_-]*$/i.test(id)))
|
|
826
|
+
throw new CliUsageError('Dreamstate API returned an invalid skill id.');
|
|
827
|
+
const skills = [];
|
|
828
|
+
for (const id of ids) {
|
|
829
|
+
const skill = hostedSkill(await client.getSkill(id));
|
|
830
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(skill.id))
|
|
831
|
+
throw new CliUsageError('Dreamstate API returned an invalid skill id.');
|
|
832
|
+
if (skill.id !== id)
|
|
833
|
+
throw new client_1.CliError('Dreamstate API returned a mismatched skill id.', 'invalid_response', 200);
|
|
834
|
+
skills.push(skill);
|
|
835
|
+
}
|
|
836
|
+
const packs = await client.listPacks();
|
|
837
|
+
const whoami = await client.getWhoami();
|
|
838
|
+
const workspaceId = typeof whoami?.data?.workspace_id === 'string' ? whoami.data.workspace_id : 'unknown';
|
|
839
|
+
const indexVersion = (0, node_crypto_1.createHash)('sha256')
|
|
840
|
+
.update(JSON.stringify(skills.map((skill) => [skill.id, skill.description, skill.body, skill.frontmatter ?? {}])))
|
|
841
|
+
.digest('hex').slice(0, 16);
|
|
842
|
+
const installed = await (0, skillInstaller_1.installSkills)([...skills, (0, skillInstaller_1.routerSkill)(packs)], {
|
|
843
|
+
agents,
|
|
844
|
+
homeDirectory: runtime.homeDirectory,
|
|
845
|
+
project: Boolean(options.project),
|
|
846
|
+
copy: Boolean(options.copy) || process.platform === 'win32',
|
|
847
|
+
workspaceId,
|
|
848
|
+
indexVersion,
|
|
849
|
+
});
|
|
850
|
+
return { files: installed.files, skillIds: [...ids, 'router'] };
|
|
851
|
+
}
|
|
852
|
+
async function installLoginSkills(client, runtime, skip, stderr) {
|
|
853
|
+
if (skip)
|
|
854
|
+
return { skipped: 'disabled' };
|
|
855
|
+
const withPlugin = await (0, pluginInstaller_1.agentsWithDreamstatePlugin)(runtime.homeDirectory);
|
|
856
|
+
const agents = (await (0, skillInstaller_1.detectInstalledSkillAgents)(runtime.homeDirectory)).filter((agent) => !withPlugin.includes(agent));
|
|
857
|
+
if (!agents.length && withPlugin.length)
|
|
858
|
+
return { skipped: 'plugin_installed' };
|
|
859
|
+
if (!agents.length) {
|
|
860
|
+
stderr.write('Run dreamstate skills install to install Dreamstate skills.\n');
|
|
861
|
+
return { skipped: 'no_agents_detected' };
|
|
862
|
+
}
|
|
863
|
+
try {
|
|
864
|
+
const installed = await installHostedSkills(client, agents, runtime);
|
|
865
|
+
stderr.write(`Installed Dreamstate skills into: ${agents.join(', ')}.\n`);
|
|
866
|
+
return { installed: agents, files: installed.files.length };
|
|
867
|
+
}
|
|
868
|
+
catch {
|
|
869
|
+
stderr.write('Run dreamstate skills install to install Dreamstate skills.\n');
|
|
870
|
+
return { skipped: 'installation_failed' };
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
function loginResult(value, skills) {
|
|
874
|
+
if (!value || typeof value !== 'object')
|
|
875
|
+
return value;
|
|
876
|
+
const envelope = value;
|
|
877
|
+
return { ...envelope, data: { ...(envelope.data && typeof envelope.data === 'object' ? envelope.data : {}), skills } };
|
|
878
|
+
}
|
|
879
|
+
async function loginWithDeviceAuthorization(runtime, stdout, stderr, profile, noSkills = false, jsonOutput = false) {
|
|
628
880
|
const environment = runtime.env ?? process.env;
|
|
629
881
|
const baseUrl = (0, config_1.resolveApiBaseUrl)(environment);
|
|
630
882
|
const state = runtime.deviceState ?? deviceState();
|
|
@@ -637,14 +889,19 @@ async function loginWithDeviceAuthorization(runtime, stdout, stderr) {
|
|
|
637
889
|
for (;;) {
|
|
638
890
|
const claim = await (0, client_1.claimDeviceAuthorization)(baseUrl, authorization.device_code);
|
|
639
891
|
if (claim.status === 'ready') {
|
|
640
|
-
|
|
892
|
+
if (profile)
|
|
893
|
+
await (0, config_1.storeCredentialProfile)(profile, claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
|
|
894
|
+
else
|
|
895
|
+
await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
|
|
641
896
|
const client = new client_1.DreamstateClient({
|
|
642
897
|
baseUrl,
|
|
643
898
|
apiKey: claim.token,
|
|
644
899
|
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
645
900
|
onVersionWarning: (message) => (runtime.stderr ?? process.stderr).write(`${message}\n`),
|
|
646
901
|
});
|
|
647
|
-
|
|
902
|
+
const whoami = await client.getWhoami();
|
|
903
|
+
const skills = await installLoginSkills(client, runtime, noSkills, stderr);
|
|
904
|
+
print(loginResult(whoami, skills), stdout, jsonOutput);
|
|
648
905
|
return;
|
|
649
906
|
}
|
|
650
907
|
await (runtime.sleep ?? wait)(authorization.interval_seconds * 1_000);
|
|
@@ -670,8 +927,9 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
670
927
|
try {
|
|
671
928
|
const extracted = extractWorkspaceFlag(argv);
|
|
672
929
|
const command = parseCliArgs(extracted.argv);
|
|
673
|
-
if (command.command === 'help') {
|
|
674
|
-
|
|
930
|
+
if (command.command === 'help' || command.command === 'pack_help') {
|
|
931
|
+
const packHelp = command.command === 'pack_help' ? command : undefined;
|
|
932
|
+
print({ ok: true, data: { usage: packHelp?.usage ?? USAGE }, meta: {} }, stdout, Boolean(packHelp?.jsonOutput));
|
|
675
933
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
676
934
|
return;
|
|
677
935
|
}
|
|
@@ -680,6 +938,19 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
680
938
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
681
939
|
return;
|
|
682
940
|
}
|
|
941
|
+
if (command.command === 'install') {
|
|
942
|
+
const installed = await (0, pluginInstaller_1.installAgentPlugin)({ agents: command.agents, bundleDirectory: (0, node_path_1.join)(__dirname, '..', 'agent-plugin'), homeDirectory: runtime.homeDirectory, run: runtime.runAgentCommand });
|
|
943
|
+
const done = installed.agents.filter((item) => item.status === 'installed').map((item) => item.agent);
|
|
944
|
+
const failed = installed.agents.filter((item) => item.status === 'failed');
|
|
945
|
+
if (failed.length || !done.length) {
|
|
946
|
+
throw new client_1.CliError(done.length ? 'Dreamstate plugin install failed for some agents.' : 'No supported agent was found. Install Claude Code, Codex or Grok Build, then rerun dreamstate install.', 'plugin_install_failed', 0, { details: installed });
|
|
947
|
+
}
|
|
948
|
+
const removedSkills = done.includes('claude') ? await (0, pluginInstaller_1.removeClaudeSkillCopies)(runtime.homeDirectory) : [];
|
|
949
|
+
stderr.write(`Dreamstate plugin installed for ${done.join(', ')}. Start a new session; the first Dreamstate tool call asks you to sign in.\n`);
|
|
950
|
+
print({ ok: true, data: { ...installed, removed_skill_copies: removedSkills }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
951
|
+
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
683
954
|
if (command.command === 'logout') {
|
|
684
955
|
const path = runtimeCredentialsPath(runtime);
|
|
685
956
|
let removed = true;
|
|
@@ -699,6 +970,38 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
699
970
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
700
971
|
return;
|
|
701
972
|
}
|
|
973
|
+
if (command.command === 'profiles_list' || command.command === 'profiles_current' || command.command === 'profiles_use' || command.command === 'profiles_remove' || command.command === 'profiles_env') {
|
|
974
|
+
const path = runtimeCredentialsPath(runtime);
|
|
975
|
+
const environment = runtime.env ?? process.env;
|
|
976
|
+
if (command.command === 'profiles_list') {
|
|
977
|
+
const current = await (0, config_1.currentCredentialProfile)(path, environment);
|
|
978
|
+
const profiles = await (0, config_1.listCredentialProfiles)(path);
|
|
979
|
+
print({ ok: true, data: { current_profile: current, profiles: profiles.map(({ name, workspace }) => ({ name, workspace, active: name === current })) }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
980
|
+
}
|
|
981
|
+
else if (command.command === 'profiles_current') {
|
|
982
|
+
print({ ok: true, data: { profile: await (0, config_1.currentCredentialProfile)(path, environment) }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
983
|
+
}
|
|
984
|
+
else if (command.command === 'profiles_use') {
|
|
985
|
+
await (0, config_1.useCredentialProfile)(command.name, path);
|
|
986
|
+
print({ ok: true, data: { profile: command.name }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
987
|
+
}
|
|
988
|
+
else if (command.command === 'profiles_remove') {
|
|
989
|
+
const removed = await (0, config_1.removeCredentialProfile)(command.name, path);
|
|
990
|
+
print({ ok: true, data: { profile: command.name, removed }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
const profiles = await (0, config_1.listCredentialProfiles)(path);
|
|
994
|
+
if (!profiles.some((profile) => profile.name === command.name))
|
|
995
|
+
throw new CliUsageError(`Profile not found: ${command.name}.`);
|
|
996
|
+
const result = { ok: true, data: { profile: command.name, exports: [`DREAMSTATE_PROFILE=${command.name}`] }, meta: {} };
|
|
997
|
+
if (command.jsonOutput)
|
|
998
|
+
print(result, stdout, true);
|
|
999
|
+
else
|
|
1000
|
+
stdout.write(`export DREAMSTATE_PROFILE=${command.name}\n`);
|
|
1001
|
+
}
|
|
1002
|
+
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
702
1005
|
if (command.command === 'login' && command.token !== undefined) {
|
|
703
1006
|
const token = command.token.trim();
|
|
704
1007
|
if (!(0, config_1.isDreamstateToken)(token))
|
|
@@ -715,8 +1018,23 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
715
1018
|
onVersionWarning: (message) => stderr.write(`${message}\n`),
|
|
716
1019
|
});
|
|
717
1020
|
const whoami = await client.getWhoami();
|
|
718
|
-
|
|
719
|
-
|
|
1021
|
+
if (command.profile)
|
|
1022
|
+
await (0, config_1.storeCredentialProfile)(command.profile, token, runtimeCredentialsPath(runtime));
|
|
1023
|
+
else
|
|
1024
|
+
await (0, config_1.storeApiKey)(token, runtimeCredentialsPath(runtime));
|
|
1025
|
+
const skills = await installLoginSkills(client, runtime, Boolean(command.noSkills), stderr);
|
|
1026
|
+
print(loginResult(whoami, skills), stdout, Boolean(command.jsonOutput));
|
|
1027
|
+
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (command.command === 'status') {
|
|
1031
|
+
const environment = runtime.env ?? process.env;
|
|
1032
|
+
const credentialFilePath = runtimeCredentialsPath(runtime);
|
|
1033
|
+
const apiKey = await (0, config_1.resolveApiKey)(environment, credentialFilePath);
|
|
1034
|
+
const workspace = await (0, config_1.resolveWorkspace)(extracted.workspace, environment, credentialFilePath);
|
|
1035
|
+
const client = new client_1.DreamstateClient({ baseUrl: (0, config_1.resolveApiBaseUrl)(environment), apiKey, ...(workspace ? { workspace } : {}), requestTimeoutMs: runtime.requestTimeoutMs });
|
|
1036
|
+
const [whoami, health] = await Promise.all([client.getStatusWhoami(), client.getHealth()]);
|
|
1037
|
+
print({ ok: true, data: statusData(whoami, health), meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
720
1038
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
721
1039
|
return;
|
|
722
1040
|
}
|
|
@@ -725,14 +1043,16 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
725
1043
|
const key = await readLoginKey(command, runtime);
|
|
726
1044
|
if (!(0, config_1.isDreamstateToken)(key))
|
|
727
1045
|
throw new CliUsageError('Dreamstate API keys must start with ds_user_ or ds_live_.');
|
|
728
|
-
const path =
|
|
729
|
-
|
|
730
|
-
|
|
1046
|
+
const path = command.profile
|
|
1047
|
+
? await (0, config_1.storeCredentialProfile)(command.profile, key, runtimeCredentialsPath(runtime))
|
|
1048
|
+
: await (0, config_1.storeApiKey)(key, runtimeCredentialsPath(runtime));
|
|
1049
|
+
const client = new client_1.DreamstateClient({ baseUrl: (0, config_1.resolveApiBaseUrl)(runtime.env ?? process.env), apiKey: key, requestTimeoutMs: runtime.requestTimeoutMs, onVersionWarning: (message) => stderr.write(`${message}\n`) });
|
|
1050
|
+
const skills = await installLoginSkills(client, runtime, Boolean(command.noSkills), stderr);
|
|
1051
|
+
print({ ok: true, data: { credentials_path: path, skills }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
731
1052
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
732
1053
|
return;
|
|
733
1054
|
}
|
|
734
|
-
await loginWithDeviceAuthorization(runtime, stdout, stderr);
|
|
735
|
-
stderr.write('Run dreamstate skills install to install Dreamstate skills.\n');
|
|
1055
|
+
await loginWithDeviceAuthorization(runtime, stdout, stderr, command.profile, Boolean(command.noSkills), Boolean(command.jsonOutput));
|
|
736
1056
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
737
1057
|
return;
|
|
738
1058
|
}
|
|
@@ -824,7 +1144,7 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
824
1144
|
result = await client.getWhoami();
|
|
825
1145
|
if (!command.jsonOutput) {
|
|
826
1146
|
const data = whoamiData(result);
|
|
827
|
-
const activeId = data.workspace_id ??
|
|
1147
|
+
const activeId = data.workspace_id ?? null;
|
|
828
1148
|
const rows = data.workspaces ?? [];
|
|
829
1149
|
stdout.write(rows.length
|
|
830
1150
|
? `${rows.map((row) => `${row.id === activeId ? '*' : ' '} ${row.slug ?? '-'} ${row.name} ${row.role ?? '-'} ${row.id}`).join('\n')}\n`
|
|
@@ -841,10 +1161,10 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
841
1161
|
const match = data.workspaces?.find((row) => row.id.toLowerCase() === wanted || row.slug?.toLowerCase() === wanted);
|
|
842
1162
|
if (!match)
|
|
843
1163
|
throw new CliUsageError(`You are not a member of workspace "${command.workspace}". Run dreamstate workspaces list to see the workspaces you can use.`);
|
|
844
|
-
const stored = await (0, config_1.readStoredCredentials)(credentialFilePath);
|
|
1164
|
+
const stored = await (0, config_1.readStoredCredentials)(await (0, config_1.selectedCredentialsPath)(credentialFilePath, environment));
|
|
845
1165
|
if (!stored)
|
|
846
1166
|
throw new CliUsageError('workspaces use saves the default next to a stored login. Run dreamstate login or dreamstate login --token first.');
|
|
847
|
-
await (0, config_1.storeApiKey)(stored.token, credentialFilePath, match.id);
|
|
1167
|
+
await (0, config_1.storeApiKey)(stored.token, await (0, config_1.selectedCredentialsPath)(credentialFilePath, environment), match.id);
|
|
848
1168
|
stderr.write(`Default workspace: ${match.name} (${match.slug})\n`);
|
|
849
1169
|
result = { ok: true, data: { workspace: { id: match.id, name: match.name, slug: match.slug }, credentials_path: credentialFilePath }, meta: {} };
|
|
850
1170
|
break;
|
|
@@ -868,18 +1188,17 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
868
1188
|
result = await client.searchSkillDocuments(command.query);
|
|
869
1189
|
break;
|
|
870
1190
|
case 'skills_install': {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
throw new client_1.CliError('Dreamstate API returned no installable skills.', 'invalid_response', 200);
|
|
1191
|
+
if (command.skillId === '*') {
|
|
1192
|
+
const installed = await installHostedSkills(client, command.agents, runtime, { project: command.project, copy: command.copy });
|
|
1193
|
+
result = { ok: true, data: { installed_files: installed.files, skill_ids: installed.skillIds, agents: command.agents, ...(command.project ? { lock_file: 'skills-lock.json' } : {}) }, meta: {} };
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
const ids = [command.skillId];
|
|
878
1197
|
// A remote id becomes a directory name; refuse traversal before any request or write.
|
|
879
1198
|
if (ids.some((id) => !/^[a-z0-9][a-z0-9_-]*$/i.test(id)))
|
|
880
1199
|
throw new CliUsageError('Dreamstate API returned an invalid skill id.');
|
|
881
|
-
//
|
|
882
|
-
//
|
|
1200
|
+
// Read each skill one at a time because the workspace's private vault
|
|
1201
|
+
// admits two reads at once, and fifteen parallel reads failed in production.
|
|
883
1202
|
const skills = [];
|
|
884
1203
|
for (const id of ids) {
|
|
885
1204
|
const skill = hostedSkill(await client.getSkill(id));
|
|
@@ -937,54 +1256,83 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
937
1256
|
const pending = command.turnToken
|
|
938
1257
|
? null
|
|
939
1258
|
: await (0, turnCache_1.reusableTurnToken)(turns, baseUrl, turnKey, command.capabilityId, Date.now(), warnTurnCache);
|
|
940
|
-
const
|
|
941
|
-
const
|
|
1259
|
+
const inlineRead = !command.turnToken && !pending && !command.idempotencyKey && omitsToolTurnForInlineTableRead(command.capabilityId);
|
|
1260
|
+
const fetchedForRun = command.turnToken || pending || inlineRead ? null : await client.getTool(command.capabilityId);
|
|
1261
|
+
let turnToken = command.turnToken ?? pending ?? (inlineRead ? undefined : toolTurnToken(fetchedForRun));
|
|
1262
|
+
const callerParameterKey = parameterIdempotencyKey(command.body);
|
|
1263
|
+
let idempotencyKey = capabilityIdempotencyKey(fetchedForRun, callerParameterKey ?? command.idempotencyKey);
|
|
1264
|
+
let recoveredInlineRead = false;
|
|
1265
|
+
const run = () => client.runCapability(command.capabilityId, {
|
|
1266
|
+
...command.body,
|
|
1267
|
+
...(turnToken ? { tool_turn_token: turnToken } : {}),
|
|
1268
|
+
}, idempotencyKey);
|
|
942
1269
|
try {
|
|
943
1270
|
try {
|
|
944
1271
|
result = await run();
|
|
945
1272
|
}
|
|
946
1273
|
catch (error) {
|
|
947
|
-
//
|
|
948
|
-
//
|
|
949
|
-
//
|
|
950
|
-
if (
|
|
1274
|
+
// The inline skip is only valid while the server still treats this
|
|
1275
|
+
// capability as a free sync read. capability_not_fetched means the
|
|
1276
|
+
// fetch-first fence is on, so retry once on the old get-then-run path.
|
|
1277
|
+
if (inlineRead && !recoveredInlineRead && code(error) === 'capability_not_fetched') {
|
|
1278
|
+
recoveredInlineRead = true;
|
|
1279
|
+
const fetched = await client.getTool(command.capabilityId);
|
|
1280
|
+
turnToken = toolTurnToken(fetched);
|
|
1281
|
+
idempotencyKey = capabilityIdempotencyKey(fetched, callerParameterKey ?? command.idempotencyKey);
|
|
1282
|
+
result = await run();
|
|
1283
|
+
}
|
|
1284
|
+
else if (command.body.mode !== 'live' || code(error) !== 'quote_required') {
|
|
1285
|
+
// A quote_required refusal binds a fresh quote to this turn, so the
|
|
1286
|
+
// identical call admits on the next try. Retry once here instead of
|
|
1287
|
+
// making the caller rerun the same command by hand.
|
|
951
1288
|
throw error;
|
|
952
|
-
|
|
1289
|
+
}
|
|
1290
|
+
else {
|
|
1291
|
+
result = await run();
|
|
1292
|
+
}
|
|
953
1293
|
}
|
|
954
1294
|
}
|
|
955
1295
|
catch (error) {
|
|
956
|
-
if (!command.turnToken && code(error) === 'quote_required') {
|
|
1296
|
+
if (!command.turnToken && turnToken && code(error) === 'quote_required') {
|
|
957
1297
|
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl, turnKey, command.capabilityId, turnToken, Date.now(), warnTurnCache);
|
|
958
1298
|
}
|
|
959
1299
|
throw error;
|
|
960
1300
|
}
|
|
961
1301
|
if (!command.turnToken) {
|
|
962
|
-
if (command.body.mode === 'dry_run')
|
|
1302
|
+
if (command.body.mode === 'dry_run' && turnToken)
|
|
963
1303
|
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl, turnKey, command.capabilityId, turnToken, Date.now(), warnTurnCache);
|
|
964
1304
|
else if (pending)
|
|
965
1305
|
await (0, turnCache_1.forgetTurnToken)(turns, baseUrl, turnKey, command.capabilityId, warnTurnCache);
|
|
966
1306
|
}
|
|
1307
|
+
if (!command.noWait && !(0, runWait_1.columnRunReturnsRunId)(command.capabilityId)) {
|
|
1308
|
+
const waited = await (0, runWait_1.waitForRun)(client, result, command.waitTimeout, runtime.sleep ?? wait);
|
|
1309
|
+
result = waited.value;
|
|
1310
|
+
if (waited.timedOut)
|
|
1311
|
+
process.exitCode = runWait_1.RUN_WAIT_TIMEOUT_EXIT_CODE;
|
|
1312
|
+
}
|
|
967
1313
|
break;
|
|
968
1314
|
}
|
|
969
1315
|
case 'tables_import': {
|
|
970
1316
|
await (0, promises_1.access)(command.file);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
const
|
|
1317
|
+
// Search ranking is not an import contract. This exact capability owns the
|
|
1318
|
+
// prepare handoff for caller-local files, while nearby upload capabilities
|
|
1319
|
+
// can also match these words and made a real import fail after local work.
|
|
1320
|
+
const importCapabilityId = 'tables.table_sources.csv_import_prepare';
|
|
1321
|
+
const importContract = await client.getTool(importCapabilityId);
|
|
976
1322
|
result = await client.runCapability(importCapabilityId, {
|
|
977
1323
|
// A mutation run needs the turn token its own get minted; without it
|
|
978
1324
|
// an API-key import was refused after the local file work.
|
|
979
|
-
tool_turn_token: toolTurnToken(
|
|
1325
|
+
tool_turn_token: toolTurnToken(importContract),
|
|
980
1326
|
mode: 'live',
|
|
981
1327
|
params: {
|
|
982
1328
|
table_id: command.tableId,
|
|
983
1329
|
file_path: command.file,
|
|
984
1330
|
...(command.format ? { file_kind: command.format } : {}),
|
|
985
1331
|
...(command.upsertKey ? { upsert_key: command.upsertKey } : {}),
|
|
1332
|
+
...(command.rowKind ? { row_kind: command.rowKind } : {}),
|
|
1333
|
+
...(command.keepUnmappedColumns === false ? { keep_unmapped_columns: false } : {}),
|
|
986
1334
|
},
|
|
987
|
-
});
|
|
1335
|
+
}, capabilityIdempotencyKey(importContract));
|
|
988
1336
|
break;
|
|
989
1337
|
}
|
|
990
1338
|
case 'runs_list':
|
|
@@ -1002,15 +1350,20 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
1002
1350
|
case 'runs_events':
|
|
1003
1351
|
result = await client.getRunEvents(command.runId);
|
|
1004
1352
|
break;
|
|
1005
|
-
case 'runs_cancel':
|
|
1006
|
-
|
|
1353
|
+
case 'runs_cancel': {
|
|
1354
|
+
const body = command.body ?? {
|
|
1355
|
+
expected_state_version: command.stateVersion ?? stateVersionFromRunRead(await client.getRun(command.runId)),
|
|
1356
|
+
...(command.reason ? { reason: command.reason } : {}),
|
|
1357
|
+
};
|
|
1358
|
+
result = await client.cancelRun(command.runId, body, command.idempotencyKey);
|
|
1007
1359
|
break;
|
|
1360
|
+
}
|
|
1008
1361
|
case 'pack': {
|
|
1009
1362
|
if (command.pack === 'sequences') {
|
|
1010
|
-
await require('./commands/sequences').runSequencesCommand(client, command, stdout);
|
|
1363
|
+
await require('./commands/sequences').runSequencesCommand(client, command, stdout, runtime.sleep ?? wait);
|
|
1011
1364
|
}
|
|
1012
1365
|
else {
|
|
1013
|
-
await require('./commands/workflows').runWorkflowsCommand(client, command, stdout);
|
|
1366
|
+
await require('./commands/workflows').runWorkflowsCommand(client, command, stdout, runtime.sleep ?? wait);
|
|
1014
1367
|
}
|
|
1015
1368
|
return;
|
|
1016
1369
|
}
|
|
@@ -1022,7 +1375,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
1022
1375
|
printPackRoute(result, stdout);
|
|
1023
1376
|
else
|
|
1024
1377
|
print(result, stdout, Boolean(command.jsonOutput));
|
|
1025
|
-
process.exitCode
|
|
1378
|
+
if (process.exitCode !== runWait_1.RUN_WAIT_TIMEOUT_EXIT_CODE)
|
|
1379
|
+
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
1026
1380
|
}
|
|
1027
1381
|
catch (error) {
|
|
1028
1382
|
const exitCode = exitCodeForError(error);
|