@trydreamstate/cli 1.0.1 → 1.1.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 +6 -1
- package/dist/client.d.ts +13 -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 +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +116 -16
- package/dist/skillInstaller.d.ts +1 -1
- package/dist/skillInstaller.d.ts.map +1 -1
- package/dist/skillInstaller.js +35 -7
- 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,16 @@ 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
|
+
/** Workspaces created without a url slug report null (found live). */
|
|
35
|
+
slug: string | null;
|
|
36
|
+
role: string | null;
|
|
25
37
|
}
|
|
26
38
|
export interface CapabilityRunBody {
|
|
27
39
|
params: Record<string, unknown>;
|
|
@@ -82,6 +94,7 @@ export declare function claimDeviceAuthorization(baseUrl: string, state: string)
|
|
|
82
94
|
export declare class DreamstateClient {
|
|
83
95
|
private readonly apiKey;
|
|
84
96
|
private readonly baseUrl;
|
|
97
|
+
private readonly workspace?;
|
|
85
98
|
private readonly requestTimeoutMs?;
|
|
86
99
|
private readonly onVersionWarning?;
|
|
87
100
|
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
|
@@ -29,6 +29,13 @@ export type CliCommand = {
|
|
|
29
29
|
} | {
|
|
30
30
|
command: 'whoami';
|
|
31
31
|
jsonOutput?: boolean;
|
|
32
|
+
} | {
|
|
33
|
+
command: 'workspaces_list';
|
|
34
|
+
jsonOutput?: boolean;
|
|
35
|
+
} | {
|
|
36
|
+
command: 'workspaces_use';
|
|
37
|
+
workspace: string;
|
|
38
|
+
jsonOutput?: boolean;
|
|
32
39
|
} | {
|
|
33
40
|
command: 'packs_list';
|
|
34
41
|
jsonOutput?: boolean;
|
|
@@ -164,6 +171,14 @@ type SpawnLike = (command: string, args: readonly string[], options: {
|
|
|
164
171
|
};
|
|
165
172
|
export declare function openWebUrl(rawUrl: string, launch?: SpawnLike): void;
|
|
166
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
|
+
};
|
|
167
182
|
export declare function runCli(argv?: string[], runtime?: CliRuntime): Promise<void>;
|
|
168
183
|
export {};
|
|
169
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')
|
|
@@ -567,6 +581,41 @@ function deviceState() {
|
|
|
567
581
|
function runtimeCredentialsPath(runtime) {
|
|
568
582
|
return runtime.credentialsPath ?? (0, config_1.credentialsPath)(undefined, runtime.env ?? process.env);
|
|
569
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
|
+
}
|
|
570
619
|
function wait(milliseconds) {
|
|
571
620
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
572
621
|
}
|
|
@@ -583,7 +632,7 @@ async function loginWithDeviceAuthorization(runtime, stdout, stderr) {
|
|
|
583
632
|
for (;;) {
|
|
584
633
|
const claim = await (0, client_1.claimDeviceAuthorization)(baseUrl, authorization.device_code);
|
|
585
634
|
if (claim.status === 'ready') {
|
|
586
|
-
await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime));
|
|
635
|
+
await (0, config_1.storeApiKey)(claim.token, runtimeCredentialsPath(runtime), claim.workspace.id);
|
|
587
636
|
const client = new client_1.DreamstateClient({
|
|
588
637
|
baseUrl,
|
|
589
638
|
apiKey: claim.token,
|
|
@@ -614,7 +663,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
614
663
|
ignoreBrokenPipe(stdout);
|
|
615
664
|
ignoreBrokenPipe(stderr);
|
|
616
665
|
try {
|
|
617
|
-
const
|
|
666
|
+
const extracted = extractWorkspaceFlag(argv);
|
|
667
|
+
const command = parseCliArgs(extracted.argv);
|
|
618
668
|
if (command.command === 'help') {
|
|
619
669
|
print({ ok: true, data: { usage: USAGE }, meta: {} }, stdout, false);
|
|
620
670
|
process.exitCode = exports.CLI_EXIT.SUCCESS;
|
|
@@ -646,12 +696,16 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
646
696
|
}
|
|
647
697
|
if (command.command === 'login' && command.token !== undefined) {
|
|
648
698
|
const token = command.token.trim();
|
|
649
|
-
if (!
|
|
650
|
-
throw new CliUsageError('--token must be a Dreamstate API key starting with ds_live_.');
|
|
651
|
-
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();
|
|
652
705
|
const client = new client_1.DreamstateClient({
|
|
653
706
|
baseUrl,
|
|
654
707
|
apiKey: token,
|
|
708
|
+
...(requested ? { workspace: requested } : {}),
|
|
655
709
|
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
656
710
|
onVersionWarning: (message) => stderr.write(`${message}\n`),
|
|
657
711
|
});
|
|
@@ -664,8 +718,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
664
718
|
if (command.command === 'login') {
|
|
665
719
|
if (command.apiKeyFile || (runtime.env ?? process.env).DREAMSTATE_API_KEY || runtime.promptHidden) {
|
|
666
720
|
const key = await readLoginKey(command, runtime);
|
|
667
|
-
if (!
|
|
668
|
-
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_.');
|
|
669
723
|
const path = await (0, config_1.storeApiKey)(key, runtimeCredentialsPath(runtime));
|
|
670
724
|
print({ ok: true, data: { credentials_path: path }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
671
725
|
stderr.write('Run dreamstate skills install to install Dreamstate skills.\n');
|
|
@@ -682,7 +736,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
682
736
|
const baseUrl = (0, config_1.resolveApiBaseUrl)(environment);
|
|
683
737
|
const credentialFilePath = runtimeCredentialsPath(runtime);
|
|
684
738
|
const apiKey = await (0, config_1.resolveApiKey)(environment, credentialFilePath);
|
|
685
|
-
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 });
|
|
686
741
|
await client.listSkills({ limit: 1 });
|
|
687
742
|
const installers = await (0, skillInstaller_1.inspectSkillInstallers)(command.agents, runtime.homeDirectory, Boolean(command.project));
|
|
688
743
|
print({ ok: true, data: { index_reachable: true, installers }, meta: {} }, stdout, Boolean(command.jsonOutput));
|
|
@@ -735,17 +790,60 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
735
790
|
}
|
|
736
791
|
throw error;
|
|
737
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);
|
|
738
800
|
const client = new client_1.DreamstateClient({
|
|
739
801
|
baseUrl,
|
|
740
802
|
apiKey,
|
|
803
|
+
...(workspace ? { workspace } : {}),
|
|
741
804
|
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
742
805
|
onVersionWarning: (message) => (runtime.stderr ?? process.stderr).write(`${message}\n`),
|
|
743
806
|
});
|
|
744
807
|
let result;
|
|
745
808
|
switch (command.command) {
|
|
746
|
-
case 'whoami':
|
|
809
|
+
case 'whoami': {
|
|
747
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 ? ` (${active.slug})` : ''}\n`);
|
|
815
|
+
}
|
|
748
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
|
+
}
|
|
749
847
|
case 'packs_list':
|
|
750
848
|
result = await client.listPacks();
|
|
751
849
|
break;
|
|
@@ -819,6 +917,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
819
917
|
// forget it once the paid run is admitted, so every other call keeps
|
|
820
918
|
// fetching a fresh turn exactly as before.
|
|
821
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;
|
|
822
922
|
let warnedAboutTurnCache = false;
|
|
823
923
|
const warnTurnCache = () => {
|
|
824
924
|
if (warnedAboutTurnCache)
|
|
@@ -828,22 +928,22 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
828
928
|
};
|
|
829
929
|
const pending = command.turnToken
|
|
830
930
|
? null
|
|
831
|
-
: await (0, turnCache_1.reusableTurnToken)(turns, baseUrl,
|
|
931
|
+
: await (0, turnCache_1.reusableTurnToken)(turns, baseUrl, turnKey, command.capabilityId, Date.now(), warnTurnCache);
|
|
832
932
|
const turnToken = command.turnToken ?? pending ?? toolTurnToken(await client.getTool(command.capabilityId));
|
|
833
933
|
try {
|
|
834
934
|
result = await client.runCapability(command.capabilityId, { ...command.body, tool_turn_token: turnToken }, command.idempotencyKey);
|
|
835
935
|
}
|
|
836
936
|
catch (error) {
|
|
837
937
|
if (!command.turnToken && code(error) === 'quote_required') {
|
|
838
|
-
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl,
|
|
938
|
+
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl, turnKey, command.capabilityId, turnToken, Date.now(), warnTurnCache);
|
|
839
939
|
}
|
|
840
940
|
throw error;
|
|
841
941
|
}
|
|
842
942
|
if (!command.turnToken) {
|
|
843
943
|
if (command.body.mode === 'dry_run')
|
|
844
|
-
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl,
|
|
944
|
+
await (0, turnCache_1.rememberTurnToken)(turns, baseUrl, turnKey, command.capabilityId, turnToken, Date.now(), warnTurnCache);
|
|
845
945
|
else if (pending)
|
|
846
|
-
await (0, turnCache_1.forgetTurnToken)(turns, baseUrl,
|
|
946
|
+
await (0, turnCache_1.forgetTurnToken)(turns, baseUrl, turnKey, command.capabilityId, warnTurnCache);
|
|
847
947
|
}
|
|
848
948
|
break;
|
|
849
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) {
|
|
@@ -123,7 +149,9 @@ async function installSkills(skills, options) {
|
|
|
123
149
|
const provenance = { workspaceId: options.workspaceId ?? 'unknown', indexVersion: options.indexVersion ?? 'unknown' };
|
|
124
150
|
const entries = await Promise.all(skills.map((skill) => writeHostedSkill(canonical, skill, provenance)));
|
|
125
151
|
const files = entries.map((entry) => entry.file);
|
|
126
|
-
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;
|
|
127
155
|
const root = skillInstallDirectory(agent, home, Boolean(options.project), cwd);
|
|
128
156
|
for (const entry of entries)
|
|
129
157
|
files.push(await linkAgentSkill(root, canonical, entry.name, Boolean(options.copy)));
|
|
@@ -162,7 +190,7 @@ async function inspectSkillInstallers(agents, homeDirectory = (0, node_os_1.home
|
|
|
162
190
|
catch { /* unavailable */ }
|
|
163
191
|
}
|
|
164
192
|
const symlinks = [];
|
|
165
|
-
if (agent
|
|
193
|
+
if (linksIntoCanonical(agent)) {
|
|
166
194
|
try {
|
|
167
195
|
for (const name of await (await Promise.resolve().then(() => __importStar(require('node:fs/promises')))).readdir(directory)) {
|
|
168
196
|
const path = (0, node_path_1.join)(directory, name);
|