@trydreamstate/cli 1.3.3 → 1.3.5

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.
Files changed (74) hide show
  1. package/README.md +14 -7
  2. package/agent-plugin/.claude-plugin/plugin.json +1 -1
  3. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  4. package/agent-plugin/hooks/approve-readonly.sh +2 -2
  5. package/agent-plugin/hooks/hooks.json +1 -1
  6. package/agent-plugin/skills/crm/SKILL.md +2 -2
  7. package/agent-plugin/skills/diagnostics/SKILL.md +3 -1
  8. package/agent-plugin/skills/dreamstate/SKILL.md +5 -3
  9. package/agent-plugin/skills/first-win/SKILL.md +2 -2
  10. package/agent-plugin/skills/inbox-replies/SKILL.md +3 -1
  11. package/agent-plugin/skills/knowledge/SKILL.md +3 -5
  12. package/agent-plugin/skills/linkedin-posts/SKILL.md +3 -1
  13. package/agent-plugin/skills/linkedin-warm-signals/SKILL.md +3 -3
  14. package/agent-plugin/skills/list-building/SKILL.md +4 -9
  15. package/agent-plugin/skills/list-building/column-authoring.md +3 -2
  16. package/agent-plugin/skills/list-building/sourcing.md +2 -2
  17. package/agent-plugin/skills/outreach/SKILL.md +3 -3
  18. package/agent-plugin/skills/plays/SKILL.md +3 -3
  19. package/agent-plugin/skills/reddit-posts/SKILL.md +3 -1
  20. package/agent-plugin/skills/skill-authoring/SKILL.md +3 -1
  21. package/agent-plugin/skills/social/SKILL.md +3 -5
  22. package/agent-plugin/skills/visibility/SKILL.md +2 -6
  23. package/agent-plugin/skills/workflows/SKILL.md +2 -2
  24. package/agent-plugin/skills/workflows/lifecycle-and-enrollment.md +1 -1
  25. package/agent-plugin/skills/worksheet-tidy/SKILL.md +3 -1
  26. package/agent-plugin/skills/writing/SKILL.md +3 -5
  27. package/agent-plugin/skills/x-posts/SKILL.md +3 -1
  28. package/dist/agentGuidance.generated.json +4 -4
  29. package/dist/capabilityPosture.d.ts +12 -0
  30. package/dist/capabilityPosture.d.ts.map +1 -0
  31. package/dist/capabilityPosture.js +39 -0
  32. package/dist/client.d.ts +9 -3
  33. package/dist/client.d.ts.map +1 -1
  34. package/dist/client.js +110 -63
  35. package/dist/commands/sequences.d.ts +3 -2
  36. package/dist/commands/sequences.d.ts.map +1 -1
  37. package/dist/commands/sequences.js +19 -37
  38. package/dist/commands/shared.d.ts.map +1 -1
  39. package/dist/commands/shared.js +22 -45
  40. package/dist/commands/workflows.d.ts +3 -2
  41. package/dist/commands/workflows.d.ts.map +1 -1
  42. package/dist/commands/workflows.js +19 -35
  43. package/dist/config.d.ts +5 -1
  44. package/dist/config.d.ts.map +1 -1
  45. package/dist/config.js +12 -2
  46. package/dist/errors.d.ts +2 -6
  47. package/dist/errors.d.ts.map +1 -1
  48. package/dist/errors.js +10 -43
  49. package/dist/gatedRun.d.ts +2 -2
  50. package/dist/gatedRun.d.ts.map +1 -1
  51. package/dist/gatedRun.js +157 -60
  52. package/dist/generated/packVerbs.generated.json +492 -0
  53. package/dist/generated/runContract.generated.d.ts +4 -0
  54. package/dist/generated/runContract.generated.d.ts.map +1 -0
  55. package/dist/generated/runContract.generated.js +7 -0
  56. package/dist/index.d.ts +9 -2
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +164 -59
  59. package/dist/money.d.ts +10 -0
  60. package/dist/money.d.ts.map +1 -0
  61. package/dist/money.js +75 -0
  62. package/dist/registryManifest.d.ts +6 -0
  63. package/dist/registryManifest.d.ts.map +1 -0
  64. package/dist/registryManifest.js +66 -0
  65. package/dist/runContract.d.ts +10 -0
  66. package/dist/runContract.d.ts.map +1 -0
  67. package/dist/runContract.js +13 -0
  68. package/dist/runWait.d.ts +0 -1
  69. package/dist/runWait.d.ts.map +1 -1
  70. package/dist/runWait.js +2 -13
  71. package/dist/turnCache.d.ts +3 -1
  72. package/dist/turnCache.d.ts.map +1 -1
  73. package/dist/turnCache.js +6 -2
  74. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.toolTurnToken = exports.omitsToolTurnForInlineTableRead = exports.capabilityIdempotencyKey = exports.CliUsageError = exports.CLI_EXIT = void 0;
4
+ exports.capabilityIdempotencyKey = exports.CliUsageError = exports.CLI_EXIT = void 0;
5
5
  exports.parseCliArgs = parseCliArgs;
6
6
  exports.refusalEnvelope = refusalEnvelope;
7
7
  exports.openWebUrl = openWebUrl;
@@ -20,6 +20,7 @@ const config_1 = require("./config");
20
20
  const pluginInstaller_1 = require("./pluginInstaller");
21
21
  const turnCache_1 = require("./turnCache");
22
22
  const idempotency_1 = require("./idempotency");
23
+ const money_1 = require("./money");
23
24
  const runWait_1 = require("./runWait");
24
25
  const client_1 = require("./client");
