@trydreamstate/cli 1.1.1 → 1.2.1
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 +5 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -3
- package/dist/commands/sequences.d.ts +1 -1
- package/dist/commands/sequences.d.ts.map +1 -1
- package/dist/commands/sequences.js +5 -3
- package/dist/commands/shared.d.ts +2 -1
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/shared.js +104 -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 +37 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +349 -59
- 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 +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ npm install -g @trydreamstate/cli
|
|
|
8
8
|
# Opens browser approval. No API key or prior CLI state needed.
|
|
9
9
|
dreamstate login
|
|
10
10
|
|
|
11
|
+
# Login installs every hosted Dreamstate skill plus router skill for detected Claude Code, Codex, and Cursor installations.
|
|
12
|
+
# Skip automatic installation when needed.
|
|
13
|
+
dreamstate login --no-skills
|
|
14
|
+
|
|
11
15
|
# SSH or remote box with no browser: paste the token minted by "Create session" in the web app.
|
|
12
16
|
dreamstate login --token <ds_live_...>
|
|
13
17
|
|
|
@@ -33,7 +37,7 @@ dreamstate tools search --goal "create a workbook" --limit 50 --toolset tables
|
|
|
33
37
|
dreamstate tools run tables.workbooks.create --params '{"name":"Pipeline"}' --mode live --approved
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
`login --token <ds_user_...|ds_live_...>` checks the token looks like a `ds_user_` session or `ds_live_` key, verifies it with `whoami` against the API, and only then stores it (mode `0600`) and prints the same `whoami` output. The token is never echoed, and a rejected token is not stored. `auth login` keeps `--api-key-file`, stdin and `DREAMSTATE_API_KEY`. `logout` deletes `~/.dreamstate/credentials` (or `$DREAMSTATE_CONFIG_DIR/credentials`), prints what it removed, and exits 0 even when already logged out. `--agents` accepts `codex`, `claude-code` and `cursor`, as `--agents claude-code codex cursor`, `--agents claude-code,codex` or repeated flags, for `skills install` and `skills doctor`. Node 22 or newer is required.
|
|
40
|
+
`login` installs every hosted Dreamstate skill and router skill at user level for detected Claude Code, Codex, and Cursor installations. `login --no-skills` skips this step. If no supported agent is detected or installation fails, login still succeeds and prints `Run dreamstate skills install to install Dreamstate skills.` to stderr. With `--json`, login adds `data.skills` with either installed agents and file count or a skip reason. `login --token <ds_user_...|ds_live_...>` checks the token looks like a `ds_user_` session or `ds_live_` key, verifies it with `whoami` against the API, and only then stores it (mode `0600`) and prints the same `whoami` output. The token is never echoed, and a rejected token is not stored. `auth login` keeps `--api-key-file`, stdin and `DREAMSTATE_API_KEY`. `logout` deletes `~/.dreamstate/credentials` (or `$DREAMSTATE_CONFIG_DIR/credentials`), prints what it removed, and exits 0 even when already logged out. `--agents` accepts `codex`, `claude-code` and `cursor`, as `--agents claude-code codex cursor`, `--agents claude-code,codex` or repeated flags, for `skills install` and `skills doctor`. Node 22 or newer is required.
|
|
37
41
|
|
|
38
42
|
Credentials are stored as JSON `{"token", "workspace"}` (mode `0600`); the single-line file written by 1.0.x is still read. A `ds_user_` session belongs to you and works in every workspace you are a member of. Pick the workspace with `--workspace <id|slug>` (accepted by every command), then `DREAMSTATE_WORKSPACE`, then the stored default, in that order; the choice is sent as the `X-Dreamstate-Workspace` header. `login` stores the workspace you approved as the default. `workspaces list` shows your workspaces and marks the active one (`--json` returns the raw `whoami` envelope), and `workspaces use <id|slug>` verifies membership with `whoami` and stores the default. A `ds_live_` machine key is bound to one workspace: `workspaces use` refuses it, so run `dreamstate login` for a user session that can switch. `whoami` prints the active workspace on stderr.
|
|
39
43
|
|
package/dist/client.d.ts
CHANGED
|
@@ -25,7 +25,6 @@ export interface WhoamiResponse {
|
|
|
25
25
|
api_base_url: string;
|
|
26
26
|
mcp_endpoint_url: string;
|
|
27
27
|
auth_kind?: 'user_session' | 'workspace_key';
|
|
28
|
-
active_workspace_id?: string | null;
|
|
29
28
|
workspaces?: WhoamiWorkspace[];
|
|
30
29
|
}
|
|
31
30
|
export interface WhoamiWorkspace {
|
|
@@ -99,6 +98,9 @@ export declare class DreamstateClient {
|
|
|
99
98
|
private readonly onVersionWarning?;
|
|
100
99
|
constructor(options: DreamstateClientOptions);
|
|
101
100
|
getWhoami(): Promise<unknown>;
|
|
101
|
+
/** Status reports server compatibility, including a requirement this CLI cannot satisfy. */
|
|
102
|
+
getStatusWhoami(): Promise<unknown>;
|
|
103
|
+
getHealth(): Promise<unknown>;
|
|
102
104
|
listPacks(query?: Record<string, unknown>): Promise<unknown>;
|
|
103
105
|
getPack(packId: string, query?: Record<string, unknown>): Promise<unknown>;
|
|
104
106
|
routePack(goal: string): Promise<unknown>;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,EAAE,MAA2C,CAAC;AAEtE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,eAAO,MAAM,0BAA0B,QAAS,CAAC;AACjD,eAAO,MAAM,+BAA+B,QAAS,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,cAAc,GAAG,eAAe,CAAC;IAC7C,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,EAAE,MAA2C,CAAC;AAEtE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,eAAO,MAAM,0BAA0B,QAAS,CAAC;AACjD,eAAO,MAAM,+BAA+B,QAAS,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,cAAc,GAAG,eAAe,CAAC;IAC7C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AASD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,yBAAyB,EAAE,MAAM,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAOhF,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,oGAAoG;IACpG,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO;CAYtM;AASD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQ1E;AA2CD,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACrF,OAAO,CAAC,mBAAmB,CAAC,CAE9B;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAEhH;AA0BD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAA4B;gBAElD,OAAO,EAAE,uBAAuB;IAQ5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAC7B,4FAA4F;IAC5F,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IACnC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAC7B,SAAS,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAChE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAG9E,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGzC,UAAU,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGjE,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAGtE,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAGjC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGhF,SAAS,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGhE,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGpF,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAO9G,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGpC,QAAQ,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAG/D,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO/F,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAG/E,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAQjF,OAAO;CA4DtB"}
|
package/dist/client.js
CHANGED
|
@@ -149,6 +149,9 @@ class DreamstateClient {
|
|
|
149
149
|
this.onVersionWarning = options.onVersionWarning;
|
|
150
150
|
}
|
|
151
151
|
getWhoami() { return this.request('/whoami'); }
|
|
152
|
+
/** Status reports server compatibility, including a requirement this CLI cannot satisfy. */
|
|
153
|
+
getStatusWhoami() { return this.request('/whoami', {}, exports.DEFAULT_REQUEST_TIMEOUT_MS, true); }
|
|
154
|
+
getHealth() { return this.request(new URL('/health', this.baseUrl).toString()); }
|
|
152
155
|
listPacks(query = {}) { return this.request(`/packs${queryString(query)}`); }
|
|
153
156
|
getPack(packId, query = {}) {
|
|
154
157
|
return this.request(`/packs/${encodeURIComponent(packId)}${queryString(query)}`);
|
|
@@ -204,7 +207,7 @@ class DreamstateClient {
|
|
|
204
207
|
...(key ? { headers: { 'Idempotency-Key': key } } : {}),
|
|
205
208
|
});
|
|
206
209
|
}
|
|
207
|
-
async request(path, init = {}, defaultTimeoutMs = exports.DEFAULT_REQUEST_TIMEOUT_MS) {
|
|
210
|
+
async request(path, init = {}, defaultTimeoutMs = exports.DEFAULT_REQUEST_TIMEOUT_MS, allowIncompatibleCli = false) {
|
|
208
211
|
const requestTimeoutMs = this.requestTimeoutMs ?? defaultTimeoutMs;
|
|
209
212
|
const controller = new AbortController();
|
|
210
213
|
const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
@@ -212,7 +215,7 @@ class DreamstateClient {
|
|
|
212
215
|
try {
|
|
213
216
|
const idempotencyKey = new Headers(init.headers).get('Idempotency-Key') ?? undefined;
|
|
214
217
|
try {
|
|
215
|
-
response = await fetch(`${this.baseUrl}${path}`, {
|
|
218
|
+
response = await fetch(path.startsWith('http') ? path : `${this.baseUrl}${path}`, {
|
|
216
219
|
...init,
|
|
217
220
|
signal: controller.signal,
|
|
218
221
|
headers: {
|
|
@@ -250,7 +253,7 @@ class DreamstateClient {
|
|
|
250
253
|
if (!response.ok)
|
|
251
254
|
throw new CliError(`Dreamstate API request failed (HTTP ${response.status})`, 'request_failed', response.status);
|
|
252
255
|
const minimum = payload && typeof payload === 'object' ? payload.meta?.minimum_cli_version : undefined;
|
|
253
|
-
if (typeof minimum === 'string' && isVersionGreater(minimum, exports.CLI_VERSION)) {
|
|
256
|
+
if (!allowIncompatibleCli && typeof minimum === 'string' && isVersionGreater(minimum, exports.CLI_VERSION)) {
|
|
254
257
|
throw new CliError(`CLI ${minimum} or newer is required. Run: npm install -g @trydreamstate/cli@latest`, 'cli_update_required', 0);
|
|
255
258
|
}
|
|
256
259
|
const server = payload && typeof payload === 'object' ? payload.meta?.server_version : undefined;
|
|
@@ -3,5 +3,5 @@ import type { CliCommand } from '../index';
|
|
|
3
3
|
export declare const SEQUENCE_VERBS: PackVerbSpec[];
|
|
4
4
|
export declare const SEQUENCES_USAGE: string;
|
|
5
5
|
export declare const parseSequencesCommand: (argv: string[]) => CliCommand;
|
|
6
|
-
export declare function runSequencesCommand(client: Parameters<typeof runPackCommand>[0], command: CliCommand, stdout: NodeJS.WritableStream): Promise<void>;
|
|
6
|
+
export declare function runSequencesCommand(client: Parameters<typeof runPackCommand>[0], command: CliCommand, stdout: NodeJS.WritableStream, sleep?: Parameters<typeof runPackCommand>[3]): Promise<void>;
|
|
7
7
|
//# sourceMappingURL=sequences.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequences.d.ts","sourceRoot":"","sources":["../../src/commands/sequences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,cAAc,EAAe,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC5G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"sequences.d.ts","sourceRoot":"","sources":["../../src/commands/sequences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,cAAc,EAAe,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC5G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,YAAY,EA+BxC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAqD,CAAC;AACpF,eAAO,MAAM,qBAAqB,gCAA4E,CAAC;AAC/G,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,EAC5C,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,EAC7B,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -4,6 +4,7 @@ exports.parseSequencesCommand = exports.SEQUENCES_USAGE = exports.SEQUENCE_VERBS
|
|
|
4
4
|
exports.runSequencesCommand = runSequencesCommand;
|
|
5
5
|
const shared_1 = require("./shared");
|
|
6
6
|
exports.SEQUENCE_VERBS = [
|
|
7
|
+
{ verb: 'create-from-worksheet', capabilityId: 'sequence.sequences.create_from_worksheet', summary: 'Create draft sequence from worksheet and enroll rows. Never publishes or sends.', flags: { worksheet: { type: 'string', into: 'worksheet_id' }, view: { type: 'string', into: 'view_id' }, name: { type: 'string', into: 'name' }, steps: { type: 'string', into: 'steps', json: true }, description: { type: 'string', into: 'description' }, timezone: { type: 'string', into: 'timezone' }, 'daily-cap': { type: 'string', into: 'daily_cap', number: true }, senders: { type: 'string', into: 'senders', json: true }, 'enrollment-filter': { type: 'string', into: 'enrollment_filter', json: true }, 'max-new-enrollments-per-day': { type: 'string', into: 'max_new_enrollments_per_day', number: true } }, mutating: true },
|
|
7
8
|
{ verb: 'list', capabilityId: 'workflow.automations.list', summary: 'List sequences in workspace.', flags: { limit: { type: 'string', into: 'limit' }, cursor: { type: 'string', into: 'cursor' }, lifecycle: { type: 'string', into: 'lifecycle' }, 'include-archived': { type: 'boolean', into: 'include_archived' }, search: { type: 'string', into: 'search' } }, mutating: false },
|
|
8
9
|
// Local only: writes a starter document to disk and calls no API, so authoring can
|
|
9
10
|
// begin from a file. capabilityId names the verb this scaffold is meant to feed.
|
|
@@ -23,11 +24,12 @@ exports.SEQUENCE_VERBS = [
|
|
|
23
24
|
{ verb: 'field-options', capabilityId: 'workflow.automations.field_options', summary: 'List options for sequence node fields.', positional: [{ name: 'id', into: 'automation_id', optional: true }], flags: { node: { type: 'string', into: 'node_id' }, 'node-kind': { type: 'string', into: 'node_kind' }, subtype: { type: 'string', into: 'subtype' }, 'field-key': { type: 'string', into: 'field_key' }, 'field-keys': { type: 'string', into: 'field_keys', json: true }, query: { type: 'string', into: 'query' }, limit: { type: 'string', into: 'limit' }, cursor: { type: 'string', into: 'cursor' }, 'field-context': { type: 'string', into: 'field_context', json: true } }, mutating: false },
|
|
24
25
|
{ verb: 'publish', capabilityId: 'sequence.sequences.publish', summary: 'Publish sequence draft. Does not enroll or send.', positional: [{ name: 'id', into: 'sequence_id' }], flags: { 'sequence-version-id': { type: 'string', into: 'sequence_version_id' }, 'latest-draft-version-id': { type: 'string', into: 'latest_draft_version_id' } }, mutating: true },
|
|
25
26
|
{ verb: 'enroll', capabilityId: 'sequence.sequences.enroll_selection', summary: 'Enroll rows into sequence. Spends no credits and sends nothing; dispatch only happens at launch.', positional: [{ name: 'id', into: 'sequence_id' }], flags: { worksheet: { type: 'string', into: 'worksheet_id' }, view: { type: 'string', into: 'view_id' }, 'enrollment-filter': { type: 'string', into: 'enrollment_filter', json: true }, 'enrollment-priority': { type: 'string', into: 'enrollment_priority', json: true }, 'enrollment-sort': { type: 'string', into: 'enrollment_sort', json: true }, 'max-live-sends': { type: 'string', into: 'max_live_sends' }, 'max-new-enrollments-per-day': { type: 'string', into: 'max_new_enrollments_per_day' } }, mutating: true },
|
|
27
|
+
{ verb: 'enrollments', capabilityId: 'sequence.sequences.contacts_list', summary: 'List people enrolled in sequence.', positional: [{ name: 'id', into: 'sequence_id' }], flags: { limit: { type: 'string', into: 'limit' }, cursor: { type: 'string', into: 'cursor' } }, mutating: false },
|
|
26
28
|
{ verb: 'launch', capabilityId: 'sequence.sequences.launch', summary: 'Launch published sequence dispatch.', positional: [{ name: 'id', into: 'sequence_id' }], flags: { 'max-live-sends': { type: 'string', into: 'max_live_sends' } }, mutating: true },
|
|
27
29
|
{ verb: 'pause', capabilityId: 'sequence.sequences.pause', summary: 'Pause sequence dispatch. Sends no new messages.', positional: [{ name: 'id', into: 'sequence_id' }], mutating: true },
|
|
28
30
|
{ verb: 'resume', capabilityId: 'sequence.sequences.resume', summary: 'Resume sequence dispatch.', positional: [{ name: 'id', into: 'sequence_id' }], flags: { 'max-live-sends': { type: 'string', into: 'max_live_sends' } }, mutating: true },
|
|
29
31
|
{ verb: 'stats', capabilityId: 'sequence.sequences.step_counts_get', summary: 'Get sequence step counts.', positional: [{ name: 'id', into: 'sequence_id' }], mutating: false },
|
|
30
|
-
{ verb: 'analytics', capabilityId: 'sequence.sequences.analytics_get', summary: 'Get sequence analytics.', positional: [{ name: 'id', into: 'sequence_id' }], flags: {
|
|
32
|
+
{ verb: 'analytics', capabilityId: 'sequence.sequences.analytics_get', summary: 'Get sequence analytics.', positional: [{ name: 'id', into: 'sequence_id' }], flags: { 'window-days': { type: 'string', into: 'window_days', number: true } }, mutating: false },
|
|
31
33
|
{ verb: 'duplicate', capabilityId: 'workflow.automations.duplicate', summary: 'Duplicate sequence as draft. Does not publish or send.', positional: [{ name: 'id', into: 'automation_id' }], flags: { name: { type: 'string', into: 'name' }, 'expected-version': { type: 'string', into: 'expected_version', number: true } }, mutating: true },
|
|
32
34
|
{ verb: 'archive', capabilityId: 'workflow.automations.archive', summary: 'Archive sequence. Does not delete records or send.', positional: [{ name: 'id', into: 'automation_id' }], flags: { reason: { type: 'string', into: 'reason' }, 'expected-version': { type: 'string', into: 'expected_version', number: true } }, mutating: true },
|
|
33
35
|
{ verb: 'export', capabilityId: 'workflow.automations.export', summary: 'Export sequence definition. Makes no changes.', positional: [{ name: 'id', into: 'automation_id' }], mutating: false },
|
|
@@ -35,10 +37,10 @@ exports.SEQUENCE_VERBS = [
|
|
|
35
37
|
];
|
|
36
38
|
exports.SEQUENCES_USAGE = (0, shared_1.renderPackUsage)('sequences', exports.SEQUENCE_VERBS);
|
|
37
39
|
exports.parseSequencesCommand = (0, shared_1.buildPackParser)('sequences', 'sequence', exports.SEQUENCE_VERBS, exports.SEQUENCES_USAGE);
|
|
38
|
-
async function runSequencesCommand(client, command, stdout) {
|
|
40
|
+
async function runSequencesCommand(client, command, stdout, sleep) {
|
|
39
41
|
if (command.command === 'pack' && command.verb === 'init') {
|
|
40
42
|
(0, shared_1.runPackInit)('sequence', command, stdout);
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
43
|
-
await (0, shared_1.runPackCommand)(client, command, stdout);
|
|
45
|
+
await (0, shared_1.runPackCommand)(client, command, stdout, sleep);
|
|
44
46
|
}
|
|
@@ -36,6 +36,7 @@ export interface PackVerbSpec {
|
|
|
36
36
|
json?: boolean;
|
|
37
37
|
document?: boolean;
|
|
38
38
|
number?: boolean;
|
|
39
|
+
required?: boolean;
|
|
39
40
|
}>;
|
|
40
41
|
mutating: boolean;
|
|
41
42
|
}
|
|
@@ -48,6 +49,6 @@ export declare function buildPackParser(pack: 'sequences' | 'workflows', kind: '
|
|
|
48
49
|
*/
|
|
49
50
|
export declare function packScaffold(kind: 'sequence' | 'workflow', name: string): Record<string, unknown>;
|
|
50
51
|
export declare function runPackInit(kind: 'sequence' | 'workflow', command: CliCommand, stdout: NodeJS.WritableStream): void;
|
|
51
|
-
export declare function runPackCommand(client: DreamstateClient, command: CliCommand, stdout: NodeJS.WritableStream): Promise<void>;
|
|
52
|
+
export declare function runPackCommand(client: DreamstateClient, command: CliCommand, stdout: NodeJS.WritableStream, sleep?: (milliseconds: number) => Promise<void>): Promise<void>;
|
|
52
53
|
export declare function renderPackUsage(pack: string, specs: PackVerbSpec[]): string;
|
|
53
54
|
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/commands/shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAKlD,OAAO,EAA6B,KAAK,UAAU,EAAiC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/commands/shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAKlD,OAAO,EAA6B,KAAK,UAAU,EAAiC,MAAM,UAAU,CAAC;AAOrG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IACpD;;;;;;OAMG;IACH,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAClE;;;;;;;OAOG;IACH;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,QAAQ,GAAC,SAAS,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC7I,QAAQ,EAAE,OAAO,CAAC;CACnB;AAsBD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,WAAW,GAAC,WAAW,EAAE,IAAI,EAAE,UAAU,GAAC,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAC/F,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAU,CAmHhC;AAGD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+BjG;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,GAAG,UAAU,EAC7B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,IAAI,CAuBN;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,KAAK,GAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAiF,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDhQ;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAM3E"}
|
package/dist/commands/shared.js
CHANGED
|
@@ -12,6 +12,7 @@ const node_util_1 = require("node:util");
|
|
|
12
12
|
// audit:canonical-cutover:clean treats importing them as a release blocker.
|
|
13
13
|
const index_1 = require("../index");
|
|
14
14
|
const node_fs_1 = require("node:fs");
|
|
15
|
+
const runWait_1 = require("../runWait");
|
|
15
16
|
const MODES = ['dry_run', 'live', 'smoke_test'];
|
|
16
17
|
function parseObject(raw, flag) {
|
|
17
18
|
try {
|
|
@@ -47,7 +48,7 @@ function buildPackParser(pack, kind, specs, usage) {
|
|
|
47
48
|
const options = {
|
|
48
49
|
mode: { type: 'string' }, 'dry-run': { type: 'boolean' }, live: { type: 'boolean' }, 'agent-session-id': { type: 'string' },
|
|
49
50
|
'idempotency-key': { type: 'string' }, 'max-credits': { type: 'string' }, params: { type: 'string' },
|
|
50
|
-
'json-output': { type: 'boolean' }, open: { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
51
|
+
'wait-timeout': { type: 'string' }, 'no-wait': { type: 'boolean' }, 'json-output': { type: 'boolean' }, open: { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
51
52
|
};
|
|
52
53
|
// A verb that takes a JSON document on a flag also takes it from a file, so a
|
|
53
54
|
// whole authored document can be linted or imported straight off disk instead of
|
|
@@ -63,7 +64,14 @@ function buildPackParser(pack, kind, specs, usage) {
|
|
|
63
64
|
options.file = { type: 'string' };
|
|
64
65
|
for (const [flag, definition] of Object.entries(spec.flags ?? {}))
|
|
65
66
|
options[flag] = { type: definition.type };
|
|
66
|
-
|
|
67
|
+
let values;
|
|
68
|
+
let positionals;
|
|
69
|
+
try {
|
|
70
|
+
({ values, positionals } = (0, node_util_1.parseArgs)({ args: argv.slice(1), options, allowPositionals: true, strict: true }));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new index_1.CliUsageError(`${error instanceof Error ? error.message : 'Invalid arguments.'}\n${usage}`);
|
|
74
|
+
}
|
|
67
75
|
const mode = values.mode;
|
|
68
76
|
const dryRun = values['dry-run'];
|
|
69
77
|
const live = values.live;
|
|
@@ -71,8 +79,13 @@ function buildPackParser(pack, kind, specs, usage) {
|
|
|
71
79
|
const idempotencyKey = values['idempotency-key'];
|
|
72
80
|
const rawParams = values.params;
|
|
73
81
|
const rawMaxCredits = values['max-credits'];
|
|
82
|
+
const rawWaitTimeout = values['wait-timeout'];
|
|
74
83
|
if (values.help)
|
|
75
|
-
|
|
84
|
+
return { command: 'pack_help', usage, ...(values['json-output'] ? { jsonOutput: true } : {}) };
|
|
85
|
+
for (const [flag, definition] of Object.entries(spec.flags ?? {})) {
|
|
86
|
+
if (definition.required && values[flag] === undefined)
|
|
87
|
+
throw new index_1.CliUsageError(`--${flag} is required for ${pack} ${spec.verb}.\n${usage}`);
|
|
88
|
+
}
|
|
76
89
|
const positional = spec.positional ?? [];
|
|
77
90
|
// A document supplied inline or from a file is authored, not saved, so there is no
|
|
78
91
|
// id to name yet. lint and its kin accept a hypothetical document, which is the
|
|
@@ -147,12 +160,16 @@ function buildPackParser(pack, kind, specs, usage) {
|
|
|
147
160
|
const maxCredits = rawMaxCredits === undefined ? undefined : Number(rawMaxCredits);
|
|
148
161
|
if (maxCredits !== undefined && (!Number.isFinite(maxCredits) || maxCredits < 0))
|
|
149
162
|
throw new index_1.CliUsageError('--max-credits must be a non-negative number');
|
|
163
|
+
const waitTimeout = rawWaitTimeout === undefined ? 120 : Number(rawWaitTimeout);
|
|
164
|
+
if (!Number.isFinite(waitTimeout) || waitTimeout < 0)
|
|
165
|
+
throw new index_1.CliUsageError('--wait-timeout must be a non-negative number');
|
|
150
166
|
return {
|
|
151
167
|
command: 'pack', pack, verb: spec.verb, capabilityId: spec.capabilityId, input,
|
|
152
168
|
...(explicitMode ? { mode: explicitMode } : spec.mutating ? { mode: 'dry_run' } : {}),
|
|
153
169
|
...(agentSessionId ? { agentSessionId } : {}),
|
|
154
170
|
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
155
171
|
...(maxCredits === undefined ? {} : { maxCredits }), ...(values['json-output'] ? { jsonOutput: true } : {}),
|
|
172
|
+
waitTimeout, ...(values['no-wait'] ? { noWait: true } : {}),
|
|
156
173
|
...(values.open ? { open: true } : {}),
|
|
157
174
|
};
|
|
158
175
|
};
|
|
@@ -177,19 +194,23 @@ function packScaffold(kind, name) {
|
|
|
177
194
|
steps: [
|
|
178
195
|
{
|
|
179
196
|
id: 'step_1',
|
|
180
|
-
kind: '
|
|
181
|
-
channel: '
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// step lints clean and then fails at create, which is the same trap as before.
|
|
186
|
-
action: 'send_dm',
|
|
187
|
-
note_template: 'Hi {{first_name}}, would love to connect.',
|
|
188
|
-
body_template: 'Thanks for connecting, {{first_name}}.',
|
|
197
|
+
kind: 'email',
|
|
198
|
+
channel: 'email',
|
|
199
|
+
action: 'send_email',
|
|
200
|
+
subject_template: 'Quick question, {{first_name}}',
|
|
201
|
+
body_template: 'Hi {{first_name}},\n\nWould this be useful to you?',
|
|
189
202
|
},
|
|
190
203
|
],
|
|
191
204
|
}
|
|
192
|
-
: {
|
|
205
|
+
: {
|
|
206
|
+
kind: 'workflow',
|
|
207
|
+
name,
|
|
208
|
+
nodes: [
|
|
209
|
+
{ id: 'trigger-root', type: 'trigger', position: { x: 0, y: 0 }, data: { kind: 'trigger', subtype: 'table', config: {} } },
|
|
210
|
+
{ id: 'action-example', type: 'action', position: { x: 0, y: 160 }, data: { kind: 'action', subtype: 'send_connection_request_to_contact', config: {} } },
|
|
211
|
+
],
|
|
212
|
+
edges: [{ id: 'edge-trigger-action', source: 'trigger-root', target: 'action-example' }],
|
|
213
|
+
};
|
|
193
214
|
}
|
|
194
215
|
function runPackInit(kind, command, stdout) {
|
|
195
216
|
if (command.command !== 'pack')
|
|
@@ -216,20 +237,57 @@ function runPackInit(kind, command, stdout) {
|
|
|
216
237
|
},
|
|
217
238
|
}, null, command.jsonOutput ? 0 : 2)}\n`);
|
|
218
239
|
}
|
|
219
|
-
async function runPackCommand(client, command, stdout) {
|
|
240
|
+
async function runPackCommand(client, command, stdout, sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))) {
|
|
220
241
|
if (command.command !== 'pack')
|
|
221
242
|
throw new index_1.CliUsageError('Pack command required.');
|
|
222
243
|
// Every run needs a tool_turn_token from tools_get, exactly as the generic
|
|
223
244
|
// tools run path does. Without it the server refuses capability_not_fetched,
|
|
224
245
|
// which made every pack verb unusable even though its capability id was right.
|
|
225
|
-
const { toolTurnToken } = require('../index');
|
|
226
|
-
|
|
227
|
-
|
|
246
|
+
const { toolTurnToken, capabilityIdempotencyKey } = require('../index');
|
|
247
|
+
if (command.verb === 'activate' && command.input.max_credits === undefined) {
|
|
248
|
+
throw new index_1.CliUsageError('workflows activate requires --max-credits <n>.');
|
|
249
|
+
}
|
|
250
|
+
if (command.verb === 'publish' || command.verb === 'activate') {
|
|
251
|
+
const workflowId = command.input.workflow_id;
|
|
252
|
+
if (typeof workflowId !== 'string')
|
|
253
|
+
throw new index_1.CliUsageError(`workflows ${command.verb} requires <id>.`);
|
|
254
|
+
const readFetched = await client.getTool('workflow.automations.get');
|
|
255
|
+
const current = await client.runCapability('workflow.automations.get', {
|
|
256
|
+
params: { kind: 'workflow', automation_id: workflowId },
|
|
257
|
+
tool_turn_token: toolTurnToken(readFetched),
|
|
258
|
+
}, capabilityIdempotencyKey(readFetched));
|
|
259
|
+
const workflow = workflowFromGet(current);
|
|
260
|
+
if (command.verb === 'publish') {
|
|
261
|
+
command.input.expected_draft_version_id ??= nullableString(workflow.latest_draft_version_id, 'latest_draft_version_id');
|
|
262
|
+
command.input.expected_active_version_id ??= nullableString(workflow.active_version_id, 'active_version_id');
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
command.input.expected_version_id ??= requiredString(workflow.active_version_id, 'active_version_id');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const fetched = await client.getTool(command.capabilityId);
|
|
269
|
+
const turnToken = toolTurnToken(fetched);
|
|
270
|
+
let result = await client.runCapability(command.capabilityId, {
|
|
228
271
|
params: command.input, tool_turn_token: turnToken, ...(command.mode ? { mode: command.mode } : {}),
|
|
229
272
|
...(command.agentSessionId ? { agent_session_id: command.agentSessionId } : {}),
|
|
230
273
|
...(command.maxCredits === undefined ? {} : { max_credits: command.maxCredits }),
|
|
231
|
-
}, command.idempotencyKey);
|
|
232
|
-
|
|
274
|
+
}, command.idempotencyKey ?? capabilityIdempotencyKey(fetched));
|
|
275
|
+
if (!command.noWait && !(0, runWait_1.columnRunReturnsRunId)(command.capabilityId)) {
|
|
276
|
+
const waited = await (0, runWait_1.waitForRun)(client, result, command.waitTimeout, sleep);
|
|
277
|
+
result = waited.value;
|
|
278
|
+
if (waited.timedOut)
|
|
279
|
+
process.exitCode = runWait_1.RUN_WAIT_TIMEOUT_EXIT_CODE;
|
|
280
|
+
}
|
|
281
|
+
const output = command.mode === 'dry_run' && result && typeof result === 'object' && !Array.isArray(result)
|
|
282
|
+
? { ...result, meta: { ...(result.meta ?? {}), dry_run: true, note: 'dry_run: true, rerun with --live to apply' } }
|
|
283
|
+
: result;
|
|
284
|
+
stdout.write(`${JSON.stringify(output, null, command.jsonOutput ? 0 : 2)}\n`);
|
|
285
|
+
if (!command.jsonOutput) {
|
|
286
|
+
const root = result && typeof result === 'object' ? result : {};
|
|
287
|
+
const webUrl = root.web_url ?? root.data?.web_url;
|
|
288
|
+
if (typeof webUrl === 'string')
|
|
289
|
+
stdout.write(`Open: ${webUrl}\n`);
|
|
290
|
+
}
|
|
233
291
|
if (command.open) {
|
|
234
292
|
const root = result && typeof result === 'object' ? result : {};
|
|
235
293
|
const webUrl = root.meta?.web_url ?? root.data?.web_url;
|
|
@@ -239,5 +297,31 @@ async function runPackCommand(client, command, stdout) {
|
|
|
239
297
|
}
|
|
240
298
|
}
|
|
241
299
|
function renderPackUsage(pack, specs) {
|
|
242
|
-
return `Usage: dreamstate ${pack} <verb> [args] [flags]\n\nVerbs:\n${specs.map((spec) =>
|
|
300
|
+
return `Usage: dreamstate ${pack} <verb> [args] [flags]\nRequired positionals use <name:field>. Optional positionals use [name:field]. Flags list target input fields. Run responses wait by default, use --wait-timeout <seconds> (default 120) or --no-wait.\n\nVerbs:\n${specs.map((spec) => {
|
|
301
|
+
const positionals = (spec.positional ?? []).map(({ name, into, optional }) => `${optional ? '[' : '<'}${name}:${into}${optional ? ']' : '>'}`).join(' ');
|
|
302
|
+
const flags = Object.entries(spec.flags ?? {}).map(([flag, definition]) => `--${flag} <${definition.into}>${definition.required ? ' (required)' : ''}`).join(' ');
|
|
303
|
+
return ` ${spec.verb.padEnd(18)} ${[positionals, flags].filter(Boolean).join(' ')}\n${' '.repeat(20)}${spec.summary} Capability: ${spec.capabilityId}`;
|
|
304
|
+
}).join('\n')}`;
|
|
305
|
+
}
|
|
306
|
+
function workflowFromGet(value) {
|
|
307
|
+
const root = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
308
|
+
const data = root.data && typeof root.data === 'object' && !Array.isArray(root.data) ? root.data : {};
|
|
309
|
+
// The canonical door returns { data: { run: { steps: [{ outputs }] } } };
|
|
310
|
+
// outputs holds latest_draft_version_id and active_version_id.
|
|
311
|
+
const run = data.run && typeof data.run === 'object' ? data.run : null;
|
|
312
|
+
const outputs = run?.steps?.[0]?.outputs;
|
|
313
|
+
const workflow = outputs ?? data;
|
|
314
|
+
if (!workflow || typeof workflow !== 'object' || Array.isArray(workflow))
|
|
315
|
+
throw new index_1.CliUsageError('workflow get returned no workflow version data.');
|
|
316
|
+
return workflow;
|
|
317
|
+
}
|
|
318
|
+
function nullableString(value, name) {
|
|
319
|
+
if (value === null || typeof value === 'string')
|
|
320
|
+
return value;
|
|
321
|
+
throw new index_1.CliUsageError(`workflow get returned no ${name}.`);
|
|
322
|
+
}
|
|
323
|
+
function requiredString(value, name) {
|
|
324
|
+
if (typeof value === 'string' && value)
|
|
325
|
+
return value;
|
|
326
|
+
throw new index_1.CliUsageError(`workflow get returned no ${name}; publish workflow before activation.`);
|
|
243
327
|
}
|
|
@@ -3,5 +3,5 @@ import type { CliCommand } from '../index';
|
|
|
3
3
|
export declare const WORKFLOW_VERBS: PackVerbSpec[];
|
|
4
4
|
export declare const WORKFLOWS_USAGE: string;
|
|
5
5
|
export declare const parseWorkflowsCommand: (argv: string[]) => CliCommand;
|
|
6
|
-
export declare function runWorkflowsCommand(client: Parameters<typeof runPackCommand>[0], command: CliCommand, stdout: NodeJS.WritableStream): Promise<void>;
|
|
6
|
+
export declare function runWorkflowsCommand(client: Parameters<typeof runPackCommand>[0], command: CliCommand, stdout: NodeJS.WritableStream, sleep?: Parameters<typeof runPackCommand>[3]): Promise<void>;
|
|
7
7
|
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../src/commands/workflows.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,cAAc,EAEd,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAS3C,eAAO,MAAM,cAAc,EAAE,YAAY,EA8BxC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAqD,CAAC;AACpF,eAAO,MAAM,qBAAqB,gCAA4E,CAAC;AAC/G,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,EAC5C,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../src/commands/workflows.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,cAAc,EAEd,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAS3C,eAAO,MAAM,cAAc,EAAE,YAAY,EA8BxC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAqD,CAAC;AACpF,eAAO,MAAM,qBAAqB,gCAA4E,CAAC;AAC/G,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,EAC5C,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,cAAc,EAC7B,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -27,14 +27,14 @@ exports.WORKFLOW_VERBS = [
|
|
|
27
27
|
{ verb: 'node-remove', capabilityId: 'workflow.automations.node_remove', summary: 'Remove workflow node.', positional: id, flags: { ...workflowVersion, 'node-id': { type: 'string', into: 'node_id' } }, mutating: true },
|
|
28
28
|
{ verb: 'node-move', capabilityId: 'workflow.automations.node_move', summary: 'Move workflow node.', positional: id, flags: { ...workflowVersion, 'node-id': { type: 'string', into: 'node_id' }, 'after-node-id': { type: 'string', into: 'after_node_id' }, branch: { type: 'string', into: 'branch' } }, mutating: true },
|
|
29
29
|
{ verb: 'field-options', capabilityId: 'workflow.automations.field_options', summary: 'Resolve workflow node field options.', flags: { 'node-kind': { type: 'string', into: 'node_kind' }, subtype: { type: 'string', into: 'subtype' }, 'automation-id': { type: 'string', into: 'automation_id' }, 'node-id': { type: 'string', into: 'node_id' }, 'field-key': { type: 'string', into: 'field_key' }, 'field-keys': { type: 'string', into: 'field_keys', json: true }, query: { type: 'string', into: 'query' }, limit: { type: 'string', into: 'limit' }, cursor: { type: 'string', into: 'cursor' }, 'field-context': { type: 'string', into: 'field_context', json: true } }, mutating: false },
|
|
30
|
-
{ verb: 'draft-save', capabilityId: 'workflow.workflows.draft_save', summary: 'Save workflow draft.', positional:
|
|
31
|
-
{ verb: 'publish', capabilityId: 'workflow.workflows.draft_publish', summary: 'Publish workflow draft.', positional:
|
|
32
|
-
{ verb: 'activate', capabilityId: 'workflow.workflows.activate', summary: 'Activate workflow.', positional:
|
|
33
|
-
{ verb: 'deactivate', capabilityId: 'workflow.workflows.deactivate', summary: 'Deactivate workflow.', positional:
|
|
34
|
-
{ verb: 'enroll', capabilityId: 'workflow.workflows.enroll_selection', summary: 'Enroll selection in workflow.', positional:
|
|
30
|
+
{ verb: 'draft-save', capabilityId: 'workflow.workflows.draft_save', summary: 'Save workflow draft.', positional: workflowId, flags: { graph: { type: 'string', into: 'graph', json: true, document: true }, 'expected-latest-draft-version-id': { type: 'string', into: 'expected_latest_draft_version_id' } }, mutating: true },
|
|
31
|
+
{ verb: 'publish', capabilityId: 'workflow.workflows.draft_publish', summary: 'Publish workflow draft.', positional: workflowId, flags: { 'expected-draft-version-id': { type: 'string', into: 'expected_draft_version_id' }, 'expected-active-version-id': { type: 'string', into: 'expected_active_version_id' } }, mutating: true },
|
|
32
|
+
{ verb: 'activate', capabilityId: 'workflow.workflows.activate', summary: 'Activate workflow.', positional: workflowId, flags: { 'expected-version-id': { type: 'string', into: 'expected_version_id' }, 'max-credits': { type: 'string', into: 'max_credits', number: true, required: true }, 'delivery-binding': { type: 'string', into: 'delivery_binding', json: true } }, mutating: true },
|
|
33
|
+
{ verb: 'deactivate', capabilityId: 'workflow.workflows.deactivate', summary: 'Deactivate workflow.', positional: workflowId, mutating: true },
|
|
34
|
+
{ verb: 'enroll', capabilityId: 'workflow.workflows.enroll_selection', summary: 'Enroll saved worksheet selection in workflow.', positional: workflowId, flags: { 'workbook-id': { type: 'string', into: 'workbook_id' }, 'worksheet-id': { type: 'string', into: 'worksheet_id' }, 'saved-view-id': { type: 'string', into: 'saved_view_id' }, 'selection-snapshot-id': { type: 'string', into: 'selection_snapshot_id' }, 'trigger-node-id': { type: 'string', into: 'trigger_node_id' }, 'audience-filter': { type: 'string', into: 'audience_filter', json: true }, 'enrollment-priority': { type: 'string', into: 'enrollment_priority', json: true }, 'enrollment-sort': { type: 'string', into: 'enrollment_sort', json: true }, 'max-live-sends': { type: 'string', into: 'max_live_sends', number: true } }, mutating: true },
|
|
35
35
|
{ verb: 'runs', capabilityId: 'workflow.workflows.runs_list', summary: 'List workflow runs.', positional: workflowId, flags: { workflow: { type: 'string', into: 'workflow_id' }, limit: { type: 'string', into: 'limit' } }, mutating: false },
|
|
36
|
-
{ verb: 'run', capabilityId: 'workflow.workflows.run_trace_get', summary: 'Get workflow run trace.', positional:
|
|
37
|
-
{ verb: 'retry', capabilityId: 'workflow.workflows.run_retry', summary: 'Retry failed workflow run event.', positional:
|
|
36
|
+
{ verb: 'run', capabilityId: 'workflow.workflows.run_trace_get', summary: 'Get workflow run trace.', positional: workflowId, flags: { 'subject-id': { type: 'string', into: 'subject_id' } }, mutating: false },
|
|
37
|
+
{ verb: 'retry', capabilityId: 'workflow.workflows.run_retry', summary: 'Retry failed workflow run event.', positional: workflowId, flags: { 'subject-id': { type: 'string', into: 'subject_id' }, 'failed-event-id': { type: 'string', into: 'failed_event_id' }, 'failed-node-id': { type: 'string', into: 'failed_node_id' } }, mutating: true },
|
|
38
38
|
{ verb: 'duplicate', capabilityId: 'workflow.automations.duplicate', summary: 'Duplicate workflow.', positional: id, flags: { ...workflowVersion, name: { type: 'string', into: 'name' } }, mutating: true },
|
|
39
39
|
{ verb: 'archive', capabilityId: 'workflow.automations.archive', summary: 'Archive workflow.', positional: id, flags: { ...workflowVersion, reason: { type: 'string', into: 'reason' } }, mutating: true },
|
|
40
40
|
{ verb: 'export', capabilityId: 'workflow.automations.export', summary: 'Export workflow.', positional: id, mutating: false },
|
|
@@ -42,10 +42,10 @@ exports.WORKFLOW_VERBS = [
|
|
|
42
42
|
];
|
|
43
43
|
exports.WORKFLOWS_USAGE = (0, shared_1.renderPackUsage)('workflows', exports.WORKFLOW_VERBS);
|
|
44
44
|
exports.parseWorkflowsCommand = (0, shared_1.buildPackParser)('workflows', 'workflow', exports.WORKFLOW_VERBS, exports.WORKFLOWS_USAGE);
|
|
45
|
-
async function runWorkflowsCommand(client, command, stdout) {
|
|
45
|
+
async function runWorkflowsCommand(client, command, stdout, sleep) {
|
|
46
46
|
if (command.command === 'pack' && command.verb === 'init') {
|
|
47
47
|
(0, shared_1.runPackInit)('workflow', command, stdout);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
await (0, shared_1.runPackCommand)(client, command, stdout);
|
|
50
|
+
await (0, shared_1.runPackCommand)(client, command, stdout, sleep);
|
|
51
51
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -25,6 +25,15 @@ export interface StoredCredentials {
|
|
|
25
25
|
token: string;
|
|
26
26
|
workspace: string | null;
|
|
27
27
|
}
|
|
28
|
+
export interface CredentialProfile extends StoredCredentials {
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function validateProfileName(name: string): string;
|
|
32
|
+
export declare function currentCredentialProfile(path?: string, environment?: NodeJS.ProcessEnv): Promise<string | null>;
|
|
33
|
+
export declare function useCredentialProfile(name: string, path?: string): Promise<void>;
|
|
34
|
+
export declare function selectedCredentialsPath(path: string, environment?: NodeJS.ProcessEnv): Promise<string>;
|
|
35
|
+
export declare function listCredentialProfiles(path?: string): Promise<CredentialProfile[]>;
|
|
36
|
+
export declare function removeCredentialProfile(name: string, path?: string): Promise<boolean>;
|
|
28
37
|
/**
|
|
29
38
|
* The file is JSON `{token, workspace}`. Files written by 1.0.x hold one bare
|
|
30
39
|
* token line; they keep working so an upgrade never logs anyone out.
|
|
@@ -35,4 +44,5 @@ export declare function resolveApiKey(environment?: NodeJS.ProcessEnv, path?: st
|
|
|
35
44
|
export declare function resolveWorkspace(flag: string | undefined, environment?: NodeJS.ProcessEnv, path?: string): Promise<string | undefined>;
|
|
36
45
|
export declare function readApiKeyFile(path: string): Promise<string>;
|
|
37
46
|
export declare function storeApiKey(apiKey: string, path?: string, workspace?: string | null): Promise<string>;
|
|
47
|
+
export declare function storeCredentialProfile(name: string, apiKey: string, path?: string, workspace?: string | null): Promise<string>;
|
|
38
48
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,qBAAqB;gBAEtB,OAAO,SAA8C;CAIlE;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,qBAAqB;gBAEtB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,uBAAuB;gBACxB,OAAO,EAAE,MAAM;CAC5B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,uBAAuB;gBAGlC,OAAO,SAA6E;CAKvF;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,eAAe,CAC7B,aAAa,SAAY,EACzB,WAAW,GAAE,MAAM,CAAC,UAAwB,GAC3C,MAAM,CAGR;AAED,wBAAgB,iBAAiB,CAAC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CA+BtF;AAWD,eAAO,MAAM,gBAAgB,mCAAoC,CAAC;AAElE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAoB3F;AAED,wBAAsB,aAAa,CACjC,WAAW,GAAE,MAAM,CAAC,UAAwB,EAC5C,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,qBAAqB;gBAEtB,OAAO,SAA8C;CAIlE;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,qBAAqB;gBAEtB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,uBAAuB;gBACxB,OAAO,EAAE,MAAM;CAC5B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,uBAAuB;gBAGlC,OAAO,SAA6E;CAKvF;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,eAAe,CAC7B,aAAa,SAAY,EACzB,WAAW,GAAE,MAAM,CAAC,UAAwB,GAC3C,MAAM,CAGR;AAED,wBAAgB,iBAAiB,CAAC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CA+BtF;AAWD,eAAO,MAAM,gBAAgB,mCAAoC,CAAC;AAElE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,IAAI,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIxD;AAcD,wBAAsB,wBAAwB,CAAC,IAAI,SAAoB,EAAE,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAU7I;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAShG;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAGzH;AAED,wBAAsB,sBAAsB,CAAC,IAAI,SAAoB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAanG;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CAUtG;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAoB3F;AAED,wBAAsB,aAAa,CACjC,WAAW,GAAE,MAAM,CAAC,UAAwB,EAC5C,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED,kGAAkG;AAClG,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,GAAE,MAAM,CAAC,UAAwB,EAC5C,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBlE;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,SAAoB,EAAE,SAAS,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAW5H;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,SAAoB,EAAE,SAAS,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAIrJ"}
|
package/dist/config.js
CHANGED
|
@@ -4,11 +4,18 @@ exports.API_KEY_PREFIXES = exports.ApiKeyFileError = exports.MissingApiKeyError
|
|
|
4
4
|
exports.credentialsPath = credentialsPath;
|
|
5
5
|
exports.resolveApiBaseUrl = resolveApiBaseUrl;
|
|
6
6
|
exports.isDreamstateToken = isDreamstateToken;
|
|
7
|
+
exports.validateProfileName = validateProfileName;
|
|
8
|
+
exports.currentCredentialProfile = currentCredentialProfile;
|
|
9
|
+
exports.useCredentialProfile = useCredentialProfile;
|
|
10
|
+
exports.selectedCredentialsPath = selectedCredentialsPath;
|
|
11
|
+
exports.listCredentialProfiles = listCredentialProfiles;
|
|
12
|
+
exports.removeCredentialProfile = removeCredentialProfile;
|
|
7
13
|
exports.readStoredCredentials = readStoredCredentials;
|
|
8
14
|
exports.resolveApiKey = resolveApiKey;
|
|
9
15
|
exports.resolveWorkspace = resolveWorkspace;
|
|
10
16
|
exports.readApiKeyFile = readApiKeyFile;
|
|
11
17
|
exports.storeApiKey = storeApiKey;
|
|
18
|
+
exports.storeCredentialProfile = storeCredentialProfile;
|
|
12
19
|
const promises_1 = require("node:fs/promises");
|
|
13
20
|
const node_os_1 = require("node:os");
|
|
14
21
|
const node_path_1 = require("node:path");
|
|
@@ -103,6 +110,85 @@ exports.API_KEY_PREFIXES = ['ds_user_', 'ds_live_'];
|
|
|
103
110
|
function isDreamstateToken(value) {
|
|
104
111
|
return exports.API_KEY_PREFIXES.some((prefix) => value.startsWith(prefix) && value.length > prefix.length);
|
|
105
112
|
}
|
|
113
|
+
const PROFILE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
114
|
+
function validateProfileName(name) {
|
|
115
|
+
const normalized = name.trim();
|
|
116
|
+
if (!PROFILE_NAME.test(normalized))
|
|
117
|
+
throw new ApiKeyFileError('Profile names must use letters, numbers, hyphens, or underscores.');
|
|
118
|
+
return normalized;
|
|
119
|
+
}
|
|
120
|
+
function profileDirectory(path) {
|
|
121
|
+
return (0, node_path_1.join)(path, '..', 'profiles');
|
|
122
|
+
}
|
|
123
|
+
function profilePath(path, name) {
|
|
124
|
+
return (0, node_path_1.join)(profileDirectory(path), validateProfileName(name));
|
|
125
|
+
}
|
|
126
|
+
function currentProfilePath(path) {
|
|
127
|
+
return (0, node_path_1.join)(path, '..', 'current-profile');
|
|
128
|
+
}
|
|
129
|
+
async function currentCredentialProfile(path = credentialsPath(), environment = process.env) {
|
|
130
|
+
const fromEnvironment = environment.DREAMSTATE_PROFILE?.trim();
|
|
131
|
+
if (fromEnvironment)
|
|
132
|
+
return validateProfileName(fromEnvironment);
|
|
133
|
+
try {
|
|
134
|
+
const name = (await (0, promises_1.readFile)(currentProfilePath(path), 'utf8')).trim();
|
|
135
|
+
return name ? validateProfileName(name) : null;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
|
|
139
|
+
return null;
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function useCredentialProfile(name, path = credentialsPath()) {
|
|
144
|
+
const normalized = validateProfileName(name);
|
|
145
|
+
if (!await readStoredCredentials(profilePath(path, normalized))) {
|
|
146
|
+
throw new ApiKeyFileError(`Profile not found: ${normalized}. Run dreamstate login --profile ${normalized} --token <key> first.`);
|
|
147
|
+
}
|
|
148
|
+
const directory = (0, node_path_1.join)(path, '..');
|
|
149
|
+
await (0, promises_1.mkdir)(directory, { recursive: true, mode: 0o700 });
|
|
150
|
+
await (0, promises_1.writeFile)(currentProfilePath(path), `${normalized}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
151
|
+
await (0, promises_1.chmod)(currentProfilePath(path), 0o600);
|
|
152
|
+
}
|
|
153
|
+
async function selectedCredentialsPath(path, environment = process.env) {
|
|
154
|
+
const profile = await currentCredentialProfile(path, environment);
|
|
155
|
+
return profile ? profilePath(path, profile) : path;
|
|
156
|
+
}
|
|
157
|
+
async function listCredentialProfiles(path = credentialsPath()) {
|
|
158
|
+
let names;
|
|
159
|
+
try {
|
|
160
|
+
names = await (0, promises_1.readdir)(profileDirectory(path));
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
|
|
164
|
+
return [];
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
const profiles = await Promise.all(names.sort().map(async (name) => {
|
|
168
|
+
try {
|
|
169
|
+
const stored = await readStoredCredentials(profilePath(path, name));
|
|
170
|
+
return stored ? { name, ...stored } : null;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}));
|
|
176
|
+
return profiles.filter((profile) => profile !== null);
|
|
177
|
+
}
|
|
178
|
+
async function removeCredentialProfile(name, path = credentialsPath()) {
|
|
179
|
+
const normalized = validateProfileName(name);
|
|
180
|
+
try {
|
|
181
|
+
await (0, promises_1.unlink)(profilePath(path, normalized));
|
|
182
|
+
if (await currentCredentialProfile(path) === normalized)
|
|
183
|
+
await (0, promises_1.unlink)(currentProfilePath(path)).catch(() => undefined);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error.code === 'ENOENT')
|
|
188
|
+
return false;
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
106
192
|
/**
|
|
107
193
|
* The file is JSON `{token, workspace}`. Files written by 1.0.x hold one bare
|
|
108
194
|
* token line; they keep working so an upgrade never logs anyone out.
|
|
@@ -138,7 +224,8 @@ async function resolveApiKey(environment = process.env, path) {
|
|
|
138
224
|
const environmentKey = environment.DREAMSTATE_API_KEY?.trim();
|
|
139
225
|
if (environmentKey)
|
|
140
226
|
return environmentKey;
|
|
141
|
-
const
|
|
227
|
+
const credentials = await selectedCredentialsPath(path ?? credentialsPath(undefined, environment), environment);
|
|
228
|
+
const stored = await readStoredCredentials(credentials);
|
|
142
229
|
if (stored)
|
|
143
230
|
return stored.token;
|
|
144
231
|
throw new MissingApiKeyError();
|
|
@@ -151,7 +238,8 @@ async function resolveWorkspace(flag, environment = process.env, path) {
|
|
|
151
238
|
const fromEnvironment = environment.DREAMSTATE_WORKSPACE?.trim();
|
|
152
239
|
if (fromEnvironment)
|
|
153
240
|
return fromEnvironment;
|
|
154
|
-
|
|
241
|
+
const credentials = await selectedCredentialsPath(path ?? credentialsPath(undefined, environment), environment);
|
|
242
|
+
return (await readStoredCredentials(credentials))?.workspace ?? undefined;
|
|
155
243
|
}
|
|
156
244
|
async function readApiKeyFile(path) {
|
|
157
245
|
let file;
|
|
@@ -183,3 +271,8 @@ async function storeApiKey(apiKey, path = credentialsPath(), workspace = null) {
|
|
|
183
271
|
await (0, promises_1.chmod)(path, 0o600);
|
|
184
272
|
return path;
|
|
185
273
|
}
|
|
274
|
+
async function storeCredentialProfile(name, apiKey, path = credentialsPath(), workspace = null) {
|
|
275
|
+
const target = profilePath(path, name);
|
|
276
|
+
await storeApiKey(apiKey, target, workspace);
|
|
277
|
+
return target;
|
|
278
|
+
}
|