@trydreamstate/cli 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/client.d.ts +12 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +10 -1
- package/dist/config.d.ts +14 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -15
- package/dist/index.d.ts +19 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +140 -20
- package/dist/skillInstaller.d.ts +1 -1
- package/dist/skillInstaller.d.ts.map +1 -1
- package/dist/skillInstaller.js +38 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,9 @@ export DREAMSTATE_API_URL=http://localhost:3001/api/v1
|
|
|
22
22
|
|
|
23
23
|
dreamstate auth whoami
|
|
24
24
|
dreamstate whoami --json
|
|
25
|
+
dreamstate workspaces list
|
|
26
|
+
dreamstate workspaces use <id|slug>
|
|
27
|
+
dreamstate packs list --workspace <id|slug>
|
|
25
28
|
dreamstate packs list
|
|
26
29
|
dreamstate packs get <pack_id>
|
|
27
30
|
dreamstate commands search "create a workbook and add rows"
|
|
@@ -30,7 +33,9 @@ dreamstate tools search --goal "create a workbook" --limit 50 --toolset tables
|
|
|
30
33
|
dreamstate tools run tables.workbooks.create --params '{"name":"Pipeline"}' --mode live --approved
|
|
31
34
|
```
|
|
32
35
|
|
|
33
|
-
`login --token <ds_live_...>` checks the token looks like a `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.
|
|
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.
|
|
37
|
+
|
|
38
|
+
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.
|
|
34
39
|
|
|
35
40
|
`login` prints approval URL plus confirmation code. Approval page shows same code, then CLI stores granted credential with mode `0600` and prints `whoami`. Default API is `https://api.trydreamstate.com/api/v1`; `DREAMSTATE_API_URL` overrides it. `whoami` and `auth whoami` are aliases. `auth whoami` prints API `registry_hash` unchanged. `packs list`, `packs get`, and `commands search` read authenticated API catalog at request time.
|
|
36
41
|
|
package/dist/client.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export declare const CLI_VERSION: string;
|
|
|
2
2
|
export interface DreamstateClientOptions {
|
|
3
3
|
apiKey: string;
|
|
4
4
|
baseUrl: string;
|
|
5
|
+
/** Sent as X-Dreamstate-Workspace so a user session can act in any workspace it belongs to. */
|
|
6
|
+
workspace?: string;
|
|
5
7
|
requestTimeoutMs?: number;
|
|
6
8
|
onVersionWarning?: (message: string) => void;
|
|
7
9
|
}
|
|
@@ -22,6 +24,15 @@ export interface WhoamiResponse {
|
|
|
22
24
|
subscription_status: string | null;
|
|
23
25
|
api_base_url: string;
|
|
24
26
|
mcp_endpoint_url: string;
|
|
27
|
+
auth_kind?: 'user_session' | 'workspace_key';
|
|
28
|
+
active_workspace_id?: string | null;
|
|
29
|
+
workspaces?: WhoamiWorkspace[];
|
|
30
|
+
}
|
|
31
|
+
export interface WhoamiWorkspace {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
slug: string;
|
|
35
|
+
role: string;
|
|
25
36
|
}
|
|
26
37
|
export interface CapabilityRunBody {
|
|
27
38
|
params: Record<string, unknown>;
|
|
@@ -82,6 +93,7 @@ export declare function claimDeviceAuthorization(baseUrl: string, state: string)
|
|
|
82
93
|
export declare class DreamstateClient {
|
|
83
94
|
private readonly apiKey;
|
|
84
95
|
private readonly baseUrl;
|
|
96
|
+
private readonly workspace?;
|
|
85
97
|
private readonly requestTimeoutMs?;
|
|
86
98
|
private readonly onVersionWarning?;
|
|
87
99
|
constructor(options: DreamstateClientOptions);
|
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,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;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -8,6 +8,12 @@ const node_crypto_1 = require("node:crypto");
|
|
|
8
8
|
exports.CLI_VERSION = require('../package.json').version;
|
|
9
9
|
exports.DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
10
10
|
exports.TOOL_AND_RUN_REQUEST_TIMEOUT_MS = 60_000;
|
|
11
|
+
/** Server workspace refusals rewritten so the message says what to run next. */
|
|
12
|
+
const WORKSPACE_ERROR_MESSAGES = {
|
|
13
|
+
workspace_required: 'No workspace is selected. Run dreamstate workspaces list, then dreamstate workspaces use <workspace> (or pass --workspace <id|slug>).',
|
|
14
|
+
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
|
+
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.',
|
|
16
|
+
};
|
|
11
17
|
class CliError extends Error {
|
|
12
18
|
code;
|
|
13
19
|
status;
|
|
@@ -132,11 +138,13 @@ async function requestPublic(baseUrl, path, init) {
|
|
|
132
138
|
class DreamstateClient {
|
|
133
139
|
apiKey;
|
|
134
140
|
baseUrl;
|
|
141
|
+
workspace;
|
|
135
142
|
requestTimeoutMs;
|
|
136
143
|
onVersionWarning;
|
|
137
144
|
constructor(options) {
|
|
138
145
|
this.apiKey = options.apiKey;
|
|
139
146
|
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
147
|
+
this.workspace = options.workspace;
|
|
140
148
|
this.requestTimeoutMs = options.requestTimeoutMs;
|
|
141
149
|
this.onVersionWarning = options.onVersionWarning;
|
|
142
150
|
}
|
|
@@ -210,6 +218,7 @@ class DreamstateClient {
|
|
|
210
218
|
headers: {
|
|
211
219
|
Accept: 'application/json', Authorization: `Bearer ${this.apiKey}`,
|
|
212
220
|
'X-Dreamstate-Client-Version': exports.CLI_VERSION,
|
|
221
|
+
...(this.workspace ? { 'X-Dreamstate-Workspace': this.workspace } : {}),
|
|
213
222
|
...(init.body ? { 'Content-Type': 'application/json' } : {}), ...init.headers,
|
|
214
223
|
},
|
|
215
224
|
});
|
|
@@ -231,7 +240,7 @@ class DreamstateClient {
|
|
|
231
240
|
throw new CliError(`Dreamstate API returned invalid JSON (HTTP ${response.status})`, 'invalid_response', response.status);
|
|
232
241
|
}
|
|
233
242
|
if (isApiErrorBody(payload)) {
|
|
234
|
-
throw new CliError(payload.error.message, payload.error.code, response.status, {
|
|
243
|
+
throw new CliError(WORKSPACE_ERROR_MESSAGES[payload.error.code] ?? payload.error.message, payload.error.code, response.status, {
|
|
235
244
|
details: payload.error.details, retryAfter: retryAfter(response, payload),
|
|
236
245
|
traceId: readToolEnvelopeTraceId(payload), idempotencyKey,
|
|
237
246
|
...(typeof payload.error.retryable === 'boolean' ? { retryable: payload.error.retryable } : {}),
|
package/dist/config.d.ts
CHANGED
|
@@ -19,7 +19,20 @@ export declare class ApiKeyFileError extends Error {
|
|
|
19
19
|
}
|
|
20
20
|
export declare function credentialsPath(homeDirectory?: string, environment?: NodeJS.ProcessEnv): string;
|
|
21
21
|
export declare function resolveApiBaseUrl(environment?: NodeJS.ProcessEnv): string;
|
|
22
|
+
export declare const API_KEY_PREFIXES: readonly ["ds_user_", "ds_live_"];
|
|
23
|
+
export declare function isDreamstateToken(value: string): boolean;
|
|
24
|
+
export interface StoredCredentials {
|
|
25
|
+
token: string;
|
|
26
|
+
workspace: string | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The file is JSON `{token, workspace}`. Files written by 1.0.x hold one bare
|
|
30
|
+
* token line; they keep working so an upgrade never logs anyone out.
|
|
31
|
+
*/
|
|
32
|
+
export declare function readStoredCredentials(path: string): Promise<StoredCredentials | null>;
|
|
22
33
|
export declare function resolveApiKey(environment?: NodeJS.ProcessEnv, path?: string): Promise<string>;
|
|
34
|
+
/** Workspace precedence: --workspace flag, then DREAMSTATE_WORKSPACE, then the stored default. */
|
|
35
|
+
export declare function resolveWorkspace(flag: string | undefined, environment?: NodeJS.ProcessEnv, path?: string): Promise<string | undefined>;
|
|
23
36
|
export declare function readApiKeyFile(path: string): Promise<string>;
|
|
24
|
-
export declare function storeApiKey(apiKey: string, path?: string): Promise<string>;
|
|
37
|
+
export declare function storeApiKey(apiKey: string, path?: string, workspace?: string | null): Promise<string>;
|
|
25
38
|
//# 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,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;;;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,CAMjB;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,CAM7B;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"}
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ApiKeyFileError = exports.MissingApiKeyError = exports.HostPinMismatchError = exports.ApiBaseUrlError = exports.MissingApiUrlError = void 0;
|
|
3
|
+
exports.API_KEY_PREFIXES = exports.ApiKeyFileError = exports.MissingApiKeyError = exports.HostPinMismatchError = exports.ApiBaseUrlError = exports.MissingApiUrlError = void 0;
|
|
4
4
|
exports.credentialsPath = credentialsPath;
|
|
5
5
|
exports.resolveApiBaseUrl = resolveApiBaseUrl;
|
|
6
|
+
exports.isDreamstateToken = isDreamstateToken;
|
|
7
|
+
exports.readStoredCredentials = readStoredCredentials;
|
|
6
8
|
exports.resolveApiKey = resolveApiKey;
|
|
9
|
+
exports.resolveWorkspace = resolveWorkspace;
|
|
7
10
|
exports.readApiKeyFile = readApiKeyFile;
|
|
8
11
|
exports.storeApiKey = storeApiKey;
|
|
9
12
|
const promises_1 = require("node:fs/promises");
|
|
@@ -96,22 +99,60 @@ function isExplicitLoopbackHttpOrigin(candidate, url) {
|
|
|
96
99
|
|| /^127\.0\.0\.1:\d+$/.test(authority)
|
|
97
100
|
|| /^\[::1\]:\d+$/.test(authority);
|
|
98
101
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
exports.API_KEY_PREFIXES = ['ds_user_', 'ds_live_'];
|
|
103
|
+
function isDreamstateToken(value) {
|
|
104
|
+
return exports.API_KEY_PREFIXES.some((prefix) => value.startsWith(prefix) && value.length > prefix.length);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The file is JSON `{token, workspace}`. Files written by 1.0.x hold one bare
|
|
108
|
+
* token line; they keep working so an upgrade never logs anyone out.
|
|
109
|
+
*/
|
|
110
|
+
async function readStoredCredentials(path) {
|
|
111
|
+
let raw;
|
|
104
112
|
try {
|
|
105
|
-
|
|
106
|
-
if (storedKey)
|
|
107
|
-
return storedKey;
|
|
113
|
+
raw = (await (0, promises_1.readFile)(path, 'utf8')).trim();
|
|
108
114
|
}
|
|
109
115
|
catch (error) {
|
|
110
|
-
if (error.code
|
|
111
|
-
|
|
116
|
+
if (['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
|
|
117
|
+
return null;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
if (!raw)
|
|
121
|
+
return null;
|
|
122
|
+
if (!raw.startsWith('{'))
|
|
123
|
+
return { token: raw, workspace: null };
|
|
124
|
+
let parsed;
|
|
125
|
+
try {
|
|
126
|
+
parsed = JSON.parse(raw);
|
|
112
127
|
}
|
|
128
|
+
catch {
|
|
129
|
+
throw new ApiKeyFileError(`Credentials file is not valid JSON: ${path}. Run dreamstate login again.`);
|
|
130
|
+
}
|
|
131
|
+
const record = parsed && typeof parsed === 'object' ? parsed : {};
|
|
132
|
+
if (typeof record.token !== 'string' || !record.token.trim())
|
|
133
|
+
return null;
|
|
134
|
+
const workspace = typeof record.workspace === 'string' && record.workspace.trim() ? record.workspace.trim() : null;
|
|
135
|
+
return { token: record.token.trim(), workspace };
|
|
136
|
+
}
|
|
137
|
+
async function resolveApiKey(environment = process.env, path) {
|
|
138
|
+
const environmentKey = environment.DREAMSTATE_API_KEY?.trim();
|
|
139
|
+
if (environmentKey)
|
|
140
|
+
return environmentKey;
|
|
141
|
+
const stored = await readStoredCredentials(path ?? credentialsPath(undefined, environment));
|
|
142
|
+
if (stored)
|
|
143
|
+
return stored.token;
|
|
113
144
|
throw new MissingApiKeyError();
|
|
114
145
|
}
|
|
146
|
+
/** Workspace precedence: --workspace flag, then DREAMSTATE_WORKSPACE, then the stored default. */
|
|
147
|
+
async function resolveWorkspace(flag, environment = process.env, path) {
|
|
148
|
+
const fromFlag = flag?.trim();
|
|
149
|
+
if (fromFlag)
|
|
150
|
+
return fromFlag;
|
|
151
|
+
const fromEnvironment = environment.DREAMSTATE_WORKSPACE?.trim();
|
|
152
|
+
if (fromEnvironment)
|
|
153
|
+
return fromEnvironment;
|
|
154
|
+
return (await readStoredCredentials(path ?? credentialsPath(undefined, environment)))?.workspace ?? undefined;
|
|
155
|
+
}
|
|
115
156
|
async function readApiKeyFile(path) {
|
|
116
157
|
let file;
|
|
117
158
|
try {
|
|
@@ -131,14 +172,14 @@ async function readApiKeyFile(path) {
|
|
|
131
172
|
}
|
|
132
173
|
return (await (0, promises_1.readFile)(path, 'utf8')).trim();
|
|
133
174
|
}
|
|
134
|
-
async function storeApiKey(apiKey, path = credentialsPath()) {
|
|
175
|
+
async function storeApiKey(apiKey, path = credentialsPath(), workspace = null) {
|
|
135
176
|
const normalized = apiKey.trim();
|
|
136
|
-
if (!normalized
|
|
137
|
-
throw new Error('Dreamstate API keys must start with ds_live_.');
|
|
177
|
+
if (!isDreamstateToken(normalized)) {
|
|
178
|
+
throw new Error('Dreamstate API keys must start with ds_user_ or ds_live_.');
|
|
138
179
|
}
|
|
139
180
|
const directory = (0, node_path_1.join)(path, '..');
|
|
140
181
|
await (0, promises_1.mkdir)(directory, { recursive: true, mode: 0o700 });
|
|
141
|
-
await (0, promises_1.writeFile)(path, `${normalized}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
182
|
+
await (0, promises_1.writeFile)(path, `${JSON.stringify({ token: normalized, workspace })}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
142
183
|
await (0, promises_1.chmod)(path, 0o600);
|
|
143
184
|
return path;
|
|
144
185
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { type ChildProcess } from 'node:child_process';
|
|
3
2
|
import { type SkillInstallAgent } from './skillInstaller';
|
|
4
3
|
export type CanonicalApiCapabilityId = string;
|
|
5
4
|
export declare const CLI_EXIT: {
|
|
@@ -30,6 +29,13 @@ export type CliCommand = {
|
|
|
30
29
|
} | {
|
|
31
30
|
command: 'whoami';
|
|
32
31
|
jsonOutput?: boolean;
|
|
32
|
+
} | {
|
|
33
|
+
command: 'workspaces_list';
|
|
34
|
+
jsonOutput?: boolean;
|
|
35
|
+
} | {
|
|
36
|
+
command: 'workspaces_use';
|
|
37
|
+
workspace: string;
|
|
38
|
+
jsonOutput?: boolean;
|
|
33
39
|
} | {
|
|
34
40
|
command: 'packs_list';
|
|
35
41
|
jsonOutput?: boolean;
|
|
@@ -159,9 +165,20 @@ type SpawnLike = (command: string, args: readonly string[], options: {
|
|
|
159
165
|
detached: boolean;
|
|
160
166
|
stdio: 'ignore';
|
|
161
167
|
shell: false;
|
|
162
|
-
}) =>
|
|
168
|
+
}) => {
|
|
169
|
+
unref(): void;
|
|
170
|
+
on?(event: 'error', listener: (error: Error) => void): unknown;
|
|
171
|
+
};
|
|
163
172
|
export declare function openWebUrl(rawUrl: string, launch?: SpawnLike): void;
|
|
164
173
|
export declare function exitCodeForError(error: unknown): number;
|
|
174
|
+
/**
|
|
175
|
+
* --workspace is global: it is lifted out of argv before any subcommand parser
|
|
176
|
+
* sees it, so every command accepts it without each parser declaring it.
|
|
177
|
+
*/
|
|
178
|
+
export declare function extractWorkspaceFlag(argv: string[]): {
|
|
179
|
+
argv: string[];
|
|
180
|
+
workspace?: string;
|
|
181
|
+
};
|
|
165
182
|
export declare function runCli(argv?: string[], runtime?: CliRuntime): Promise<void>;
|
|
166
183
|
export {};
|
|
167
184
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.toolTurnToken = toolTurnToken;
|
|
|
7
7
|
exports.refusalEnvelope = refusalEnvelope;
|
|
8
8
|
exports.openWebUrl = openWebUrl;
|
|
9
9
|
exports.exitCodeForError = exitCodeForError;
|
|
10
|
+
exports.extractWorkspaceFlag = extractWorkspaceFlag;
|
|
10
11
|
exports.runCli = runCli;
|
|
11
12
|
const node_child_process_1 = require("node:child_process");
|
|
12
13
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -26,9 +27,11 @@ Usage:
|
|
|
26
27
|
dreamstate --help
|
|
27
28
|
dreamstate --version
|
|
28
29
|
dreamstate login
|
|
29
|
-
dreamstate login --token <ds_live_...>
|
|
30
|
+
dreamstate login --token <ds_user_...|ds_live_...>
|
|
30
31
|
dreamstate logout
|
|
31
32
|
dreamstate whoami [--json-output|--json]
|
|
33
|
+
dreamstate workspaces list [--json-output|--json]
|
|
34
|
+
dreamstate workspaces use <id|slug>
|
|
32
35
|
dreamstate auth login [--api-key-file <path>]
|
|
33
36
|
dreamstate auth whoami [--json-output|--json]
|
|
34
37
|
dreamstate packs list [--json-output|--json]
|
|
@@ -53,7 +56,8 @@ Usage:
|
|
|
53
56
|
dreamstate runs cancel <run_id> --json <body> [--idempotency-key <key>] [--json-output|--json]
|
|
54
57
|
|
|
55
58
|
All catalog discovery reads live API records. Set DREAMSTATE_API_URL and DREAMSTATE_API_KEY.
|
|
56
|
-
login opens browser approval; login --token stores a ds_live_ key (for SSH or remote machines) after verifying it with whoami.
|
|
59
|
+
login opens browser approval; login --token stores a ds_user_ session or ds_live_ key (for SSH or remote machines) after verifying it with whoami.
|
|
60
|
+
Global flag --workspace <id|slug> (any command) or DREAMSTATE_WORKSPACE picks the workspace for one call; workspaces use stores a default. Flag beats env beats stored. A ds_live_ key is bound to one workspace and cannot switch.
|
|
57
61
|
logout deletes the stored credentials file. --agents takes space-separated, comma-separated or repeated values.
|
|
58
62
|
`;
|
|
59
63
|
class CliUsageError extends Error {
|
|
@@ -337,7 +341,7 @@ function parseCliArgs(argv) {
|
|
|
337
341
|
if (values.help)
|
|
338
342
|
return { command: 'help' };
|
|
339
343
|
if (positionals.length)
|
|
340
|
-
throw new CliUsageError('Usage: dreamstate login [--token <ds_live_...>]');
|
|
344
|
+
throw new CliUsageError('Usage: dreamstate login [--token <ds_user_...|ds_live_...>]');
|
|
341
345
|
if (values.token !== undefined && values['api-key-file'])
|
|
342
346
|
throw new CliUsageError('Use either --token or --api-key-file, not both.');
|
|
343
347
|
return { command: 'login', ...(values.token !== undefined ? { token: values.token } : {}), ...(values['api-key-file'] ? { apiKeyFile: values['api-key-file'] } : {}), ...outputOption(values) };
|
|
@@ -356,6 +360,16 @@ function parseCliArgs(argv) {
|
|
|
356
360
|
return { command: 'whoami', ...outputOption(values) };
|
|
357
361
|
throw new CliUsageError('Usage: dreamstate whoami [--json-output]');
|
|
358
362
|
}
|
|
363
|
+
if (group === 'workspaces') {
|
|
364
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'json-output': { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
|
|
365
|
+
if (values.help)
|
|
366
|
+
return { command: 'help' };
|
|
367
|
+
if (positionals.length === 1 && positionals[0] === 'list')
|
|
368
|
+
return { command: 'workspaces_list', ...outputOption(values) };
|
|
369
|
+
if (positionals.length === 2 && positionals[0] === 'use' && positionals[1])
|
|
370
|
+
return { command: 'workspaces_use', workspace: positionals[1], ...outputOption(values) };
|
|
371
|
+
throw new CliUsageError('Usage: dreamstate workspaces <list|use <id|slug>>');
|
|
372
|
+
}
|
|
359
373
|
if (group === 'auth') {
|
|
360
374
|
const { values, positionals } = (0, node_util_1.parseArgs)({ args: rest, options: { 'api-key-file': { type: 'string' }, 'json-output': { type: 'boolean' } }, allowPositionals: true, strict: true });
|
|
361
375
|
if (positionals.length === 1 && positionals[0] === 'login')
|
|
@@ -458,7 +472,16 @@ function openWebUrl(rawUrl, launch = node_child_process_1.spawn) {
|
|
|
458
472
|
: process.platform === 'win32'
|
|
459
473
|
? ['rundll32', ['url.dll,FileProtocolHandler', target]]
|
|
460
474
|
: ['xdg-open', [target]];
|
|
461
|
-
|
|
475
|
+
// The URL is always printed first, so a machine with no browser opener (an SSH
|
|
476
|
+
// box, a container) must keep going, not crash on an unhandled spawn error.
|
|
477
|
+
try {
|
|
478
|
+
const child = launch(command, args, { detached: true, stdio: 'ignore', shell: false });
|
|
479
|
+
child.on?.('error', () => undefined);
|
|
480
|
+
child.unref();
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
// Same: opening a browser is a convenience, never a requirement.
|
|
484
|
+
}
|
|
462
485
|
}
|
|
463
486
|
function resultWebUrl(value) {
|
|
464
487
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -558,6 +581,41 @@ function deviceState() {
|
|
|
558
581
|
function runtimeCredentialsPath(runtime) {
|
|
559
582
|
return runtime.credentialsPath ?? (0, config_1.credentialsPath)(undefined, runtime.env ?? process.env);
|
|
560
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* --workspace is global: it is lifted out of argv before any subcommand parser
|
|
586
|
+
* sees it, so every command accepts it without each parser declaring it.
|
|
587
|
+
*/
|
|
588
|
+
function extractWorkspaceFlag(argv) {
|
|
589
|
+
const rest = [];
|
|
590
|
+
let workspace;
|
|
591
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
592
|
+
const argument = argv[index];
|
|
593
|
+
if (argument === '--workspace') {
|
|
594
|
+
const value = argv[index + 1];
|
|
595
|
+
if (value === undefined || value.startsWith('-') || !value.trim())
|
|
596
|
+
throw new CliUsageError('--workspace requires a workspace id or slug.');
|
|
597
|
+
workspace = value.trim();
|
|
598
|
+
index += 1;
|
|
599
|
+
}
|
|
600
|
+
else if (argument.startsWith('--workspace=')) {
|
|
601
|
+
workspace = argument.slice('--workspace='.length).trim();
|
|
602
|
+
if (!workspace)
|
|
603
|
+
throw new CliUsageError('--workspace requires a workspace id or slug.');
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
rest.push(argument);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return { argv: rest, ...(workspace ? { workspace } : {}) };
|
|
610
|
+
}
|
|
611
|
+
function whoamiData(value) {
|
|
612
|
+
const data = value && typeof value === 'object' ? value.data : undefined;
|
|
613
|
+
return data && typeof data === 'object' ? data : {};
|
|
614
|
+
}
|
|
615
|
+
function activeWorkspace(data) {
|
|
616
|
+
const activeId = data.workspace_id ?? data.active_workspace_id ?? undefined;
|
|
617
|
+
return activeId ? data.workspaces?.find((workspace) => workspace.id === activeId) : undefined;
|
|
618
|
+
}
|
|
561
619
|
function wait(milliseconds) {
|
|
562
620
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
563
621
|
}
|
|
@@ -574,7 +632,7 @@ async function loginWithDeviceAuthorization(runtime, stdout, stderr) {
|
|
|
574
632
|
for (;;) {
|
|
575
633
|
const claim = await (0, client_1.claimDeviceAuthorization)(baseUrl, authorization.device_code);
|
|
576
634
|
if (claim.status === 'ready') {
|
|
577
|
-
await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime));
|
|
635
|
+
await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
|
|
578
636
|
const client = new client_1.DreamstateClient({
|
|
579
637
|
baseUrl,
|
|
580
638
|
apiKey: claim.token,
|
|
@@ -605,7 +663,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
605
663
|
ignoreBrokenPipe(stdout);
|
|
606
664
|
ignoreBrokenPipe(stderr);
|
|
607
665
|
try {
|
|
608
|
-
const
|
|
666
|
+
const extracted = extractWorkspaceFlag(argv);
|
|
667
|
+
const command = parseCliArgs(extracted.argv);
|
|
609
668
|
if (command.command === 'help') {
|
|
610
669
|
print({ ok: true, data: { usage: USAGE }, meta: {} }, stdout, false);
|
|
611
670
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
@@ -637,12 +696,16 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
637
696
|
}
|
|
638
697
|
if (command.command === 'login' && command.token !== undefined) {
|
|
639
698
|
const token = command.token.trim();
|
|
640
|
-
if (!
|
|
641
|
-
throw new CliUsageError('--token must be a Dreamstate API key starting with ds_live_.');
|
|
642
|
-
const
|
|
699
|
+
if (!(0, config_1.isDreamstateToken)(token))
|
|
700
|
+
throw new CliUsageError('--token must be a Dreamstate session or API key starting with ds_user_ or ds_live_.');
|
|
701
|
+
const environment = runtime.env ?? process.env;
|
|
702
|
+
const baseUrl = (0, config_1.resolveApiBaseUrl)(environment);
|
|
703
|
+
// Only an explicit flag or env picks the workspace here: a stored default belongs to the previous login.
|
|
704
|
+
const requested = extracted.workspace ?? environment.DREAMSTATE_WORKSPACE?.trim();
|
|
643
705
|
const client = new client_1.DreamstateClient({
|
|
644
706
|
baseUrl,
|
|
645
707
|
apiKey: token,
|
|
708
|
+
...(requested ? { workspace: requested } : {}),
|
|
646
709
|
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
647
710
|
onVersionWarning: (message) => stderr.write(`${message}\n`),
|
|
648
711
|
});
|
|
@@ -655,8 +718,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
655
718
|
if (command.command === 'login') {
|
|
656
719
|
if (command.apiKeyFile || (runtime.env ?? process.env).DREAMSTATE_API_KEY || runtime.promptHidden) {
|
|
657
720
|
const key = await readLoginKey(command, runtime);
|
|
658
|
-
if (!
|
|
659
|
-
throw new CliUsageError('Dreamstate API keys must start with ds_live_.');
|
|
721
|
+
if (!(0, config_1.isDreamstateToken)(key))
|
|
722
|
+
throw new CliUsageError('Dreamstate API keys must start with ds_user_ or ds_live_.');
|
|
660
723
|
const path = await (0, config_1.storeApiKey)(key, runtimeCredentialsPath(runtime));
|
|
661
724
|
print({ ok: true, data: { credentials_path: path }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
662
725
|
stderr.write('Run dreamstate skills install to install Dreamstate skills.\n');
|
|
@@ -673,7 +736,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
673
736
|
const baseUrl = (0, config_1.resolveApiBaseUrl)(environment);
|
|
674
737
|
const credentialFilePath = runtimeCredentialsPath(runtime);
|
|
675
738
|
const apiKey = await (0, config_1.resolveApiKey)(environment, credentialFilePath);
|
|
676
|
-
const
|
|
739
|
+
const workspace = await (0, config_1.resolveWorkspace)(extracted.workspace, environment, credentialFilePath);
|
|
740
|
+
const client = new client_1.DreamstateClient({ baseUrl, apiKey, ...(workspace ? { workspace } : {}), requestTimeoutMs: runtime.requestTimeoutMs });
|
|
677
741
|
await client.listSkills({ limit: 1 });
|
|
678
742
|
const installers = await (0, skillInstaller_1.inspectSkillInstallers)(command.agents, runtime.homeDirectory, Boolean(command.project));
|
|
679
743
|
print({ ok: true, data: { index_reachable: true, installers }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
@@ -726,17 +790,60 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
726
790
|
}
|
|
727
791
|
throw error;
|
|
728
792
|
}
|
|
793
|
+
if (command.command === 'workspaces_use' && apiKey.startsWith('ds_live_')) {
|
|
794
|
+
// Decided locally: a workspace key can never switch, so no request is worth making.
|
|
795
|
+
throw new CliUsageError('This key is bound to one workspace. Run dreamstate login for a user session that can switch.');
|
|
796
|
+
}
|
|
797
|
+
const workspace = command.command === 'workspaces_use'
|
|
798
|
+
? command.workspace
|
|
799
|
+
: await (0, config_1.resolveWorkspace)(extracted.workspace, environment, credentialFilePath);
|
|
729
800
|
const client = new client_1.DreamstateClient({
|
|
730
801
|
baseUrl,
|
|
731
802
|
apiKey,
|
|
803
|
+
...(workspace ? { workspace } : {}),
|
|
732
804
|
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
733
805
|
onVersionWarning: (message) => (runtime.stderr ?? process.stderr).write(`${message}\n`),
|
|
734
806
|
});
|
|
735
807
|
let result;
|
|
736
808
|
switch (command.command) {
|
|
737
|
-
case 'whoami':
|
|
809
|
+
case 'whoami': {
|
|
738
810
|
result = await client.getWhoami();
|
|
811
|
+
if (!command.jsonOutput) {
|
|
812
|
+
const active = activeWorkspace(whoamiData(result));
|
|
813
|
+
if (active)
|
|
814
|
+
stderr.write(`Active workspace: ${active.name} (${active.slug})\n`);
|
|
815
|
+
}
|
|
739
816
|
break;
|
|
817
|
+
}
|
|
818
|
+
case 'workspaces_list': {
|
|
819
|
+
result = await client.getWhoami();
|
|
820
|
+
if (!command.jsonOutput) {
|
|
821
|
+
const data = whoamiData(result);
|
|
822
|
+
const activeId = data.workspace_id ?? data.active_workspace_id ?? null;
|
|
823
|
+
const rows = data.workspaces ?? [];
|
|
824
|
+
stdout.write(rows.length
|
|
825
|
+
? `${rows.map((row) => `${row.id === activeId ? '*' : ' '} ${row.slug} ${row.name} ${row.role} ${row.id}`).join('\n')}\n`
|
|
826
|
+
: 'No workspaces. Ask a workspace owner to invite you.\n');
|
|
827
|
+
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
case 'workspaces_use': {
|
|
833
|
+
const whoami = await client.getWhoami();
|
|
834
|
+
const data = whoamiData(whoami);
|
|
835
|
+
const wanted = command.workspace.toLowerCase();
|
|
836
|
+
const match = data.workspaces?.find((row) => row.id.toLowerCase() === wanted || row.slug.toLowerCase() === wanted);
|
|
837
|
+
if (!match)
|
|
838
|
+
throw new CliUsageError(`You are not a member of workspace "${command.workspace}". Run dreamstate workspaces list to see the workspaces you can use.`);
|
|
839
|
+
const stored = await (0, config_1.readStoredCredentials)(credentialFilePath);
|
|
840
|
+
if (!stored)
|
|
841
|
+
throw new CliUsageError('workspaces use saves the default next to a stored login. Run dreamstate login or dreamstate login --token first.');
|
|
842
|
+
await (0, config_1.storeApiKey)(stored.token, credentialFilePath, match.id);
|
|
843
|
+
stderr.write(`Default workspace: ${match.name} (${match.slug})\n`);
|
|
844
|
+
result = { ok: true, data: { workspace: { id: match.id, name: match.name, slug: match.slug }, credentials_path: credentialFilePath }, meta: {} };
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
740
847
|
case 'packs_list':
|
|
741
848
|
result = await client.listPacks();
|
|
742
849
|
break;
|
|
@@ -766,20 +873,31 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
766
873
|
// A remote id becomes a directory name; refuse traversal before any request or write.
|
|
767
874
|
if (ids.some((id) => !/^[a-z0-9][a-z0-9_-]*$/i.test(id)))
|
|
768
875
|
throw new CliUsageError('Dreamstate API returned an invalid skill id.');
|
|
769
|
-
|
|
876
|
+
// One at a time: each read comes from the workspace's private vault,
|
|
877
|
+
// which admits two reads at once, and fifteen in parallel failed on prod.
|
|
878
|
+
const skills = [];
|
|
879
|
+
for (const id of ids) {
|
|
770
880
|
const skill = hostedSkill(await client.getSkill(id));
|
|
771
881
|
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(skill.id))
|
|
772
882
|
throw new CliUsageError('Dreamstate API returned an invalid skill id.');
|
|
773
883
|
if (skill.id !== id)
|
|
774
884
|
throw new client_1.CliError('Dreamstate API returned a mismatched skill id.', 'invalid_response', 200);
|
|
775
|
-
|
|
776
|
-
}
|
|
885
|
+
skills.push(skill);
|
|
886
|
+
}
|
|
777
887
|
const packs = await client.listPacks();
|
|
888
|
+
const whoami = await client.getWhoami();
|
|
889
|
+
const workspaceId = typeof whoami?.data?.workspace_id === 'string' ? whoami.data.workspace_id : 'unknown';
|
|
890
|
+
// Stamped into every file so a stale install is visible against the live skills.
|
|
891
|
+
const indexVersion = (0, node_crypto_1.createHash)('sha256')
|
|
892
|
+
.update(JSON.stringify(skills.map((skill) => [skill.id, skill.description, skill.body, skill.frontmatter ?? {}])))
|
|
893
|
+
.digest('hex').slice(0, 16);
|
|
778
894
|
const installed = await (0, skillInstaller_1.installSkills)([...skills, (0, skillInstaller_1.routerSkill)(packs)], {
|
|
779
895
|
agents: command.agents,
|
|
780
896
|
homeDirectory: runtime.homeDirectory,
|
|
781
897
|
project: Boolean(command.project),
|
|
782
898
|
copy: Boolean(command.copy) || process.platform === 'win32',
|
|
899
|
+
workspaceId,
|
|
900
|
+
indexVersion,
|
|
783
901
|
});
|
|
784
902
|
result = { ok: true, data: { installed_files: installed.files, skill_ids: [...ids, 'router'], agents: command.agents, ...(command.project ? { lock_file: 'skills-lock.json' } : {}) }, meta: {} };
|
|
785
903
|
break;
|
|
@@ -799,6 +917,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
799
917
|
// forget it once the paid run is admitted, so every other call keeps
|
|
800
918
|
// fetching a fresh turn exactly as before.
|
|
801
919
|
const turns = (0, turnCache_1.turnCachePath)(credentialFilePath);
|
|
920
|
+
// A turn belongs to one workspace, so the workspace is part of the cache identity.
|
|
921
|
+
const turnKey = workspace ? `${apiKey}\n${workspace}` : apiKey;
|
|
802
922
|
let warnedAboutTurnCache = false;
|
|
803
923
|
const warnTurnCache = () => {
|
|
804
924
|
if (warnedAboutTurnCache)
|
|
@@ -808,22 +928,22 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
808
928
|
};
|
|
809
929
|
const pending = command.turnToken
|
|
810
930
|
? null
|
|
811
|
-
: await (0, turnCache_1.reusableTurnToken)(turns, baseUrl,
|
|
931
|
+
: await (0, turnCache_1.reusableTurnToken)(turns, baseUrl, turnKey, command.capabilityId, Date.now(), warnTurnCache);
|
|
812
932
|
const turnToken = command.turnToken ?? pending ?? toolTurnToken(await client.getTool(command.capabilityId));
|
|
813
933
|
try {
|
|
814
934
|
result = await client.runCapability(command.capabilityId, { ...command.body, tool_turn_token: turnToken }, command.idempotencyKey);
|
|
815
935
|
}
|
|
816
936
|
catch (error) {
|
|
817
937
|
if (!command.turnToken && code(error) === 'quote_required') {
|
|
818
|
-
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl,
|
|
938
|
+
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl, turnKey, command.capabilityId, turnToken, Date.now(), warnTurnCache);
|
|
819
939
|
}
|
|
820
940
|
throw error;
|
|
821
941
|
}
|
|
822
942
|
if (!command.turnToken) {
|
|
823
943
|
if (command.body.mode === 'dry_run')
|
|
824
|
-
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl,
|
|
944
|
+
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl, turnKey, command.capabilityId, turnToken, Date.now(), warnTurnCache);
|
|
825
945
|
else if (pending)
|
|
826
|
-
await (0, turnCache_1.forgetTurnToken)(turns, baseUrl,
|
|
946
|
+
await (0, turnCache_1.forgetTurnToken)(turns, baseUrl, turnKey, command.capabilityId, warnTurnCache);
|
|
827
947
|
}
|
|
828
948
|
break;
|
|
829
949
|
}
|
package/dist/skillInstaller.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const SKILL_INSTALL_AGENTS: readonly ["codex", "claude-code", "cursor"];
|
|
1
|
+
export declare const SKILL_INSTALL_AGENTS: readonly ["codex", "claude-code", "cursor", "opencode", "hermes", "openclaw"];
|
|
2
2
|
export type SkillInstallAgent = typeof SKILL_INSTALL_AGENTS[number];
|
|
3
3
|
export interface HostedSkill {
|
|
4
4
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skillInstaller.d.ts","sourceRoot":"","sources":["../src/skillInstaller.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"skillInstaller.d.ts","sourceRoot":"","sources":["../src/skillInstaller.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,oBAAoB,+EAAgF,CAAC;AAClH,MAAM,MAAM,iBAAiB,GAAG,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD,wBAAgB,wBAAwB,CAAC,aAAa,SAAY,EAAE,OAAO,UAAQ,EAAE,GAAG,SAAgB,GAAG,MAAM,CAEhH;AAiBD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,iBAAiB,EAAE,aAAa,SAAY,EAAE,OAAO,UAAQ,EAAE,GAAG,SAAgB,GAAG,MAAM,CAIvI;AA8BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAczH;AA8BD,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,QAAQ,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAgB1N;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,WAAW,CAO3D;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,EAAE,aAAa,SAAY,EAAE,OAAO,UAAQ,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAkC1W"}
|
package/dist/skillInstaller.js
CHANGED
|
@@ -46,18 +46,44 @@ const node_crypto_1 = require("node:crypto");
|
|
|
46
46
|
const node_os_1 = require("node:os");
|
|
47
47
|
const node_path_1 = require("node:path");
|
|
48
48
|
const index_1 = require("./index");
|
|
49
|
-
exports.SKILL_INSTALL_AGENTS = ['codex', 'claude-code', 'cursor'];
|
|
49
|
+
exports.SKILL_INSTALL_AGENTS = ['codex', 'claude-code', 'cursor', 'opencode', 'hermes', 'openclaw'];
|
|
50
50
|
const allowedTools = 'Bash(dreamstate *), Bash(jq *)';
|
|
51
51
|
const safeId = /^[a-z0-9][a-z0-9_-]*$/i;
|
|
52
52
|
function canonicalSkillsDirectory(homeDirectory = (0, node_os_1.homedir)(), project = false, cwd = process.cwd()) {
|
|
53
53
|
return project ? (0, node_path_1.join)(cwd, '.agents', 'skills') : (0, node_path_1.join)(homeDirectory, '.agents', 'skills');
|
|
54
54
|
}
|
|
55
|
+
// One canonical copy in .agents/skills; every other agent's skills directory
|
|
56
|
+
// links back into it, so all agents on a machine see the same skills.
|
|
57
|
+
// Codex and OpenCode read .agents/skills themselves and need no link.
|
|
58
|
+
const AGENT_SKILL_DIRECTORIES = {
|
|
59
|
+
codex: null,
|
|
60
|
+
opencode: null,
|
|
61
|
+
'claude-code': ['.claude', 'skills'],
|
|
62
|
+
cursor: ['.cursor', 'skills'],
|
|
63
|
+
hermes: ['.hermes', 'skills'],
|
|
64
|
+
openclaw: ['.openclaw', 'skills'],
|
|
65
|
+
};
|
|
66
|
+
// Only linked when the agent is already installed (its home folder exists), so
|
|
67
|
+
// installing skills never creates a folder for an agent the person does not use.
|
|
68
|
+
const LINK_ONLY_WHEN_INSTALLED = new Set(['hermes', 'openclaw']);
|
|
55
69
|
function skillInstallDirectory(agent, homeDirectory = (0, node_os_1.homedir)(), project = false, cwd = process.cwd()) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (agent !== 'claude-code')
|
|
70
|
+
const segments = AGENT_SKILL_DIRECTORIES[agent];
|
|
71
|
+
if (!segments)
|
|
59
72
|
return canonicalSkillsDirectory(homeDirectory, project, cwd);
|
|
60
|
-
return (0, node_path_1.join)(project ? cwd : homeDirectory,
|
|
73
|
+
return (0, node_path_1.join)(project ? cwd : homeDirectory, ...segments);
|
|
74
|
+
}
|
|
75
|
+
function linksIntoCanonical(agent) {
|
|
76
|
+
return AGENT_SKILL_DIRECTORIES[agent] !== null;
|
|
77
|
+
}
|
|
78
|
+
async function agentInstalled(agent, homeDirectory, project, cwd) {
|
|
79
|
+
if (!LINK_ONLY_WHEN_INSTALLED.has(agent))
|
|
80
|
+
return true;
|
|
81
|
+
try {
|
|
82
|
+
return (await (0, promises_1.stat)((0, node_path_1.dirname)(skillInstallDirectory(agent, homeDirectory, project, cwd)))).isDirectory();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
61
87
|
}
|
|
62
88
|
function yamlValue(value) { return JSON.stringify(value); }
|
|
63
89
|
function skillName(id) {
|
|
@@ -74,11 +100,12 @@ function packs(frontmatter) {
|
|
|
74
100
|
}
|
|
75
101
|
function renderSkillMarkdown(skill, provenance) {
|
|
76
102
|
const name = skillName(skill.id);
|
|
77
|
-
const source = skill.frontmatter ?? {};
|
|
103
|
+
const { name: _name, description: _description, 'allowed-tools': _tools, ...source } = skill.frontmatter ?? {};
|
|
104
|
+
// name and description lead, as agent hosts read them first.
|
|
78
105
|
const frontmatter = {
|
|
79
|
-
...source,
|
|
80
106
|
name,
|
|
81
107
|
description: skill.description,
|
|
108
|
+
...source,
|
|
82
109
|
'allowed-tools': allowedTools,
|
|
83
110
|
};
|
|
84
111
|
const lines = Object.entries(frontmatter).flatMap(([key, value]) => Array.isArray(value)
|
|
@@ -122,7 +149,9 @@ async function installSkills(skills, options) {
|
|
|
122
149
|
const provenance = { workspaceId: options.workspaceId ?? 'unknown', indexVersion: options.indexVersion ?? 'unknown' };
|
|
123
150
|
const entries = await Promise.all(skills.map((skill) => writeHostedSkill(canonical, skill, provenance)));
|
|
124
151
|
const files = entries.map((entry) => entry.file);
|
|
125
|
-
for (const agent of options.agents.filter(
|
|
152
|
+
for (const agent of options.agents.filter(linksIntoCanonical)) {
|
|
153
|
+
if (!await agentInstalled(agent, home, Boolean(options.project), cwd))
|
|
154
|
+
continue;
|
|
126
155
|
const root = skillInstallDirectory(agent, home, Boolean(options.project), cwd);
|
|
127
156
|
for (const entry of entries)
|
|
128
157
|
files.push(await linkAgentSkill(root, canonical, entry.name, Boolean(options.copy)));
|
|
@@ -161,7 +190,7 @@ async function inspectSkillInstallers(agents, homeDirectory = (0, node_os_1.home
|
|
|
161
190
|
catch { /* unavailable */ }
|
|
162
191
|
}
|
|
163
192
|
const symlinks = [];
|
|
164
|
-
if (agent
|
|
193
|
+
if (linksIntoCanonical(agent)) {
|
|
165
194
|
try {
|
|
166
195
|
for (const name of await (await Promise.resolve().then(() => __importStar(require('node:fs/promises')))).readdir(directory)) {
|
|
167
196
|
const path = (0, node_path_1.join)(directory, name);
|