25
26
  const errors_1 = require("./errors");
@@ -27,12 +28,10 @@ Object.defineProperty(exports, "CLI_EXIT", { enumerable: true, get: function ()
27
28
  Object.defineProperty(exports, "CliUsageError", { enumerable: true, get: function () { return errors_1.CliUsageError; } });
28
29
  const gatedRun_1 = require("./gatedRun");
29
30
  Object.defineProperty(exports, "capabilityIdempotencyKey", { enumerable: true, get: function () { return gatedRun_1.capabilityIdempotencyKey; } });
30
- Object.defineProperty(exports, "omitsToolTurnForInlineTableRead", { enumerable: true, get: function () { return gatedRun_1.omitsToolTurnForInlineTableRead; } });
31
- Object.defineProperty(exports, "toolTurnToken", { enumerable: true, get: function () { return gatedRun_1.toolTurnToken; } });
32
31
  const tablesImport_1 = require("./tablesImport");
33
32
  const commandTable_1 = require("./commandTable");
34
33
  const skillInstaller_1 = require("./skillInstaller");
35
- const TOOLS_RUN_MODES = ['smoke_test', 'dry_run', 'live'];
34
+ const runContract_1 = require("./runContract");
36
35
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
37
36
  const USAGE = `Dreamstate CLI
38
37
 
@@ -53,26 +52,28 @@ Usage:
53
52
  dreamstate packs list [--json-output|--json]
54
53
  dreamstate packs get <pack_id> [--json-output|--json]
55
54
  dreamstate packs route --goal <text> [--json-output|--json]
56
- dreamstate skills list [--query <query>] [--limit <n>] [--json-output|--json]
55
+ dreamstate skills list [--query <query>] [--limit <n>] [--cursor <cursor>] [--json-output|--json]
57
56
  dreamstate skills get <skill_id> [--file <path>] [--json-output|--json]
58
57
  dreamstate skills search <query> [--limit <n>] [--json-output|--json]
59
58
  dreamstate skills install [--skill <skill_id|*>] [--agents <codex claude-code cursor>] [--project] [--copy] [--json-output|--json]
60
59
  dreamstate skills doctor [--agents <codex claude-code cursor>] [--project] [--json-output|--json]
61
60
  dreamstate commands search <term> [--json-output|--json]
62
- dreamstate tools search --goal <goal> [--terms <csv>] [--limit <n>] [--toolset <pack_id>] [--json-output|--json]
61
+ dreamstate tools search --goal <goal> [--terms <csv>] [--limit <n>] [--cursor <cursor>] [--toolset <pack_id>] [--json-output|--json]
63
62
  dreamstate tools get <capability_id> [--json-output|--json]
64
- dreamstate tools run <capability_id> [--params <object>|--json <body>] [--turn-token <token>] (--live | --dry-run | --mode <smoke_test|dry_run|live>) [--approved] [--approval-id <uuid>] [--approved-effect-unknown] [--max-credits <n>] [--quote-hash <sha256:...>] [--idempotency-key <key>] [--wait-timeout <seconds>] [--no-wait] [--open] [--json-output|--json]
63
+ dreamstate tools run <capability_id> [--params <object>|--json <body>] [--turn-token <token>] (--live | --dry-run | --mode <smoke_test|dry_run|live>) [--approved] [--approval-id <uuid>] [--approved-effect-unknown] [--max-credits <n>] [--max-live-sends <n>] [--quote-hash <sha256:...>] [--idempotency-key <key>] [--wait-timeout <seconds>] [--no-wait] [--open] [--json-output]
65
64
  dreamstate sequences <verb> [args] [flags]
66
65
  dreamstate workflows <verb> [args] [flags]
67
66
  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>] [--json-output|--json]
68
67
  dreamstate runs list [--limit <n>] [--cursor <cursor>] [--json <query>] [--json-output|--json]
69
68
  dreamstate runs get <run_id> [--json-output|--json]
70
- dreamstate runs events <run_id> [--json-output|--json]
69
+ dreamstate runs status <run_id> [--json-output|--json]
70
+ dreamstate runs wait <run_id> [--wait-timeout <seconds>] [--json-output|--json]
71
+ dreamstate runs events <run_id> [--limit <n>] [--cursor <cursor>] [--json-output|--json]
71
72
  dreamstate runs cancel <run_id> [--state-version <n>] [--reason <text>] [--json <body>] [--idempotency-key <key>] [--json-output|--json]
72
73
 
73
74
  All catalog discovery reads live API records. Set DREAMSTATE_API_URL and DREAMSTATE_API_KEY.
74
75
  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}.
75
- A write you re-run unchanged presents the same Idempotency-Key (printed as meta.idempotency_key), so the server replays instead of running it twice; --idempotency-key sets one yourself. Exit codes: 0 ok, 2 usage, 3 auth, 4 not found, 5 server failure, 6 rate limited, 7 approval/consent/spend gate required, 8 CLI update required, 9 refused, 10 transport, 11 timeout.
76
+ A write you re-run unchanged presents the same Idempotency-Key (printed as meta.idempotency_key), so the server replays instead of running it twice; --idempotency-key sets one yourself. Exit codes: 0 ok, 2 usage, 3 auth, 4 not found, 5 server failure, 6 rate limited, 7 repair.kind ask_user, 8 CLI update required, 9 refused, 10 transport, 11 timeout.
76
77
  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.
77
78
  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.
78
79
  logout deletes the stored credentials file. --agents takes space-separated, comma-separated or repeated values.
@@ -103,7 +104,7 @@ function csv(raw, flag = '--terms') {
103
104
  return [...new Set(values)];
104
105
  }
105
106
  function requireToolsRunMode(body) {
106
- if (TOOLS_RUN_MODES.includes(body.mode))
107
+ if (runContract_1.RUN_MODES.includes(body.mode))
107
108
  return;
108
109
  throw new errors_1.CliUsageError('mode must be one of: smoke_test, dry_run, live.');
109
110
  }
@@ -119,6 +120,12 @@ function nonNegativeInteger(raw, flag) {
119
120
  throw new errors_1.CliUsageError(`${flag} must be a non-negative integer`);
120
121
  return value;
121
122
  }
123
+ function positiveInteger(raw, flag) {
124
+ const value = Number(raw);
125
+ if (!Number.isSafeInteger(value) || value < 1)
126
+ throw new errors_1.CliUsageError(`${flag} must be an integer greater than or equal to 1`);
127
+ return value;
128
+ }
122
129
  // runs.get returns { data: { run: { state_version } } }. Cancel posts
123
130
  // expected_state_version, and a missing read must not invent 0.
124
131
  function stateVersionFromRunRead(payload) {
@@ -143,8 +150,13 @@ function parsePacks(argv) {
143
150
  return { command: 'packs_list', ...outputOption(values) };
144
151
  if (positionals[0] === 'get')
145
152
  return { command: 'packs_get', packId: requireOne(positionals, 'dreamstate packs get <pack_id>'), ...outputOption(values) };
146
- if (positionals.length === 1 && positionals[0] === 'route' && values.goal?.trim())
147
- return { command: 'packs_route', goal: values.goal.trim(), ...outputOption(values) };
153
+ if (positionals[0] === 'route') {
154
+ // `packs route "find work emails"` used to fall through to the generic
155
+ // usage line, which never said the goal belongs in --goal.
156
+ if (positionals.length === 1 && values.goal?.trim())
157
+ return { command: 'packs_route', goal: values.goal.trim(), ...outputOption(values) };
158
+ throw new errors_1.CliUsageError('Usage: dreamstate packs route --goal <text>. Pass the goal as --goal, not as a positional argument.');
159
+ }
148
160
  throw new errors_1.CliUsageError('Usage: dreamstate packs <list|get|route>');
149
161
  }
150
162
  /** Rewrites `--agents a b c` into `--agents=a --agents=b --agents=c` so parseArgs sees every value. */
@@ -167,7 +179,7 @@ function spaceSeparatedAgents(argv) {
167
179
  return out;
168
180
  }
169
181
  function parseSkills(argv) {
170
- const { values, positionals } = (0, node_util_1.parseArgs)({ args: spaceSeparatedAgents(argv), options: { query: { type: 'string' }, limit: { type: 'string' }, skill: { type: 'string' }, file: { type: 'string' }, agents: { type: 'string', multiple: true }, project: { type: 'boolean' }, copy: { type: 'boolean' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
182
+ const { values, positionals } = (0, node_util_1.parseArgs)({ args: spaceSeparatedAgents(argv), options: { query: { type: 'string' }, limit: { type: 'string' }, cursor: { type: 'string' }, skill: { type: 'string' }, file: { type: 'string' }, agents: { type: 'string', multiple: true }, project: { type: 'boolean' }, copy: { type: 'boolean' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
171
183
  if (values.help)
172
184
  return { command: 'help' };
173
185
  const agents = () => {
@@ -185,8 +197,16 @@ function parseSkills(argv) {
185
197
  query.query = values.query;
186
198
  if (values.limit !== undefined)
187
199
  query.limit = nonNegativeInteger(values.limit, '--limit');
200
+ if (values.cursor !== undefined) {
201
+ const cursor = values.cursor.trim();
202
+ if (!cursor)
203
+ throw new errors_1.CliUsageError('--cursor must be a non-empty string');
204
+ query.cursor = cursor;
205
+ }
188
206
  return { command: 'skills_list', query, ...outputOption(values) };
189
207
  }
208
+ if (values.cursor !== undefined)
209
+ throw new errors_1.CliUsageError('--cursor is only valid on skills list');
190
210
  if (positionals[0] === 'get')
191
211
  return { command: 'skills_get', skillId: requireOne(positionals, 'dreamstate skills get <skill_id>'), ...(values.file?.trim() ? { file: values.file.trim() } : {}), ...outputOption(values) };
192
212
  if (positionals[0] === 'search' && positionals.length > 1) {
@@ -225,8 +245,8 @@ function toolsRunMode(values) {
225
245
  chosen.add('live');
226
246
  if (values.mode !== undefined) {
227
247
  const mode = values.mode.trim();
228
- if (!TOOLS_RUN_MODES.includes(mode)) {
229
- throw new errors_1.CliUsageError(`--mode must be one of: ${TOOLS_RUN_MODES.join(', ')}`);
248
+ if (!runContract_1.RUN_MODES.includes(mode)) {
249
+ throw new errors_1.CliUsageError(`--mode must be one of: ${runContract_1.RUN_MODES.join(', ')}`);
230
250
  }
231
251
  chosen.add(mode);
232
252
  }
@@ -238,12 +258,12 @@ function toolsRunMode(values) {
238
258
  function parseTools(argv) {
239
259
  const { values, positionals } = (0, node_util_1.parseArgs)({ args: argv, options: {
240
260
  json: { type: 'string' }, params: { type: 'string' }, goal: { type: 'string' }, terms: { type: 'string' },
241
- limit: { type: 'string' }, toolset: { type: 'string' },
261
+ limit: { type: 'string' }, cursor: { type: 'string' }, toolset: { type: 'string' },
242
262
  'idempotency-key': { type: 'string' }, 'json-output': { type: 'boolean' }, open: { type: 'boolean' },
243
263
  'dry-run': { type: 'boolean' }, live: { type: 'boolean' }, mode: { type: 'string' },
244
264
  approved: { type: 'boolean' }, 'approved-effect-unknown': { type: 'boolean' },
245
265
  'quote-hash': { type: 'string' }, 'approval-id': { type: 'string' },
246
- 'max-credits': { type: 'string' }, 'turn-token': { type: 'string' }, 'wait-timeout': { type: 'string' }, 'no-wait': { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
266
+ 'max-credits': { type: 'string' }, 'max-live-sends': { type: 'string' }, 'turn-token': { type: 'string' }, 'wait-timeout': { type: 'string' }, 'no-wait': { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
247
267
  }, allowPositionals: true, strict: true });
248
268
  if (values.help)
249
269
  return { command: 'help' };
@@ -256,12 +276,20 @@ function parseTools(argv) {
256
276
  query.query_terms = csv(values.terms);
257
277
  if (values.limit !== undefined)
258
278
  query.limit = nonNegativeInteger(values.limit, '--limit');
279
+ if (values.cursor !== undefined) {
280
+ const cursor = values.cursor.trim();
281
+ if (!cursor)
282
+ throw new errors_1.CliUsageError('--cursor must be a non-empty string');
283
+ query.cursor = cursor;
284
+ }
259
285
  if (values.toolset !== undefined)
260
286
  query.toolset = values.toolset;
261
287
  if (typeof query.goal !== 'string' && !Array.isArray(query.query_terms))
262
288
  throw new errors_1.CliUsageError('tools search requires --goal, --terms, or --json query fields');
263
289
  return { command: 'tools_search', query, ...output };
264
290
  }
291
+ if (values.cursor !== undefined)
292
+ throw new errors_1.CliUsageError('--cursor is only valid on tools search');
265
293
  if (positionals[0] === 'get')
266
294
  return { command: 'tools_get', capabilityId: requireOne(positionals, 'dreamstate tools get <capability_id>'), ...output };
267
295
  if (positionals[0] === 'run') {
@@ -293,8 +321,17 @@ function parseTools(argv) {
293
321
  body.approved_effect_unknown = true;
294
322
  if (values['max-credits'] !== undefined)
295
323
  body.max_credits = nonNegativeNumber(values['max-credits'], '--max-credits');
324
+ if (body.max_live_sends !== undefined) {
325
+ throw new errors_1.CliUsageError('max_live_sends is a capability parameter. Put it in params.max_live_sends or pass --max-live-sends.');
326
+ }
327
+ if (values['max-live-sends'] !== undefined) {
328
+ const params = body.params;
329
+ if (!params || typeof params !== 'object' || Array.isArray(params))
330
+ throw new errors_1.CliUsageError('tools run params must be an object');
331
+ params.max_live_sends = positiveInteger(values['max-live-sends'], '--max-live-sends');
332
+ }
296
333
  if (values['quote-hash'] !== undefined) {
297
- if (!/^sha256:[a-f0-9]{64}$/.test(values['quote-hash']))
334
+ if (!runContract_1.QUOTE_HASH_PATTERN.test(values['quote-hash']))
298
335
  throw new errors_1.CliUsageError('--quote-hash must be the sha256:<64 hex> quote_hash a dry run returned');
299
336
  body.quote_hash = values['quote-hash'];
300
337
  }
@@ -317,6 +354,9 @@ function parseTools(argv) {
317
354
  if (body.mode !== undefined)
318
355
  requireToolsRunMode(body);
319
356
  const waitTimeout = values['wait-timeout'] === undefined ? 120 : nonNegativeNumber(values['wait-timeout'], '--wait-timeout');
357
+ if (values['idempotency-key'] !== undefined && values['idempotency-key'].length > runContract_1.IDEMPOTENCY_KEY_MAX_LENGTH) {
358
+ throw new errors_1.CliUsageError(`--idempotency-key must be at most ${runContract_1.IDEMPOTENCY_KEY_MAX_LENGTH} characters`);
359
+ }
320
360
  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 };
321
361
  }
322
362
  throw new errors_1.CliUsageError('Usage: dreamstate tools <search|get|run>');
@@ -374,7 +414,7 @@ function runsListLimit(raw) {
374
414
  return value;
375
415
  }
376
416
  function parseRuns(argv) {
377
- 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 });
417
+ 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' }, 'wait-timeout': { type: 'string' }, open: { type: 'boolean' }, 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
378
418
  if (values.help)
379
419
  return { command: 'help' };
380
420
  const output = outputOption(values);
@@ -390,19 +430,39 @@ function parseRuns(argv) {
390
430
  }
391
431
  return { command: 'runs_list', query, ...output };
392
432
  }
433
+ if (positionals[0] === 'events') {
434
+ const runId = requireOne(positionals, 'dreamstate runs events <run_id>');
435
+ const query = {};
436
+ if (values.limit !== undefined)
437
+ query.limit = runsListLimit(values.limit);
438
+ if (values.cursor !== undefined) {
439
+ const cursor = values.cursor.trim();
440
+ if (!cursor)
441
+ throw new errors_1.CliUsageError('--cursor must be a non-empty string');
442
+ query.cursor = cursor;
443
+ }
444
+ return { command: 'runs_events', runId, ...(Object.keys(query).length ? { query } : {}), ...output };
445
+ }
393
446
  if (values.limit !== undefined || values.cursor !== undefined) {
394
- throw new errors_1.CliUsageError('--limit and --cursor are only valid on runs list');
447
+ throw new errors_1.CliUsageError('--limit and --cursor are only valid on runs list and runs events');
395
448
  }
396
449
  if (positionals[0] === 'resume') {
397
450
  // The server retired generic run resume (verifyExecutionDoorCollapse.ts blocks the
398
451
  // route), so there is nothing to call. Say where the replacement is.
399
452
  throw new errors_1.CliUsageError('runs resume was retired: the server has no generic run resume. A table run checkpoint resumes with: dreamstate tools run tables.table_runs.resume --params \'{...}\' --live. Start a new run for anything else.');
400
453
  }
401
- const runId = requireOne(positionals, `dreamstate runs ${positionals[0] ?? '<get|events|cancel>'} <run_id>`);
402
- if (positionals[0] === 'get')
454
+ const runId = requireOne(positionals, `dreamstate runs ${positionals[0] ?? '<get|status|wait|events|cancel>'} <run_id>`);
455
+ if (positionals[0] === 'get' || positionals[0] === 'status') {
456
+ if (values['wait-timeout'] !== undefined)
457
+ throw new errors_1.CliUsageError('--wait-timeout is only valid on runs wait');
403
458
  return { command: 'runs_get', runId, ...output };
404
- if (positionals[0] === 'events')
405
- return { command: 'runs_events', runId, ...output };
459
+ }
460
+ if (positionals[0] === 'wait') {
461
+ const waitTimeout = values['wait-timeout'] === undefined ? 120 : nonNegativeNumber(values['wait-timeout'], '--wait-timeout');
462
+ return { command: 'runs_wait', runId, waitTimeout, ...output };
463
+ }
464
+ if (values['wait-timeout'] !== undefined)
465
+ throw new errors_1.CliUsageError('--wait-timeout is only valid on runs wait');
406
466
  if (positionals[0] === 'cancel') {
407
467
  const idempotency = values['idempotency-key'] ? { idempotencyKey: values['idempotency-key'] } : {};
408
468
  if (values.json && (values['state-version'] !== undefined || values.reason)) {
@@ -419,7 +479,13 @@ function parseRuns(argv) {
419
479
  ...output,
420
480
  };
421
481
  }
422
- throw new errors_1.CliUsageError('Usage: dreamstate runs <list|get|events|cancel> <run_id>');
482
+ throw new errors_1.CliUsageError('Usage: dreamstate runs <list|get|status|wait|events|cancel> <run_id>');
483
+ }
484
+ function sequencesCommands() {
485
+ return require('./commands/sequences');
486
+ }
487
+ function workflowsCommands() {
488
+ return require('./commands/workflows');
423
489
  }
424
490
  function normalizeJsonOutputAlias(argv) {
425
491
  return argv.map((argument, index) => argument === '--json' && (index === argv.length - 1 || argv[index + 1]?.startsWith('-'))
@@ -521,17 +587,15 @@ function parseCliArgs(argv) {
521
587
  if (group === 'runs')
522
588
  return parseRuns(rest);
523
589
  if (group === 'sequences')
524
- return require('./commands/sequences').parseSequencesCommand(rest);
590
+ return sequencesCommands().parseSequencesCommand(rest);
525
591
  if (group === 'workflows')
526
- return require('./commands/workflows').parseWorkflowsCommand(rest);
592
+ return workflowsCommands().parseWorkflowsCommand(rest);
527
593
  throw new errors_1.CliUsageError(`Unknown command: ${group}`);
528
594
  }
529
595
  function print(value, stdout, compact) {
530
- // Printed exactly as the server returned it. tool_turn_token used to be stripped
531
- // here, which made --turn-token impossible to use: a caller could never read the
532
- // token from `tools get` to pass back. The token is a short-lived fetch fence tied
533
- // to the caller's own session, not a credential, and MCP and REST return it too.
534
- stdout.write(`${JSON.stringify(value, null, compact ? 0 : 2)}\n`);
596
+ // tool_turn_token stays: it is a short-lived fetch fence, not a credential.
597
+ // Provider USD does not. Customer maps (values, cells, params, ...) are left whole.
598
+ stdout.write(`${JSON.stringify((0, money_1.scrubCliOutput)(value), null, compact ? 0 : 2)}\n`);
535
599
  }
536
600
  function printPackRoute(value, stdout) {
537
601
  const data = value && typeof value === 'object' && !Array.isArray(value)
@@ -557,7 +621,7 @@ function printPackRoute(value, stdout) {
557
621
  continue;
558
622
  const { pack_id: packId, reason } = alternate;
559
623
  if (typeof packId === 'string')
560
- stdout.write(`${packId}${typeof reason === 'string' ? `: ${reason}` : ''}\n`);
624
+ stdout.write(`${(0, money_1.scrubDollarText)(packId)}${typeof reason === 'string' ? `: ${(0, money_1.scrubDollarText)(reason)}` : ''}\n`);
561
625
  }
562
626
  }
563
627
  /**
@@ -721,12 +785,16 @@ function exitCodeForError(error) {
721
785
  // "a person has to decide", and an update-required refusal is 426.
722
786
  if (errorCode === 'cli_update_required')
723
787
  return errors_1.CLI_EXIT.UPDATE_REQUIRED;
724
- if ((0, errors_1.isApprovalGateCode)(errorCode))
725
- return errors_1.CLI_EXIT.APPROVAL;
788
+ // Local transport failures stamp repair.kind ask_user (read the target back,
789
+ // then ask a person). Their published exits win over that repair.
726
790
  if (errorCode === 'request_timeout')
727
791
  return errors_1.CLI_EXIT.TIMEOUT;
792
+ if (errorCode === 'network_error')
793
+ return errors_1.CLI_EXIT.TRANSPORT;
728
794
  if ((0, errors_1.isLocalRefusalCode)(errorCode))
729
795
  return errors_1.CLI_EXIT.REFUSED;
796
+ if ((0, errors_1.isApprovalRepair)(error))
797
+ return errors_1.CLI_EXIT.APPROVAL;
730
798
  const status = error instanceof client_1.CliError ? error.status : 0;
731
799
  if (status === 401 || status === 403)
732
800
  return errors_1.CLI_EXIT.AUTH;
@@ -734,8 +802,6 @@ function exitCodeForError(error) {
734
802
  return errors_1.CLI_EXIT.NOT_FOUND;
735
803
  if (status === 429)
736
804
  return errors_1.CLI_EXIT.RATE_LIMITED;
737
- if (errorCode === 'network_error')
738
- return errors_1.CLI_EXIT.TRANSPORT;
739
805
  if (status >= 500 || errorCode === 'invalid_response')
740
806
  return errors_1.CLI_EXIT.SERVER;
741
807
  if (status >= 400)
@@ -797,6 +863,23 @@ function whoamiData(value) {
797
863
  const data = value && typeof value === 'object' ? value.data : undefined;
798
864
  return data && typeof data === 'object' ? data : {};
799
865
  }
866
+ const WORKSPACE_MISMATCH_MESSAGE = 'The server acted in a different workspace than the one you approved.';
867
+ /** Canonical workspace id the whoami request actually acted in, or a refusal. */
868
+ function workspacePinFromWhoami(whoami, requested) {
869
+ const data = whoamiData(whoami);
870
+ const acted = typeof data.workspace_id === 'string' ? data.workspace_id : '';
871
+ const wanted = requested.trim().toLowerCase();
872
+ const rows = data.workspaces ?? [];
873
+ const match = rows.find((row) => row.id.toLowerCase() === wanted || row.slug?.toLowerCase() === wanted);
874
+ if (match) {
875
+ if (acted !== match.id)
876
+ throw new client_1.CliError(WORKSPACE_MISMATCH_MESSAGE, 'workspace_mismatch', 400);
877
+ return match.id;
878
+ }
879
+ if (rows.length === 0 && acted.toLowerCase() === wanted)
880
+ return acted;
881
+ throw new client_1.CliError(WORKSPACE_MISMATCH_MESSAGE, 'workspace_mismatch', 400);
882
+ }
800
883
  /**
801
884
  * What `dreamstate status` reports. The release identity comes from the server's
802
885
  * own answers (envelope meta and X-Dreamstate-* headers on every response), so
@@ -804,16 +887,15 @@ function whoamiData(value) {
804
887
  * "unknown", never "compatible".
805
888
  */
806
889
  function statusData(whoami, health, info) {
807
- const healthData = health && typeof health === 'object' ? health.data : null;
808
- const healthVersion = healthData && typeof healthData === 'object' ? healthData.version : null;
809
- const serverVersion = info.server_version ?? (typeof healthVersion === 'string' ? healthVersion : null);
810
- const resolved = { ...info, server_version: serverVersion };
811
- const skew = (0, client_1.describeVersionSkew)(resolved);
890
+ // health.data.version is not the release. server_version comes only from envelope meta.
891
+ const serverVersion = info.server_version;
892
+ const skew = (0, client_1.describeVersionSkew)(info);
812
893
  const compatible = skew !== 'cli_below_minimum';
813
894
  return {
814
895
  cli_version: client_1.CLI_VERSION,
815
896
  api_version: serverVersion,
816
897
  server_version: serverVersion,
898
+ registry_hash: info.registry_hash,
817
899
  release_sha: info.release_sha,
818
900
  minimum_cli_version: info.minimum_cli_version,
819
901
  minimum_cli_version_published: info.minimum_cli_version !== null,
@@ -899,17 +981,21 @@ async function loginWithDeviceAuthorization(runtime, stdout, stderr, profile, no
899
981
  for (;;) {
900
982
  const claim = await (0, client_1.claimDeviceAuthorization)(baseUrl, authorization.device_code);
901
983
  if (claim.status === 'ready') {
902
- if (profile)
903
- await (0, config_1.storeCredentialProfile)(profile, claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
904
- else
905
- await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
906
984
  const client = new client_1.DreamstateClient({
907
985
  baseUrl,
908
986
  apiKey: claim.token,
987
+ workspace: claim.workspace.id,
909
988
  requestTimeoutMs: runtime.requestTimeoutMs,
910
989
  onVersionWarning: (message) => (runtime.stderr ?? process.stderr).write(`${message}\n`),
911
990
  });
912
991
  const whoami = await client.getWhoami();
992
+ if (whoamiData(whoami).workspace_id !== claim.workspace.id) {
993
+ throw new client_1.CliError(WORKSPACE_MISMATCH_MESSAGE, 'workspace_mismatch', 400);
994
+ }
995
+ if (profile)
996
+ await (0, config_1.storeCredentialProfile)(profile, claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
997
+ else
998
+ await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
913
999
  const skills = await installLoginSkills(client, runtime, noSkills, stderr);
914
1000
  print(loginResult(whoami, skills), stdout, jsonOutput);
915
1001
  return;
@@ -1028,10 +1114,11 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
1028
1114
  onVersionWarning: (message) => stderr.write(`${message}\n`),
1029
1115
  });
1030
1116
  const whoami = await client.getWhoami();
1117
+ const pinned = requested ? workspacePinFromWhoami(whoami, requested) : null;
1031
1118
  if (command.profile)
1032
- await (0, config_1.storeCredentialProfile)(command.profile, token, runtimeCredentialsPath(runtime));
1119
+ await (0, config_1.storeCredentialProfile)(command.profile, token, runtimeCredentialsPath(runtime), pinned);
1033
1120
  else
1034
- await (0, config_1.storeApiKey)(token, runtimeCredentialsPath(runtime));
1121
+ await (0, config_1.storeApiKey)(token, runtimeCredentialsPath(runtime), pinned);
1035
1122
  const skills = await installLoginSkills(client, runtime, Boolean(command.noSkills), stderr);
1036
1123
  print(loginResult(whoami, skills), stdout, Boolean(command.jsonOutput));
1037
1124
  process.exitCode = errors_1.CLI_EXIT.SUCCESS;
@@ -1073,10 +1160,19 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
1073
1160
  const key = await readLoginKey(command, runtime);
1074
1161
  if (!(0, config_1.isDreamstateToken)(key))
1075
1162
  throw new errors_1.CliUsageError('Dreamstate API keys must start with ds_user_ or ds_live_.');
1163
+ const environment = runtime.env ?? process.env;
1164
+ const requested = extracted.workspace ?? environment.DREAMSTATE_WORKSPACE?.trim();
1165
+ const client = new client_1.DreamstateClient({
1166
+ baseUrl: (0, config_1.resolveApiBaseUrl)(environment),
1167
+ apiKey: key,
1168
+ ...(requested ? { workspace: requested } : {}),
1169
+ requestTimeoutMs: runtime.requestTimeoutMs,
1170
+ onVersionWarning: (message) => stderr.write(`${message}\n`),
1171
+ });
1172
+ const pinned = requested ? workspacePinFromWhoami(await client.getWhoami(), requested) : null;
1076
1173
  const path = command.profile
1077
- ? await (0, config_1.storeCredentialProfile)(command.profile, key, runtimeCredentialsPath(runtime))
1078
- : await (0, config_1.storeApiKey)(key, runtimeCredentialsPath(runtime));
1079
- 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`) });
1174
+ ? await (0, config_1.storeCredentialProfile)(command.profile, key, runtimeCredentialsPath(runtime), pinned)
1175
+ : await (0, config_1.storeApiKey)(key, runtimeCredentialsPath(runtime), pinned);
1080
1176
  const skills = await installLoginSkills(client, runtime, Boolean(command.noSkills), stderr);
