@trydreamstate/cli 1.3.2 → 1.3.4
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 +36 -16
- package/agent-plugin/.claude-plugin/plugin.json +1 -1
- package/agent-plugin/.codex-plugin/plugin.json +1 -1
- package/agent-plugin/hooks/approve-readonly.sh +15 -0
- package/agent-plugin/hooks/hooks.json +17 -0
- package/agent-plugin/skills/crm/SKILL.md +2 -2
- package/agent-plugin/skills/diagnostics/SKILL.md +3 -1
- package/agent-plugin/skills/dreamstate/SKILL.md +15 -5
- package/agent-plugin/skills/first-win/SKILL.md +2 -2
- package/agent-plugin/skills/inbox-replies/SKILL.md +3 -1
- package/agent-plugin/skills/knowledge/SKILL.md +3 -5
- package/agent-plugin/skills/linkedin-posts/SKILL.md +3 -1
- package/agent-plugin/skills/linkedin-warm-signals/SKILL.md +2 -2
- package/agent-plugin/skills/list-building/SKILL.md +5 -10
- package/agent-plugin/skills/list-building/column-authoring.md +1 -1
- package/agent-plugin/skills/list-building/sourcing.md +2 -2
- package/agent-plugin/skills/outreach/SKILL.md +2 -2
- package/agent-plugin/skills/outreach/steps-and-timing.md +1 -1
- package/agent-plugin/skills/plays/SKILL.md +3 -3
- package/agent-plugin/skills/reddit-posts/SKILL.md +3 -1
- package/agent-plugin/skills/skill-authoring/SKILL.md +3 -1
- package/agent-plugin/skills/social/SKILL.md +4 -6
- package/agent-plugin/skills/visibility/SKILL.md +2 -6
- package/agent-plugin/skills/workflows/SKILL.md +3 -3
- package/agent-plugin/skills/workflows/lifecycle-and-enrollment.md +1 -1
- package/agent-plugin/skills/worksheet-tidy/SKILL.md +3 -1
- package/agent-plugin/skills/writing/SKILL.md +3 -5
- package/agent-plugin/skills/x-posts/SKILL.md +3 -1
- package/dist/agentGuidance.generated.json +35 -0
- package/dist/capabilityPosture.d.ts +12 -0
- package/dist/capabilityPosture.d.ts.map +1 -0
- package/dist/capabilityPosture.js +39 -0
- package/dist/client.d.ts +33 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +155 -64
- package/dist/commandTable.d.ts +26 -0
- package/dist/commandTable.d.ts.map +1 -0
- package/dist/commandTable.js +54 -0
- package/dist/commands/sequences.d.ts +3 -2
- package/dist/commands/sequences.d.ts.map +1 -1
- package/dist/commands/sequences.js +17 -34
- package/dist/commands/shared.d.ts +4 -26
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/shared.js +136 -158
- package/dist/commands/workflows.d.ts +3 -2
- package/dist/commands/workflows.d.ts.map +1 -1
- package/dist/commands/workflows.js +17 -39
- package/dist/errors.d.ts +74 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +84 -0
- package/dist/gatedRun.d.ts +79 -0
- package/dist/gatedRun.d.ts.map +1 -0
- package/dist/gatedRun.js +346 -0
- package/dist/generated/packVerbs.generated.json +492 -0
- package/dist/generated/runContract.generated.d.ts +4 -0
- package/dist/generated/runContract.generated.d.ts.map +1 -0
- package/dist/generated/runContract.generated.js +7 -0
- package/dist/idempotency.d.ts +38 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +106 -0
- package/dist/index.d.ts +22 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +405 -378
- package/dist/registryManifest.d.ts +6 -0
- package/dist/registryManifest.d.ts.map +1 -0
- package/dist/registryManifest.js +66 -0
- package/dist/runContract.d.ts +10 -0
- package/dist/runContract.d.ts.map +1 -0
- package/dist/runContract.js +13 -0
- package/dist/runWait.d.ts +2 -2
- package/dist/runWait.d.ts.map +1 -1
- package/dist/runWait.js +3 -14
- package/dist/schemaCoercion.d.ts +20 -0
- package/dist/schemaCoercion.d.ts.map +1 -0
- package/dist/schemaCoercion.js +123 -0
- package/dist/skillInstaller.d.ts +40 -0
- package/dist/skillInstaller.d.ts.map +1 -1
- package/dist/skillInstaller.js +108 -9
- package/dist/tableFilePreview.d.ts +14 -0
- package/dist/tableFilePreview.d.ts.map +1 -1
- package/dist/tableFilePreview.js +19 -8
- package/dist/tableFileReader.d.ts +26 -0
- package/dist/tableFileReader.d.ts.map +1 -0
- package/dist/tableFileReader.js +108 -0
- package/dist/tablesImport.d.ts +57 -0
- package/dist/tablesImport.d.ts.map +1 -0
- package/dist/tablesImport.js +263 -0
- package/dist/turnCache.d.ts +14 -1
- package/dist/turnCache.d.ts.map +1 -1
- package/dist/turnCache.js +25 -8
- package/package.json +6 -3
|
@@ -3,7 +3,7 @@ name: workflows
|
|
|
3
3
|
description: "Best practices for building and running automations that are not outreach sequences: frame the job, smallest graph, validate after every change, runtime inputs, safe recovery of failed runs."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run` (`
|
|
6
|
+
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run`. Anything that writes, spends credits or reaches a person: dry_run first, then live with the quote_hash the dry_run returned, a max_credits ceiling and an idempotency_key. max_credits 0 is valid only when that quote is 0. A larger paid run, or a live external send above the server's recipient threshold, also needs the user's yes before you send approved: true; never send it on your own. `max_rows` is a param of `tables.columns.run` and `tables.table_sources.run`, not of `tools_run`. `max_live_sends` is a param of the external-send sequence capabilities (`sequence.sequences.activate_audience`, `sequence.sequences.resume`, and the rest of that family), sent inside `params`, not a gating input of `tools_run`. The paid-run consent rule is one sentence in the MCP server instructions, rendered from consentRule.json and PAID_RUN_APPROVAL_THRESHOLDS: rows, dollars, credits, and the conversion. If the server asks you to sign in, approve it in the browser.
|
|
7
7
|
|
|
8
8
|
# Workflows
|
|
9
9
|
|
|
@@ -19,7 +19,7 @@ Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabiliti
|
|
|
19
19
|
Best practices for building and running automations. The workflow pack lists the tools and their exact inputs; this skill is how to use them well. Outreach cadences to people are sequences, covered by the outreach skill. Everything else is a workflow.
|
|
20
20
|
|
|
21
21
|
1. Build new unless asked. A new request gets a new automation built from scratch. When the user names an existing one to change, resolve that name among existing automations by listing workflows and sequences with that name as the search text, then change the match. A named thing that is not a workflow or sequence may be a task, a record or a worksheet; check those only after automations come back empty. A step inside a workflow, such as a task step, is edited in that workflow's graph, not in the task list.
|
|
22
|
-
1a. "Enroll these in a workflow" is never a missing input. Use a workflow whose table trigger is the worksheet the user is looking at, or one they named; otherwise build a new draft for that worksheet (worksheet rows trigger plus fitting steps, a follow-up task per person by default), validate, and enroll. Never ask which workflow.
|
|
22
|
+
1a. "Enroll these in a workflow" is never a missing input. Use a workflow whose table trigger is the worksheet the user is looking at, or one they named; otherwise build a new draft for that worksheet (worksheet rows trigger plus fitting steps, a follow-up task per person by default), validate, and enroll. Never ask which workflow. Filters on any worksheet column narrow who enrolls; with no filter every row enrolls. When the user said who should enroll, filter on the columns that hold it and report how many enrolled and how many were held back.
|
|
23
23
|
2. Frame the job before building: what triggers it, who or what it acts on, what it changes, what it costs, when it stops and how often it runs.
|
|
24
24
|
2a. Use a blueprint when one does exactly what you need; otherwise build your own. Workflow blueprints already cover common jobs: CRM syncs, list and do-not-contact syncs, routers, digests and signal monitors. List blueprints of the workflow kind, read what the closest one creates and what it still needs, install it, then adapt it to this request. Installing never needs answers or connections; the result names what is still to pick, so say that. Build from scratch only when no blueprint fits, and say which one came closest.
|
|
25
25
|
3. Ask one clarifying question only when a required input is missing. Otherwise build with sensible defaults and say what you assumed.
|
|
@@ -31,7 +31,7 @@ Best practices for building and running automations. The workflow pack lists the
|
|
|
31
31
|
8a. Building never waits on rows. A worksheet trigger on an empty or still-filling worksheet is still a valid enrollment: build, validate and turn it on, and rows enroll as they land. Say it is waiting for rows, never stop because the worksheet is empty.
|
|
32
32
|
8b. Use the row-created trigger when each new worksheet row must enter once. It watches future row arrivals on one worksheet, carries that row identity through the graph, and does not reuse or freeze a saved selection. Use the table trigger only when user wants a selected saved-view audience.
|
|
33
33
|
8c. To tag or mark a triggering row, add the worksheet-row update step. Choose its column and either replace its value or append a value to its existing list. Do not ask user to export, reimport, or manually tag rows when workflow already has triggering row identity.
|
|
34
|
-
9.
|
|
34
|
+
9. Activating something that sends or spends follows the paid-run consent rule.
|
|
35
35
|
10. Draft, published, active, running and finished are separate facts. Report only what the last result proves.
|
|
36
36
|
|
|
37
37
|
## Delete and archive
|
|
@@ -6,6 +6,6 @@ Missing connections never block building, editing, saving a draft or validating.
|
|
|
6
6
|
|
|
7
7
|
Enrollment, activation and delivery are different events. Read the result after each one. After an unclear result, check current state before retrying, and never enroll twice or replay an effect that already happened.
|
|
8
8
|
|
|
9
|
-
Preview first. Activation
|
|
9
|
+
Preview first. Activation stores `params.max_credits` on `workflow.workflows.activate`: read exact `required_caps.max_credits` from preview, then resend that value unchanged. This persisted per-delivery ceiling is a declared domain input, not run `max_credits`; put run gating fields at door. Live enrollment takes no send cap: every eligible selected row is enrolled. Never guess it or ask user to approve it.
|
|
10
10
|
|
|
11
11
|
An active worksheet trigger snapshots its saved view on periodic ticks and idempotently enrolls rows that arrive later; draft, paused, and inactive workflows never refill.
|
|
@@ -3,7 +3,7 @@ name: worksheet-tidy
|
|
|
3
3
|
description: "Review a workbook worksheet's structure and values, including duplicate or overlapping columns, column order, types and formatting, columns to hide, criteria and run conditions that never vary, and suspicious values, then apply reversible fixes. Use it when a worksheet has grown messy, before sharing it, or before enrolling it into a sequence. It does not merge CRM records across the workspace, rebuild the list's source (list-building does) or explain failed runs (diagnostics does)."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run` (`
|
|
6
|
+
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run`. Anything that writes, spends credits or reaches a person: dry_run first, then live with the quote_hash the dry_run returned, a max_credits ceiling and an idempotency_key. max_credits 0 is valid only when that quote is 0. A larger paid run, or a live external send above the server's recipient threshold, also needs the user's yes before you send approved: true; never send it on your own. `max_rows` is a param of `tables.columns.run` and `tables.table_sources.run`, not of `tools_run`. `max_live_sends` is a param of the external-send sequence capabilities (`sequence.sequences.activate_audience`, `sequence.sequences.resume`, and the rest of that family), sent inside `params`, not a gating input of `tools_run`. The paid-run consent rule is one sentence in the MCP server instructions, rendered from consentRule.json and PAID_RUN_APPROVAL_THRESHOLDS: rows, dollars, credits, and the conversion. If the server asks you to sign in, approve it in the browser.
|
|
7
7
|
|
|
8
8
|
# Worksheet Tidy
|
|
9
9
|
|
|
@@ -59,3 +59,5 @@ Search the workbook tool for each change. The search returns runnable schemas fo
|
|
|
59
59
|
## Check it works
|
|
60
60
|
|
|
61
61
|
Done means checked, not built. Before you report, read back everything you made or changed, and run, test or preview it where that is offered; publish or send only when the user asked. An empty, failed or wrong result means an input or setting is wrong: find which from the error or the output, correct it and check again, until it is right or a real blocker stops it. Report what you checked and what it showed.
|
|
62
|
+
|
|
63
|
+
Paid and outward runs follow the paid-run consent rule.
|
|
@@ -3,7 +3,7 @@ name: writing
|
|
|
3
3
|
description: "Draft, revise and publish articles and landing copy grounded in workspace context and a target query."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run` (`
|
|
6
|
+
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run`. Anything that writes, spends credits or reaches a person: dry_run first, then live with the quote_hash the dry_run returned, a max_credits ceiling and an idempotency_key. max_credits 0 is valid only when that quote is 0. A larger paid run, or a live external send above the server's recipient threshold, also needs the user's yes before you send approved: true; never send it on your own. `max_rows` is a param of `tables.columns.run` and `tables.table_sources.run`, not of `tools_run`. `max_live_sends` is a param of the external-send sequence capabilities (`sequence.sequences.activate_audience`, `sequence.sequences.resume`, and the rest of that family), sent inside `params`, not a gating input of `tools_run`. The paid-run consent rule is one sentence in the MCP server instructions, rendered from consentRule.json and PAID_RUN_APPROVAL_THRESHOLDS: rows, dollars, credits, and the conversion. If the server asks you to sign in, approve it in the browser.
|
|
7
7
|
|
|
8
8
|
# Writing
|
|
9
9
|
|
|
@@ -14,10 +14,6 @@ Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabiliti
|
|
|
14
14
|
- Short-form social posts: [`social`].
|
|
15
15
|
- The `social` pack lists the tools and their exact inputs.
|
|
16
16
|
|
|
17
|
-
<!-- architect-operation-contract
|
|
18
|
-
{"required_capability_ids":[]}
|
|
19
|
-
-->
|
|
20
|
-
|
|
21
17
|
Own the draft, review, revise, and publish lifecycle for long-form articles and landing copy. Every factual claim traces to a workspace-wiki fact, cited evidence, or a dated external source; the voice matches what the workspace has already published; and each lifecycle step is a deliberate, verifiable state change. Traditional keyword, ranking, and generative-citation evidence belongs to `visibility`; current external evidence and durable facts about the business belong to `knowledge`. Never re-derive those measurements or restate a business fact from memory.
|
|
22
18
|
|
|
23
19
|
Authoring: prepare brief, evidence ledger, novelty decision, destination, and voice; compose internally; discover owner and hydrate schema; save complete artifact; read result before showing or advancing. Unsaved long-form copy in chat is not an article. Preserve exact user-named title. A rewrite is not persisted until live result says save succeeded.
|
|
@@ -82,3 +78,5 @@ Report the missing source, stale revision, failed destination, or missing publis
|
|
|
82
78
|
## Check it works
|
|
83
79
|
|
|
84
80
|
Done means checked, not built. Before you report, read back everything you made or changed, and run, test or preview it where that is offered; publish or send only when the user asked. An empty, failed or wrong result means an input or setting is wrong: find which from the error or the output, correct it and check again, until it is right or a real blocker stops it. Report what you checked and what it showed.
|
|
81
|
+
|
|
82
|
+
Paid and outward runs follow the paid-run consent rule.
|
|
@@ -3,7 +3,7 @@ name: x-posts
|
|
|
3
3
|
description: "Write and revise X posts and threads: the first line, single post versus thread, weighted length and Premium limits, thread structure, post types, replies and links, and phrases to avoid. Use it for the copy of any post going to X. It does not cover scheduling, publishing or automations (social does)."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run` (`
|
|
6
|
+
Run this through the Dreamstate MCP tools: `packs_get` for the pack's capabilities, `tools_get` for the exact input schema, then `tools_run`. Anything that writes, spends credits or reaches a person: dry_run first, then live with the quote_hash the dry_run returned, a max_credits ceiling and an idempotency_key. max_credits 0 is valid only when that quote is 0. A larger paid run, or a live external send above the server's recipient threshold, also needs the user's yes before you send approved: true; never send it on your own. `max_rows` is a param of `tables.columns.run` and `tables.table_sources.run`, not of `tools_run`. `max_live_sends` is a param of the external-send sequence capabilities (`sequence.sequences.activate_audience`, `sequence.sequences.resume`, and the rest of that family), sent inside `params`, not a gating input of `tools_run`. The paid-run consent rule is one sentence in the MCP server instructions, rendered from consentRule.json and PAID_RUN_APPROVAL_THRESHOLDS: rows, dollars, credits, and the conversion. If the server asks you to sign in, approve it in the browser.
|
|
7
7
|
|
|
8
8
|
# X posts
|
|
9
9
|
|
|
@@ -71,3 +71,5 @@ Change what was asked and keep the rest verbatim. If a thread post runs long, sp
|
|
|
71
71
|
## Check it works
|
|
72
72
|
|
|
73
73
|
Read the saved draft back: the hook alone justifies post two, each post is under the account's limit, each thread post stands alone, every number traces to something real, and nothing contains an em dash.
|
|
74
|
+
|
|
75
|
+
Paid and outward runs follow the paid-run consent rule.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generated_by": "packages/cli/scripts/build-agent-plugin.mjs from apps/backend/src/services/mcp/consentRule.json; do not edit by hand",
|
|
3
|
+
"consent": {
|
|
4
|
+
"summary": "Anything that writes, spends credits or reaches a person: dry_run first, then live with the quote_hash the dry_run returned, a max_credits ceiling and an idempotency_key. max_credits 0 is valid only when that quote is 0. A larger paid run, or a live external send above the server's recipient threshold, also needs the user's yes before you send approved: true; never send it on your own.",
|
|
5
|
+
"paid_run": "Anything that writes, spends credits or reaches a real person: run mode dry_run first, show the user the preview, the row count and the credit estimate, then run live with the quote_hash that dry_run returned, a max_credits ceiling and an idempotency_key. max_credits 0 is valid only when that quote is 0. A live paid run states max_rows; an external send states max_live_sends of at least the dry_run recipient_count. A quote returned by a refused live call is only a price, never a confirmation.",
|
|
6
|
+
"human_approval": "A paid run above the server's row or provider-spend limit, a live external send above the server's recipient threshold, or a run that turns on a recurring trigger, also needs the user's yes. Ask them, and only then send approved: true with the same quote_hash. You are asserting that you asked a person; never send it on your own."
|
|
7
|
+
},
|
|
8
|
+
"router_steps": [
|
|
9
|
+
{
|
|
10
|
+
"mcp": "whoami",
|
|
11
|
+
"cli": "dreamstate whoami --json",
|
|
12
|
+
"text": "names the user and the active workspace. If it lists several workspaces, confirm the right one before writing anything."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"mcp": "packs_route",
|
|
16
|
+
"cli": "dreamstate packs route --goal \"<goal>\" --json",
|
|
17
|
+
"text": "maps a goal to a domain; `packs_get` returns that domain's capabilities and skills."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"mcp": "tools_search",
|
|
21
|
+
"cli": "dreamstate tools search --goal \"<goal>\" --toolset <pack_id> --json",
|
|
22
|
+
"text": "finds the capability for the goal; `tools_get` returns its exact input schema. Read the schema every time, never guess params from memory."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"mcp": "tools_run",
|
|
26
|
+
"cli": "dreamstate tools run <capability_id> --params '{...}' --mode <dry_run|live> --json",
|
|
27
|
+
"text": "runs it with `capability_id` and `params`. The gating inputs (`mode`, `max_credits`, `idempotency_key`, `quote_hash`, `approved`) go on the run call itself, never inside `params`. `max_rows` is a param of `tables.columns.run` and `tables.table_sources.run`, not of `tools_run`. `max_live_sends` is a param of the external-send sequence capabilities (`sequence.sequences.activate_audience`, `sequence.sequences.resume`, and the rest of that family), sent inside `params`, not a gating input of `tools_run`."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"mcp": "runs_get",
|
|
31
|
+
"cli": "dreamstate runs get <run_id> --json",
|
|
32
|
+
"text": "follows long work; `runs_events` and `runs_list` read more of it and `runs_cancel` stops a run."
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline-read and column-run hand-off, derived from the tools-get capability
|
|
3
|
+
* record. No capability id list: a free synchronous read omits the turn token,
|
|
4
|
+
* and a queued external test run (column runs and the same posture class)
|
|
5
|
+
* returns without waiting.
|
|
6
|
+
*/
|
|
7
|
+
export interface CapabilityPosture {
|
|
8
|
+
inlineRead: boolean;
|
|
9
|
+
returnsRunId: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function postureFromContract(fetched: unknown): CapabilityPosture;
|
|
12
|
+
//# sourceMappingURL=capabilityPosture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilityPosture.d.ts","sourceRoot":"","sources":["../src/capabilityPosture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAaD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAkBvE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Inline-read and column-run hand-off, derived from the tools-get capability
|
|
4
|
+
* record. No capability id list: a free synchronous read omits the turn token,
|
|
5
|
+
* and a queued external test run (column runs and the same posture class)
|
|
6
|
+
* returns without waiting.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.postureFromContract = postureFromContract;
|
|
10
|
+
function record(value) {
|
|
11
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
12
|
+
}
|
|
13
|
+
function capabilityOf(fetched) {
|
|
14
|
+
const root = record(fetched);
|
|
15
|
+
if (!root)
|
|
16
|
+
return null;
|
|
17
|
+
const data = record(root.data);
|
|
18
|
+
return record(data?.capability) ?? record(root.capability);
|
|
19
|
+
}
|
|
20
|
+
function postureFromContract(fetched) {
|
|
21
|
+
const capability = capabilityOf(fetched);
|
|
22
|
+
if (!capability)
|
|
23
|
+
return { inlineRead: false, returnsRunId: false };
|
|
24
|
+
const asyncPolicy = record(capability.async_policy);
|
|
25
|
+
const asyncMode = typeof asyncPolicy?.mode === 'string' ? asyncPolicy.mode : undefined;
|
|
26
|
+
const credit = typeof capability.credit_class === 'string' ? capability.credit_class : undefined;
|
|
27
|
+
const consequence = typeof capability.consequence === 'string' ? capability.consequence : undefined;
|
|
28
|
+
const effect = typeof capability.effect_class === 'string' ? capability.effect_class : undefined;
|
|
29
|
+
// Missing async_policy is unknown. Do not guess sync, and do not treat it as a hand-off.
|
|
30
|
+
const inlineRead = capability.mutates !== true
|
|
31
|
+
&& asyncMode === 'sync'
|
|
32
|
+
&& (credit === 'free' || credit === undefined)
|
|
33
|
+
&& (consequence === 'read_only' || consequence === 'test_run');
|
|
34
|
+
const returnsRunId = asyncMode === 'async'
|
|
35
|
+
&& consequence === 'test_run'
|
|
36
|
+
&& credit === 'dynamic'
|
|
37
|
+
&& effect === 'external';
|
|
38
|
+
return { inlineRead, returnsRunId };
|
|
39
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
export declare const CLI_VERSION: string;
|
|
2
|
+
/** Command that updates the CLI. One constant so every surface prints the same one. */
|
|
3
|
+
export declare const CLI_UPDATE_COMMAND = "npm install -g @trydreamstate/cli@latest";
|
|
4
|
+
/**
|
|
5
|
+
* Release identity the server stamps on every /api/v1 response (contract C2):
|
|
6
|
+
* meta.server_version, meta.minimum_cli_version, meta.release_sha and
|
|
7
|
+
* meta.registry_hash in the JSON envelope (whoami also puts registry_hash on
|
|
8
|
+
* data), and X-Dreamstate-Server-Version / -Minimum-Cli-Version / -Release-Sha
|
|
9
|
+
* / -Registry-Hash as response headers. The client reads both so a server that
|
|
10
|
+
* only sets one of them (a proxy that rewrites bodies, an error page with no
|
|
11
|
+
* envelope) still reports skew.
|
|
12
|
+
*/
|
|
13
|
+
export interface ServerReleaseInfo {
|
|
14
|
+
server_version: string | null;
|
|
15
|
+
minimum_cli_version: string | null;
|
|
16
|
+
release_sha: string | null;
|
|
17
|
+
registry_hash: string | null;
|
|
18
|
+
}
|
|
2
19
|
export interface DreamstateClientOptions {
|
|
3
20
|
apiKey: string;
|
|
4
21
|
baseUrl: string;
|
|
@@ -37,7 +54,6 @@ export interface WhoamiWorkspace {
|
|
|
37
54
|
export interface CapabilityRunBody {
|
|
38
55
|
params: Record<string, unknown>;
|
|
39
56
|
mode?: 'smoke_test' | 'dry_run' | 'live';
|
|
40
|
-
agent_session_id?: string;
|
|
41
57
|
approved?: boolean;
|
|
42
58
|
max_credits?: number;
|
|
43
59
|
[key: string]: unknown;
|
|
@@ -71,6 +87,8 @@ export declare class CliError extends Error {
|
|
|
71
87
|
/** The server's retry decision and repair, forwarded unchanged so the CLI never re-derives them. */
|
|
72
88
|
readonly retryable?: boolean;
|
|
73
89
|
readonly repair?: unknown;
|
|
90
|
+
/** The server's own envelope meta (trace_id, server_version, ...), kept whole so a refusal prints what the server said. */
|
|
91
|
+
readonly meta?: Record<string, unknown>;
|
|
74
92
|
constructor(message: string, code: string, status: number, options?: {
|
|
75
93
|
details?: unknown;
|
|
76
94
|
retryAfter?: string;
|
|
@@ -78,8 +96,18 @@ export declare class CliError extends Error {
|
|
|
78
96
|
idempotencyKey?: string;
|
|
79
97
|
retryable?: boolean;
|
|
80
98
|
repair?: unknown;
|
|
99
|
+
meta?: Record<string, unknown>;
|
|
81
100
|
});
|
|
82
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Release identity from one response, preferring the JSON envelope and falling
|
|
104
|
+
* back to the X-Dreamstate-* headers. Success and error envelopes carry it
|
|
105
|
+
* alike (contract C2), so this runs on both.
|
|
106
|
+
*/
|
|
107
|
+
export declare function readServerReleaseInfo(payload: unknown, headers?: Headers): ServerReleaseInfo;
|
|
108
|
+
/** How far this CLI is from the server it just talked to. */
|
|
109
|
+
export type VersionSkew = 'compatible' | 'cli_behind_server' | 'cli_below_minimum' | 'unknown';
|
|
110
|
+
export declare function describeVersionSkew(info: ServerReleaseInfo, cliVersion?: string): VersionSkew;
|
|
83
111
|
export declare function readToolEnvelopeTraceId(value: unknown): string | undefined;
|
|
84
112
|
export declare function requestDeviceAuthorization(baseUrl: string, body: {
|
|
85
113
|
state: string;
|
|
@@ -96,6 +124,9 @@ export declare class DreamstateClient {
|
|
|
96
124
|
private readonly workspace?;
|
|
97
125
|
private readonly requestTimeoutMs?;
|
|
98
126
|
private readonly onVersionWarning?;
|
|
127
|
+
private releaseInfo;
|
|
128
|
+
/** Release identity from the most recent response, success or error. */
|
|
129
|
+
get serverInfo(): ServerReleaseInfo;
|
|
99
130
|
constructor(options: DreamstateClientOptions);
|
|
100
131
|
getWhoami(): Promise<unknown>;
|
|
101
132
|
/** Status reports server compatibility, including a requirement this CLI cannot satisfy. */
|
|
@@ -113,9 +144,9 @@ export declare class DreamstateClient {
|
|
|
113
144
|
runCapability(capabilityId: string, body: CapabilityRunBody, idempotencyKey?: string | null): Promise<unknown>;
|
|
114
145
|
getRun(id: string): Promise<unknown>;
|
|
115
146
|
listRuns(query?: Record<string, unknown>): Promise<unknown>;
|
|
116
|
-
resumeRun(id: string, body: Record<string, unknown>, idempotencyKey?: string): Promise<unknown>;
|
|
117
147
|
getRunEvents(id: string, query?: Record<string, unknown>): Promise<unknown>;
|
|
118
148
|
cancelRun(id: string, body: Record<string, unknown>, idempotencyKey?: string): Promise<unknown>;
|
|
149
|
+
private noteServerInfo;
|
|
119
150
|
private request;
|
|
120
151
|
}
|
|
121
152
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DreamstateClient = exports.CliError = exports.TOOL_AND_RUN_REQUEST_TIMEOUT_MS = exports.DEFAULT_REQUEST_TIMEOUT_MS = exports.CLI_VERSION = void 0;
|
|
3
|
+
exports.DreamstateClient = exports.CliError = exports.TOOL_AND_RUN_REQUEST_TIMEOUT_MS = exports.DEFAULT_REQUEST_TIMEOUT_MS = exports.CLI_UPDATE_COMMAND = exports.CLI_VERSION = void 0;
|
|
4
|
+
exports.readServerReleaseInfo = readServerReleaseInfo;
|
|
5
|
+
exports.describeVersionSkew = describeVersionSkew;
|
|
4
6
|
exports.readToolEnvelopeTraceId = readToolEnvelopeTraceId;
|
|
5
7
|
exports.requestDeviceAuthorization = requestDeviceAuthorization;
|
|
6
8
|
exports.claimDeviceAuthorization = claimDeviceAuthorization;
|
|
7
|
-
const
|
|
9
|
+
const idempotency_1 = require("./idempotency");
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
const registryManifest_1 = require("./registryManifest");
|
|
8
12
|
exports.CLI_VERSION = require('../package.json').version;
|
|
13
|
+
/** Command that updates the CLI. One constant so every surface prints the same one. */
|
|
14
|
+
exports.CLI_UPDATE_COMMAND = 'npm install -g @trydreamstate/cli@latest';
|
|
9
15
|
exports.DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
10
16
|
exports.TOOL_AND_RUN_REQUEST_TIMEOUT_MS = 60_000;
|
|
11
|
-
/**
|
|
12
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Next steps for server workspace refusals. The server's message is forwarded
|
|
19
|
+
* unchanged; the CLI's own guidance travels beside it as details.cli_hint, so
|
|
20
|
+
* an agent reading either surface sees the same message and a script can tell
|
|
21
|
+
* what the server said from what the CLI added.
|
|
22
|
+
*/
|
|
23
|
+
const WORKSPACE_ERROR_HINTS = {
|
|
13
24
|
workspace_required: 'No workspace is selected. Run dreamstate workspaces list, then dreamstate workspaces use <workspace> (or pass --workspace <id|slug>).',
|
|
14
25
|
workspace_forbidden: 'You are not a member of that workspace. Run dreamstate workspaces list to see the workspaces you can use, then dreamstate workspaces use <workspace>.',
|
|
15
26
|
workspace_scoped_credential: 'This key is bound to one workspace. Run dreamstate login for a user session that can switch, or remove --workspace and DREAMSTATE_WORKSPACE.',
|
|
@@ -24,6 +35,8 @@ class CliError extends Error {
|
|
|
24
35
|
/** The server's retry decision and repair, forwarded unchanged so the CLI never re-derives them. */
|
|
25
36
|
retryable;
|
|
26
37
|
repair;
|
|
38
|
+
/** The server's own envelope meta (trace_id, server_version, ...), kept whole so a refusal prints what the server said. */
|
|
39
|
+
meta;
|
|
27
40
|
constructor(message, code, status, options = {}) {
|
|
28
41
|
super(message);
|
|
29
42
|
this.name = 'CliError';
|
|
@@ -41,9 +54,52 @@ class CliError extends Error {
|
|
|
41
54
|
this.retryable = options.retryable;
|
|
42
55
|
if (options.repair !== undefined)
|
|
43
56
|
this.repair = options.repair;
|
|
57
|
+
else if (code in errors_1.CLI_LOCAL_ERROR_REPAIRS)
|
|
58
|
+
this.repair = errors_1.CLI_LOCAL_ERROR_REPAIRS[code];
|
|
59
|
+
if (options.meta !== undefined)
|
|
60
|
+
this.meta = options.meta;
|
|
44
61
|
}
|
|
45
62
|
}
|
|
46
63
|
exports.CliError = CliError;
|
|
64
|
+
function objectRecord(value) {
|
|
65
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
66
|
+
}
|
|
67
|
+
function metaRecord(payload) {
|
|
68
|
+
return objectRecord(objectRecord(payload)?.meta);
|
|
69
|
+
}
|
|
70
|
+
function nonEmptyString(value) {
|
|
71
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Release identity from one response, preferring the JSON envelope and falling
|
|
75
|
+
* back to the X-Dreamstate-* headers. Success and error envelopes carry it
|
|
76
|
+
* alike (contract C2), so this runs on both.
|
|
77
|
+
*/
|
|
78
|
+
function readServerReleaseInfo(payload, headers) {
|
|
79
|
+
const meta = metaRecord(payload);
|
|
80
|
+
const header = (name) => nonEmptyString(typeof headers?.get === 'function' ? headers.get(name) : null);
|
|
81
|
+
const data = objectRecord(objectRecord(payload)?.data);
|
|
82
|
+
return {
|
|
83
|
+
server_version: nonEmptyString(meta?.server_version) ?? header('X-Dreamstate-Server-Version'),
|
|
84
|
+
minimum_cli_version: nonEmptyString(meta?.minimum_cli_version) ?? header('X-Dreamstate-Minimum-Cli-Version'),
|
|
85
|
+
release_sha: nonEmptyString(meta?.release_sha) ?? header('X-Dreamstate-Release-Sha'),
|
|
86
|
+
registry_hash: nonEmptyString(meta?.registry_hash) ?? nonEmptyString(data?.registry_hash) ?? header('X-Dreamstate-Registry-Hash'),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function describeVersionSkew(info, cliVersion = exports.CLI_VERSION) {
|
|
90
|
+
if (info.minimum_cli_version && isVersionGreater(info.minimum_cli_version, cliVersion))
|
|
91
|
+
return 'cli_below_minimum';
|
|
92
|
+
if (info.server_version && isVersionGreater(info.server_version, cliVersion))
|
|
93
|
+
return 'cli_behind_server';
|
|
94
|
+
return info.server_version || info.minimum_cli_version ? 'compatible' : 'unknown';
|
|
95
|
+
}
|
|
96
|
+
function updateRequiredError(minimum, cliVersion, status, details, meta) {
|
|
97
|
+
return new CliError(`CLI ${minimum} or newer is required (this is ${cliVersion}). Run: ${exports.CLI_UPDATE_COMMAND}`, 'cli_update_required', status, {
|
|
98
|
+
details: { ...details, minimum_cli_version: minimum, cli_version: cliVersion, update_command: exports.CLI_UPDATE_COMMAND },
|
|
99
|
+
retryable: false,
|
|
100
|
+
...(meta ? { meta } : {}),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
47
103
|
function isApiErrorBody(value) {
|
|
48
104
|
const error = value && typeof value === 'object' ? value.error : null;
|
|
49
105
|
if (!error || typeof error !== 'object')
|
|
@@ -110,24 +166,61 @@ function waitForRetryAfter(value) {
|
|
|
110
166
|
function versionAtMost(current, target) {
|
|
111
167
|
return !isVersionGreater(current, target);
|
|
112
168
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
169
|
+
function errorEnvelope(payload) {
|
|
170
|
+
return isApiErrorBody(payload) ? payload : null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The one parser for an error envelope. request() and the public login
|
|
174
|
+
* requests both use it, so rate-limit receipts, cli_update_required, repair,
|
|
175
|
+
* trace, meta and the idempotency key are not a second copy.
|
|
176
|
+
*/
|
|
177
|
+
function cliErrorFromEnvelope(payload, status, extras = {}) {
|
|
178
|
+
const meta = metaRecord(payload);
|
|
179
|
+
const envelope = errorEnvelope(payload);
|
|
180
|
+
if (envelope) {
|
|
181
|
+
const retryAfterValue = extras.response ? retryAfter(extras.response, envelope) : undefined;
|
|
182
|
+
const repair = envelope.error.repair && typeof envelope.error.repair === 'object' ? { repair: envelope.error.repair } : {};
|
|
183
|
+
if (envelope.error.code === 'cli_update_required') {
|
|
184
|
+
const minimum = extras.info?.minimum_cli_version ?? '';
|
|
185
|
+
const serverDetails = envelope.error.details;
|
|
186
|
+
return new CliError(envelope.error.message, 'cli_update_required', status, {
|
|
187
|
+
details: {
|
|
188
|
+
...(serverDetails && typeof serverDetails === 'object' && !Array.isArray(serverDetails) ? serverDetails : {}),
|
|
189
|
+
...(minimum ? { minimum_cli_version: minimum } : {}),
|
|
190
|
+
cli_version: exports.CLI_VERSION,
|
|
191
|
+
update_command: exports.CLI_UPDATE_COMMAND,
|
|
192
|
+
},
|
|
193
|
+
retryAfter: retryAfterValue,
|
|
194
|
+
traceId: readToolEnvelopeTraceId(payload),
|
|
195
|
+
idempotencyKey: extras.idempotencyKey,
|
|
196
|
+
retryable: false,
|
|
197
|
+
...repair,
|
|
198
|
+
...(meta ? { meta } : {}),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const hint = WORKSPACE_ERROR_HINTS[envelope.error.code];
|
|
202
|
+
const serverDetails = envelope.error.details;
|
|
203
|
+
return new CliError(envelope.error.message, envelope.error.code, status, {
|
|
204
|
+
...(hint
|
|
205
|
+
? { details: { ...(serverDetails && typeof serverDetails === 'object' && !Array.isArray(serverDetails) ? serverDetails : {}), cli_hint: hint } }
|
|
206
|
+
: { details: serverDetails }),
|
|
207
|
+
retryAfter: retryAfterValue,
|
|
208
|
+
traceId: readToolEnvelopeTraceId(payload),
|
|
209
|
+
idempotencyKey: extras.idempotencyKey,
|
|
210
|
+
...(typeof envelope.error.retryable === 'boolean' ? { retryable: envelope.error.retryable } : {}),
|
|
211
|
+
...repair,
|
|
212
|
+
...(meta ? { meta } : {}),
|
|
123
213
|
});
|
|
124
214
|
}
|
|
125
215
|
const error = payload && typeof payload === 'object' ? payload.error : null;
|
|
126
|
-
if (error &&
|
|
127
|
-
typeof error === 'object' &&
|
|
128
|
-
typeof error.code === 'string') {
|
|
216
|
+
if (error && typeof error === 'object' && typeof error.code === 'string') {
|
|
129
217
|
const code = error.code;
|
|
130
|
-
|
|
218
|
+
const message = typeof error.message === 'string' ? error.message : code;
|
|
219
|
+
return new CliError(message, code, status, {
|
|
220
|
+
idempotencyKey: extras.idempotencyKey,
|
|
221
|
+
traceId: readToolEnvelopeTraceId(payload),
|
|
222
|
+
...(meta ? { meta } : {}),
|
|
223
|
+
});
|
|
131
224
|
}
|
|
132
225
|
return null;
|
|
133
226
|
}
|
|
@@ -166,7 +259,7 @@ async function requestPublic(baseUrl, path, init) {
|
|
|
166
259
|
catch {
|
|
167
260
|
throw new CliError(`Dreamstate API returned invalid JSON (HTTP ${response.status})`, 'invalid_response', response.status);
|
|
168
261
|
}
|
|
169
|
-
const apiError =
|
|
262
|
+
const apiError = cliErrorFromEnvelope(payload, response.status, { response });
|
|
170
263
|
if (apiError)
|
|
171
264
|
throw apiError;
|
|
172
265
|
if (!response.ok)
|
|
@@ -182,6 +275,11 @@ class DreamstateClient {
|
|
|
182
275
|
workspace;
|
|
183
276
|
requestTimeoutMs;
|
|
184
277
|
onVersionWarning;
|
|
278
|
+
releaseInfo = { server_version: null, minimum_cli_version: null, release_sha: null, registry_hash: null };
|
|
279
|
+
/** Release identity from the most recent response, success or error. */
|
|
280
|
+
get serverInfo() {
|
|
281
|
+
return { ...this.releaseInfo };
|
|
282
|
+
}
|
|
185
283
|
constructor(options) {
|
|
186
284
|
this.apiKey = options.apiKey;
|
|
187
285
|
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
@@ -240,25 +338,28 @@ class DreamstateClient {
|
|
|
240
338
|
listRuns(query = {}) {
|
|
241
339
|
return this.request(`/runs${queryString(query)}`, {}, exports.TOOL_AND_RUN_REQUEST_TIMEOUT_MS);
|
|
242
340
|
}
|
|
243
|
-
resumeRun(id, body, idempotencyKey) {
|
|
244
|
-
const key = idempotencyKey ?? (0, node_crypto_1.randomUUID)();
|
|
245
|
-
return this.request(`/runs/${encodeURIComponent(id)}/resume`, {
|
|
246
|
-
method: 'POST',
|
|
247
|
-
body: JSON.stringify(body),
|
|
248
|
-
headers: { 'Idempotency-Key': key },
|
|
249
|
-
}, exports.TOOL_AND_RUN_REQUEST_TIMEOUT_MS);
|
|
250
|
-
}
|
|
251
341
|
getRunEvents(id, query = {}) {
|
|
252
342
|
return this.request(`/runs/${encodeURIComponent(id)}/events${queryString(query)}`);
|
|
253
343
|
}
|
|
254
344
|
cancelRun(id, body, idempotencyKey) {
|
|
255
|
-
|
|
345
|
+
// A re-run of the same cancel presents the same key, so a cancel whose reply was
|
|
346
|
+
// lost replays instead of racing the run's own state version.
|
|
347
|
+
const key = idempotencyKey ?? (0, idempotency_1.deterministicIdempotencyKey)({ scope: 'runs.cancel', workspace: this.workspace, body: { run_id: id, ...body } });
|
|
256
348
|
return this.request(`/runs/${encodeURIComponent(id)}/cancel`, {
|
|
257
349
|
method: 'POST',
|
|
258
350
|
body: JSON.stringify(body),
|
|
259
|
-
|
|
351
|
+
headers: { 'Idempotency-Key': key },
|
|
260
352
|
});
|
|
261
353
|
}
|
|
354
|
+
noteServerInfo(info) {
|
|
355
|
+
(0, registryManifest_1.noteRegistryHash)(info.registry_hash);
|
|
356
|
+
this.releaseInfo = {
|
|
357
|
+
server_version: info.server_version ?? this.releaseInfo.server_version,
|
|
358
|
+
minimum_cli_version: info.minimum_cli_version ?? this.releaseInfo.minimum_cli_version,
|
|
359
|
+
release_sha: info.release_sha ?? this.releaseInfo.release_sha,
|
|
360
|
+
registry_hash: info.registry_hash ?? this.releaseInfo.registry_hash,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
262
363
|
async request(path, init = {}, defaultTimeoutMs = exports.DEFAULT_REQUEST_TIMEOUT_MS, allowIncompatibleCli = false) {
|
|
263
364
|
const requestTimeoutMs = this.requestTimeoutMs ?? defaultTimeoutMs;
|
|
264
365
|
let response;
|
|
@@ -283,9 +384,9 @@ class DreamstateClient {
|
|
|
283
384
|
}
|
|
284
385
|
catch (error) {
|
|
285
386
|
if (controller.signal.aborted) {
|
|
286
|
-
throw new CliError(
|
|
387
|
+
throw new CliError(errors_1.CLI_LOCAL_ERROR_CODES.request_timeout, 'request_timeout', 0, { idempotencyKey, repair: errors_1.CLI_LOCAL_ERROR_REPAIRS.request_timeout });
|
|
287
388
|
}
|
|
288
|
-
throw new CliError(`Request failed: ${error instanceof Error ? error.message : String(error)}`, 'network_error', 0);
|
|
389
|
+
throw new CliError(`Request failed: ${error instanceof Error ? error.message : String(error)}`, 'network_error', 0, { repair: errors_1.CLI_LOCAL_ERROR_REPAIRS.network_error });
|
|
289
390
|
}
|
|
290
391
|
finally {
|
|
291
392
|
clearTimeout(timeout);
|
|
@@ -295,48 +396,38 @@ class DreamstateClient {
|
|
|
295
396
|
payload = await response.json();
|
|
296
397
|
}
|
|
297
398
|
catch {
|
|
298
|
-
|
|
399
|
+
this.noteServerInfo(readServerReleaseInfo(undefined, response.headers));
|
|
400
|
+
throw new CliError(`Dreamstate API returned invalid JSON (HTTP ${response.status})`, 'invalid_response', response.status, { idempotencyKey, repair: errors_1.CLI_LOCAL_ERROR_REPAIRS.invalid_response });
|
|
299
401
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
402
|
+
const info = readServerReleaseInfo(payload, response.headers);
|
|
403
|
+
this.noteServerInfo(info);
|
|
404
|
+
const meta = metaRecord(payload);
|
|
405
|
+
const envelopeError = errorEnvelope(payload);
|
|
406
|
+
if (envelopeError && response.status === 429 && !retriedRateLimit) {
|
|
407
|
+
const retryAfterValue = retryAfter(response, envelopeError);
|
|
408
|
+
if (retryAfterValue && retryAfterDelayMs(retryAfterValue) !== null) {
|
|
306
409
|
retriedRateLimit = true;
|
|
307
410
|
await waitForRetryAfter(retryAfterValue);
|
|
308
411
|
continue;
|
|
309
412
|
}
|
|
310
|
-
throw new CliError(WORKSPACE_ERROR_MESSAGES[payload.error.code] ?? payload.error.message, payload.error.code, response.status, {
|
|
311
|
-
details: payload.error.details,
|
|
312
|
-
retryAfter: retryAfterValue,
|
|
313
|
-
traceId: readToolEnvelopeTraceId(payload),
|
|
314
|
-
idempotencyKey,
|
|
315
|
-
...(typeof payload.error.retryable === 'boolean'
|
|
316
|
-
? { retryable: payload.error.retryable }
|
|
317
|
-
: {}),
|
|
318
|
-
...(payload.error.repair && typeof payload.error.repair === 'object'
|
|
319
|
-
? { repair: payload.error.repair }
|
|
320
|
-
: {}),
|
|
321
|
-
});
|
|
322
413
|
}
|
|
414
|
+
const parsed = cliErrorFromEnvelope(payload, response.status, { idempotencyKey, response, info });
|
|
415
|
+
if (parsed)
|
|
416
|
+
throw parsed;
|
|
323
417
|
if (!response.ok)
|
|
324
|
-
throw new CliError(`Dreamstate API request failed (HTTP ${response.status})`, 'request_failed', response.status);
|
|
325
|
-
const minimum =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
throw new CliError(`CLI ${minimum} or newer is required. Run: npm install -g @trydreamstate/cli@latest`, 'cli_update_required', 0);
|
|
418
|
+
throw new CliError(`Dreamstate API request failed (HTTP ${response.status})`, 'request_failed', response.status, { idempotencyKey, ...(meta ? { meta } : {}) });
|
|
419
|
+
const minimum = info.minimum_cli_version;
|
|
420
|
+
if (!allowIncompatibleCli && minimum && isVersionGreater(minimum, exports.CLI_VERSION)) {
|
|
421
|
+
// A conforming server refuses first (C3), so reaching here means the
|
|
422
|
+
// request may already have executed. The body is kept in the error so a
|
|
423
|
+
// write's receipt is not lost behind the update notice.
|
|
424
|
+
throw updateRequiredError(minimum, exports.CLI_VERSION, 0, init.method && init.method !== 'GET' ? { response: payload } : undefined, meta);
|
|
332
425
|
}
|
|
333
|
-
const server =
|
|
334
|
-
|
|
335
|
-
: undefined;
|
|
336
|
-
if (typeof server === 'string' &&
|
|
426
|
+
const server = info.server_version;
|
|
427
|
+
if (server &&
|
|
337
428
|
isVersionGreater(server, exports.CLI_VERSION) &&
|
|
338
|
-
(
|
|
339
|
-
this.onVersionWarning?.(`Warning: CLI ${exports.CLI_VERSION} is behind server ${server}. Proceeding; server minimum is ${minimum ?? 'not declared'}
|
|
429
|
+
(!minimum || versionAtMost(minimum, exports.CLI_VERSION))) {
|
|
430
|
+
this.onVersionWarning?.(`Warning: CLI ${exports.CLI_VERSION} is behind server ${server}. Proceeding; server minimum is ${minimum ?? 'not declared'}. Update: ${exports.CLI_UPDATE_COMMAND}`);
|
|
340
431
|
}
|
|
341
432
|
return payload;
|
|
342
433
|
}
|