1081
1177
  print({ ok: true, data: { credentials_path: path, skills }, meta: {} }, stdout, Boolean(command.jsonOutput));
1082
1178
  process.exitCode = errors_1.CLI_EXIT.SUCCESS;
@@ -1168,12 +1264,10 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
1168
1264
  // Resolving credentials first would refuse it with not_authenticated.
1169
1265
  if (command.command === 'pack' && command.verb === 'init') {
1170
1266
  if (command.pack === 'sequences') {
1171
- await require('./commands/sequences')
1172
- .runSequencesCommand(undefined, command, stdout);
1267
+ await sequencesCommands().runSequencesCommand(undefined, command, stdout);
1173
1268
  }
1174
1269
  else {
1175
- await require('./commands/workflows')
1176
- .runWorkflowsCommand(undefined, command, stdout);
1270
+ await workflowsCommands().runWorkflowsCommand(undefined, command, stdout);
1177
1271
  }
1178
1272
  process.exitCode = errors_1.CLI_EXIT.SUCCESS;
1179
1273
  return;
@@ -1341,8 +1435,19 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
1341
1435
  case 'runs_get':
1342
1436
  result = await client.getRun(command.runId);
1343
1437
  break;
1438
+ case 'runs_wait': {
1439
+ const initial = await client.getRun(command.runId);
1440
+ const waited = await (0, runWait_1.waitForRun)(client, initial, command.waitTimeout, runtime.sleep ?? wait);
1441
+ const blocked = (0, gatedRun_1.refusalFromRunResult)(waited.value);
1442
+ if (blocked)
1443
+ throw blocked;
1444
+ result = waited.value;
1445
+ if (waited.timedOut)
1446
+ process.exitCode = runWait_1.RUN_WAIT_TIMEOUT_EXIT_CODE;
1447
+ break;
1448
+ }
1344
1449
  case 'runs_events':
1345
- result = await client.getRunEvents(command.runId);
1450
+ result = await client.getRunEvents(command.runId, command.query ?? {});
1346
1451
  break;
1347
1452
  case 'runs_cancel': {
1348
1453
  const body = command.body ?? {
@@ -1366,10 +1471,10 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
1366
1471
  }
1367
1472
  case 'pack': {
1368
1473
  if (command.pack === 'sequences') {
1369
- await require('./commands/sequences').runSequencesCommand(gatedContext(), command, stdout);
1474
+ await sequencesCommands().runSequencesCommand(gatedContext(), command, stdout);
1370
1475
  }
1371
1476
  else {
1372
- await require('./commands/workflows').runWorkflowsCommand(gatedContext(), command, stdout);
1477
+ await workflowsCommands().runWorkflowsCommand(gatedContext(), command, stdout);
1373
1478
  }
1374
1479
  return;
1375
1480
  }
@@ -1386,7 +1491,7 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
1386
1491
  }
1387
1492
  catch (error) {
1388
1493
  const exitCode = exitCodeForError(error);
1389
- stdout.write(`${JSON.stringify(refusalEnvelope(error, exitCode))}\n`);
1494
+ stdout.write(`${JSON.stringify((0, money_1.scrubCliOutput)(refusalEnvelope(error, exitCode)))}\n`);
1390
1495
  process.exitCode = exitCode;
1391
1496
  throw error;
1392
1497
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Customers see credits. Provider USD (a key that pairs usd with a cost word,
3
+ * a cogs key other than cogs_credits, or a provider_cost / provider_spend
4
+ * prefix) is dropped. Dollar amounts in our own sentences become "an amount".
5
+ * Customer maps are not entered: annual_revenue_usd and price_usd stay.
6
+ */
7
+ export declare function isProviderCostKey(key: string): boolean;
8
+ export declare function scrubDollarText(text: string): string;
9
+ export declare function scrubCliOutput(value: unknown, depth?: number): unknown;
10
+ //# sourceMappingURL=money.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQtD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGpD;AAeD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAWjE"}
package/dist/money.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Customers see credits. Provider USD (a key that pairs usd with a cost word,
4
+ * a cogs key other than cogs_credits, or a provider_cost / provider_spend
5
+ * prefix) is dropped. Dollar amounts in our own sentences become "an amount".
6
+ * Customer maps are not entered: annual_revenue_usd and price_usd stay.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isProviderCostKey = isProviderCostKey;
10
+ exports.scrubDollarText = scrubDollarText;
11
+ exports.scrubCliOutput = scrubCliOutput;
12
+ const PROVIDER_COST_WORDS = new Set([
13
+ 'provider', 'cost', 'spend', 'spent', 'worst', 'reserved', 'counted', 'settled', 'ledger',
14
+ 'snapshot', 'actual', 'platform', 'vendor', 'upstream', 'internal',
15
+ ]);
16
+ const MARGIN_CONTEXT = new Set(['provider', 'platform', 'cogs', 'vendor']);
17
+ const CUSTOMER_DATA_CONTAINERS = new Set([
18
+ 'values', 'cells', 'cell_values', 'row_values', 'field_values', 'record_values',
19
+ 'fields', 'custom_fields', 'attributes', 'payload', 'params',
20
+ ]);
21
+ const SCHEMA_KEYWORDS = new Set([
22
+ 'type', '$ref', 'anyOf', 'oneOf', 'allOf', 'enum', 'const', 'properties', 'items', 'description', 'not',
23
+ ]);
24
+ const DOLLAR_AMOUNT_TEXT = /(?:US\$|\$)\s?\d[\d,]*(?:\.\d+)?(?:\s?(?:USD|dollars?))?|\b\d[\d,]*(?:\.\d+)?\s?(?:USD|dollars?)\b|\bUSD\s?\d[\d,]*(?:\.\d+)?/gi;
25
+ function snake(key) {
26
+ return key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/[-\s.]+/g, '_').toLowerCase();
27
+ }
28
+ function isPlainObject(value) {
29
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
30
+ }
31
+ function isProviderCostKey(key) {
32
+ const normalized = snake(key);
33
+ const tokens = normalized.split('_');
34
+ if (tokens.includes('cogs') && !tokens.includes('credits'))
35
+ return true;
36
+ if (normalized.startsWith('provider_cost') || normalized.startsWith('provider_spend'))
37
+ return true;
38
+ if (tokens.includes('usd') && tokens.some((token) => PROVIDER_COST_WORDS.has(token)))
39
+ return true;
40
+ if (tokens.includes('margin') && tokens.some((token) => MARGIN_CONTEXT.has(token)))
41
+ return true;
42
+ return false;
43
+ }
44
+ function scrubDollarText(text) {
45
+ DOLLAR_AMOUNT_TEXT.lastIndex = 0;
46
+ return text.replace(DOLLAR_AMOUNT_TEXT, 'an amount').replace(/\s{2,}/g, ' ');
47
+ }
48
+ function looksLikeSchemaProperties(value) {
49
+ if (!isPlainObject(value))
50
+ return false;
51
+ const entries = Object.values(value);
52
+ return entries.length > 0 && entries.every((entry) => isPlainObject(entry) && Object.keys(entry).some((key) => SCHEMA_KEYWORDS.has(key)));
53
+ }
54
+ function isCustomerContainer(key, value) {
55
+ if (key === 'properties')
56
+ return !looksLikeSchemaProperties(value);
57
+ return CUSTOMER_DATA_CONTAINERS.has(snake(key)) && isPlainObject(value);
58
+ }
59
+ function scrubCliOutput(value, depth = 0) {
60
+ if (depth > 32)
61
+ return value;
62
+ if (typeof value === 'string')
63
+ return scrubDollarText(value);
64
+ if (Array.isArray(value))
65
+ return value.map((entry) => scrubCliOutput(entry, depth + 1));
66
+ if (!isPlainObject(value))
67
+ return value;
68
+ const output = {};
69
+ for (const [key, child] of Object.entries(value)) {
70
+ if (isProviderCostKey(key))
71
+ continue;
72
+ output[key] = isCustomerContainer(key, child) ? child : scrubCliOutput(child, depth + 1);
73
+ }
74
+ return output;
75
+ }
@@ -0,0 +1,6 @@
1
+ import type { CapabilityPosture } from './capabilityPosture';
2
+ export declare function lookupPosture(capabilityId: string): CapabilityPosture | null;
3
+ export declare function rememberPosture(capabilityId: string, posture: CapabilityPosture): void;
4
+ /** Records the hash from a response. A change clears cached postures. */
5
+ export declare function noteRegistryHash(hash: string | null): void;
6
+ //# sourceMappingURL=registryManifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registryManifest.d.ts","sourceRoot":"","sources":["../src/registryManifest.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAuD7D,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAE5E;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAItF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAK1D"}