coolhand-cli 0.10.0 → 0.11.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 +7 -0
- package/dist/api/last-sync.d.ts.map +1 -1
- package/dist/api/last-sync.js +2 -0
- package/dist/api/last-sync.js.map +1 -1
- package/dist/api/llm-reference-client.d.ts +25 -0
- package/dist/api/llm-reference-client.d.ts.map +1 -0
- package/dist/api/llm-reference-client.js +49 -0
- package/dist/api/llm-reference-client.js.map +1 -0
- package/dist/api/template-client.d.ts +19 -0
- package/dist/api/template-client.d.ts.map +1 -0
- package/dist/api/template-client.js +47 -0
- package/dist/api/template-client.js.map +1 -0
- package/dist/auth/open-browser.d.ts.map +1 -1
- package/dist/auth/open-browser.js +3 -1
- package/dist/auth/open-browser.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +240 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/get-template.d.ts +3 -0
- package/dist/commands/get-template.d.ts.map +1 -0
- package/dist/commands/get-template.js +66 -0
- package/dist/commands/get-template.js.map +1 -0
- package/dist/commands/list-referenced-file-sessions.d.ts +3 -0
- package/dist/commands/list-referenced-file-sessions.d.ts.map +1 -0
- package/dist/commands/list-referenced-file-sessions.js +44 -0
- package/dist/commands/list-referenced-file-sessions.js.map +1 -0
- package/dist/commands/map-claude-projects.d.ts.map +1 -1
- package/dist/commands/map-claude-projects.js +20 -0
- package/dist/commands/map-claude-projects.js.map +1 -1
- package/dist/commands/search-referenced-files.d.ts +3 -0
- package/dist/commands/search-referenced-files.d.ts.map +1 -0
- package/dist/commands/search-referenced-files.js +53 -0
- package/dist/commands/search-referenced-files.js.map +1 -0
- package/dist/commands/search-templates.d.ts +3 -0
- package/dist/commands/search-templates.d.ts.map +1 -0
- package/dist/commands/search-templates.js +63 -0
- package/dist/commands/search-templates.js.map +1 -0
- package/dist/commands/sync-skills.d.ts +18 -0
- package/dist/commands/sync-skills.d.ts.map +1 -0
- package/dist/commands/sync-skills.js +222 -0
- package/dist/commands/sync-skills.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/proxy/proxy.d.ts.map +1 -1
- package/dist/proxy/proxy.js +71 -3
- package/dist/proxy/proxy.js.map +1 -1
- package/dist/proxy/sender.d.ts.map +1 -1
- package/dist/proxy/sender.js +1 -0
- package/dist/proxy/sender.js.map +1 -1
- package/dist/proxy/wrap-runner.d.ts +2 -5
- package/dist/proxy/wrap-runner.d.ts.map +1 -1
- package/dist/proxy/wrap-runner.js +2 -31
- package/dist/proxy/wrap-runner.js.map +1 -1
- package/dist/sessions/redact-secrets.d.ts.map +1 -1
- package/dist/sessions/redact-secrets.js +6 -1
- package/dist/sessions/redact-secrets.js.map +1 -1
- package/dist/skills/skill-scanner.d.ts +55 -0
- package/dist/skills/skill-scanner.d.ts.map +1 -0
- package/dist/skills/skill-scanner.js +132 -0
- package/dist/skills/skill-scanner.js.map +1 -0
- package/dist/skills/skill-state.d.ts +29 -0
- package/dist/skills/skill-state.d.ts.map +1 -0
- package/dist/skills/skill-state.js +111 -0
- package/dist/skills/skill-state.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/dist/win-spawn.d.ts +6 -0
- package/dist/win-spawn.d.ts.map +1 -0
- package/dist/win-spawn.js +32 -0
- package/dist/win-spawn.js.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -83,6 +83,8 @@ coolhand close-optimization opt-123 "Added the suggested index; verified query l
|
|
|
83
83
|
| `coolhand list-workloads` | List workloads with optional search and pagination |
|
|
84
84
|
| `coolhand get-workload` | Fetch a single workload by ID |
|
|
85
85
|
| `coolhand update-workload` | Update a workload's name and/or description |
|
|
86
|
+
| `coolhand search-templates` | List and search LLM request templates |
|
|
87
|
+
| `coolhand get-template` | Fetch a single template by ID, with its prompt patterns |
|
|
86
88
|
| `coolhand search-optimizations` | List and filter optimization records |
|
|
87
89
|
| `coolhand get-optimization` | Fetch a single optimization by ID |
|
|
88
90
|
| `coolhand update-optimization` | Update an optimization's fields |
|
|
@@ -91,9 +93,12 @@ coolhand close-optimization opt-123 "Added the suggested index; verified query l
|
|
|
91
93
|
| `coolhand get-feedback` | Fetch a single feedback record by ID |
|
|
92
94
|
| `coolhand fetch-log` | Fetch the input/output content of a single LLM request log |
|
|
93
95
|
| `coolhand search-logs` | Search LLM request logs with flexible filters |
|
|
96
|
+
| `coolhand search-referenced-files` | List a client's referenced files, aggregated and ranked by reference count |
|
|
97
|
+
| `coolhand list-referenced-file-sessions` | List the raw sessions that referenced one exact file path |
|
|
94
98
|
| `coolhand analyze-claude-sessions` | Submit Claude sessions for pattern and cost analysis |
|
|
95
99
|
| `coolhand map-claude-projects` | Upload a file-tree map (names + metadata only) of every folder named "claude" |
|
|
96
100
|
| `coolhand upload-client-file` | Upload a local file to Coolhand as a client file |
|
|
101
|
+
| `coolhand sync-skills` | Upload locally-built Claude skills (SKILL.md files) to Coolhand as client files |
|
|
97
102
|
| `coolhand wildcard` | Record an agent blocker and exit cleanly |
|
|
98
103
|
|
|
99
104
|
See [docs/commands.md](./docs/commands.md) for full flag reference and usage notes.
|
|
@@ -101,6 +106,7 @@ See [docs/commands.md](./docs/commands.md) for full flag reference and usage not
|
|
|
101
106
|
## Security
|
|
102
107
|
|
|
103
108
|
- The callback listener binds to `127.0.0.1` only — never reachable from the LAN.
|
|
109
|
+
- The MITM proxy started by `coolhand claude`/`coolhand monitor` also binds to `127.0.0.1` only — never reachable from the LAN.
|
|
104
110
|
- Tokens are delivered through a one-shot localhost redirect; subsequent calls to the listener get `410 Gone`.
|
|
105
111
|
- CSRF protection: every login generates a random `state` value verified with `crypto.timingSafeEqual` before any token is accepted.
|
|
106
112
|
- `~/.coolhand/config.json` is written atomically with mode `0o600`; the parent directory is `0o700`.
|
|
@@ -165,6 +171,7 @@ Located at `$HOME/.coolhand/config.json` (override with `COOLHAND_CONFIG_DIR` fo
|
|
|
165
171
|
- [Auth Flow](./docs/auth-flow.md) — browser-callback sequence, state machine, timeout and error paths
|
|
166
172
|
- [Configuration File](./docs/config-file.md) — full config schema, multi-client model, `COOLHAND_CONFIG_DIR` override
|
|
167
173
|
- [Session Capture](./docs/session-capture.md) — session scanning, envelope format, deduplication, scope and limitations
|
|
174
|
+
- [Skill Capture](./docs/skill-capture.md) — skill discovery, dedup, and incremental upload logic for `sync-skills`
|
|
168
175
|
|
|
169
176
|
## About Coolhand Labs
|
|
170
177
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"last-sync.d.ts","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,aAAa,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"last-sync.d.ts","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,aAAa,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAqE1F"}
|
package/dist/api/last-sync.js
CHANGED
|
@@ -55,6 +55,8 @@ export async function fetchLastSync(opts = {}) {
|
|
|
55
55
|
res = await fetch(url, {
|
|
56
56
|
method: 'GET',
|
|
57
57
|
headers: { Accept: 'application/json', 'X-API-Key': apiKey },
|
|
58
|
+
// Never follow redirects: fetch would forward X-API-Key to the redirect target.
|
|
59
|
+
redirect: 'error',
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"last-sync.js","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA8B,EAAE;IAClE,IAAI,MAA0B,CAAC;IAC/B,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,GAAG,MAAM,EAAE,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;QACjE,OAAO,GAAG,MAAM,EAAE,QAAQ,IAAI,gBAAgB,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,sBAAsB,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,0BAA0B,EAAE,aAAa,CAAC,CAAC;QACpE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,oBAAoB,EAAE,aAAa,CAAC,CAAC;QAC/D,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;QACrD,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,GAAG,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YACrB,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"last-sync.js","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Coolhand } from 'coolhand-node';
|
|
2
|
+
import { CliError } from '../errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the CLI's stored client and returns a `Coolhand` instance authenticated with its
|
|
5
|
+
* *private* key. `searchReferencedFiles`/`listReferencedFileSessions` require the private key —
|
|
6
|
+
* the public key used for LLM capture is write-only on this API and 401s here.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getLlmReferenceClient(opts?: {
|
|
9
|
+
clientId?: string;
|
|
10
|
+
}): Promise<Coolhand>;
|
|
11
|
+
/**
|
|
12
|
+
* Maps an error thrown by `Coolhand#searchReferencedFiles`/`listReferencedFileSessions` to a
|
|
13
|
+
* `CliError` — a 401 means the stored private key was rejected (mirrors `log-client.ts`'s hint), a
|
|
14
|
+
* 504 means the query exceeded the backend's statement timeout and is retryable, not a bug. Any
|
|
15
|
+
* other status (e.g. 422 for an unrecognized filter or non-scalar param) surfaces the server's own
|
|
16
|
+
* message rather than swallowing it.
|
|
17
|
+
*
|
|
18
|
+
* Both endpoints can 504 — `searchReferencedFiles` on its GROUP BY aggregate, and
|
|
19
|
+
* `listReferencedFileSessions` on the pagination `COUNT(*)` for a very hot `file_path` — but they
|
|
20
|
+
* have different flags to retry with, so `timeoutHint` is caller-supplied (same shape as
|
|
21
|
+
* `mapFeedbackHttpError`'s `notFoundMessage`). Suggesting a flag the invoking command doesn't
|
|
22
|
+
* define would be a silent no-op: the arg parser accepts unknown flags without complaint.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mapLlmReferenceHttpError(err: unknown, timeoutHint: string): CliError;
|
|
25
|
+
//# sourceMappingURL=llm-reference-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-reference-client.d.ts","sourceRoot":"","sources":["../../src/api/llm-reference-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoB/F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,QAAQ,CAkBpF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Coolhand, HttpError } from 'coolhand-node';
|
|
2
|
+
import { CliError } from '../errors.js';
|
|
3
|
+
import { loadConfig, resolveClient } from '../config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the CLI's stored client and returns a `Coolhand` instance authenticated with its
|
|
6
|
+
* *private* key. `searchReferencedFiles`/`listReferencedFileSessions` require the private key —
|
|
7
|
+
* the public key used for LLM capture is write-only on this API and 401s here.
|
|
8
|
+
*/
|
|
9
|
+
export async function getLlmReferenceClient(opts = {}) {
|
|
10
|
+
const cfg = await loadConfig();
|
|
11
|
+
const client = await resolveClient(cfg, opts.clientId);
|
|
12
|
+
if (!client.private_key) {
|
|
13
|
+
throw new CliError('NO_PRIVATE_KEY', "No private key configured. Run 'coolhand login --scope private' first.");
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
// Coolhand validates baseUrl in its constructor (https required; http only for localhost).
|
|
17
|
+
return new Coolhand({ apiKey: client.private_key, baseUrl: client.base_url, silent: true });
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
throw new CliError('INVALID_BASE_URL', `Invalid base_url for client: ${client.base_url} (${err.message})`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Maps an error thrown by `Coolhand#searchReferencedFiles`/`listReferencedFileSessions` to a
|
|
25
|
+
* `CliError` — a 401 means the stored private key was rejected (mirrors `log-client.ts`'s hint), a
|
|
26
|
+
* 504 means the query exceeded the backend's statement timeout and is retryable, not a bug. Any
|
|
27
|
+
* other status (e.g. 422 for an unrecognized filter or non-scalar param) surfaces the server's own
|
|
28
|
+
* message rather than swallowing it.
|
|
29
|
+
*
|
|
30
|
+
* Both endpoints can 504 — `searchReferencedFiles` on its GROUP BY aggregate, and
|
|
31
|
+
* `listReferencedFileSessions` on the pagination `COUNT(*)` for a very hot `file_path` — but they
|
|
32
|
+
* have different flags to retry with, so `timeoutHint` is caller-supplied (same shape as
|
|
33
|
+
* `mapFeedbackHttpError`'s `notFoundMessage`). Suggesting a flag the invoking command doesn't
|
|
34
|
+
* define would be a silent no-op: the arg parser accepts unknown flags without complaint.
|
|
35
|
+
*/
|
|
36
|
+
export function mapLlmReferenceHttpError(err, timeoutHint) {
|
|
37
|
+
if (err instanceof HttpError) {
|
|
38
|
+
if (err.status === 401) {
|
|
39
|
+
return new CliError('LLM_REFERENCE_ERROR', "The stored private key was rejected. Run 'coolhand login --scope private' to re-authenticate.");
|
|
40
|
+
}
|
|
41
|
+
if (err.status === 504) {
|
|
42
|
+
return new CliError('LLM_REFERENCE_ERROR', `Referenced file request timed out (504): the query exceeded the backend's statement ` +
|
|
43
|
+
`timeout. This is retryable — ${timeoutHint}.`);
|
|
44
|
+
}
|
|
45
|
+
return new CliError('LLM_REFERENCE_ERROR', `Referenced file request failed (${err.status}): ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
return new CliError('LLM_REFERENCE_ERROR', `Referenced file request failed: ${err.message}`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=llm-reference-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-reference-client.js","sourceRoot":"","sources":["../../src/api/llm-reference-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAA8B,EAAE;IAC1E,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAChB,gBAAgB,EAChB,wEAAwE,CACzE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,2FAA2F;QAC3F,OAAO,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,gCAAgC,MAAM,CAAC,QAAQ,KAAM,GAAa,CAAC,OAAO,GAAG,CAC9E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAY,EAAE,WAAmB;IACxE,IAAI,GAAG,YAAY,SAAS,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CACjB,qBAAqB,EACrB,+FAA+F,CAChG,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CACjB,qBAAqB,EACrB,sFAAsF;gBACpF,gCAAgC,WAAW,GAAG,CACjD,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,QAAQ,CAAC,qBAAqB,EAAE,mCAAmC,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,qBAAqB,EAAE,mCAAoC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;AAC1G,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Coolhand } from 'coolhand-node';
|
|
2
|
+
import { CliError } from '../errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the CLI's stored client and returns a `Coolhand` instance authenticated with its
|
|
5
|
+
* *private* key. `searchTemplates`/`getTemplate` require the private key — the public key used by
|
|
6
|
+
* `monitor`/`logRequest` is write-only on this API and will 401 here.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getTemplateClient(opts?: {
|
|
9
|
+
clientId?: string;
|
|
10
|
+
}): Promise<Coolhand>;
|
|
11
|
+
/**
|
|
12
|
+
* Maps an error thrown by `Coolhand#searchTemplates`/`getTemplate` to a `CliError`.
|
|
13
|
+
*
|
|
14
|
+
* A `504` is not a crash: both endpoints aggregate `log_count` over `llm_request_logs` under a
|
|
15
|
+
* 10-second statement timeout, so a slow aggregate is expected and retryable. `timeoutHint` is the
|
|
16
|
+
* caller's advice for making the same call cheaper, which differs between list and show.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mapTemplateHttpError(err: unknown, notFoundMessage: string, timeoutHint: string): CliError;
|
|
19
|
+
//# sourceMappingURL=template-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-client.d.ts","sourceRoot":"","sources":["../../src/api/template-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoB3F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,GAClB,QAAQ,CAsBV"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Coolhand, HttpError } from 'coolhand-node';
|
|
2
|
+
import { CliError } from '../errors.js';
|
|
3
|
+
import { loadConfig, resolveClient } from '../config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the CLI's stored client and returns a `Coolhand` instance authenticated with its
|
|
6
|
+
* *private* key. `searchTemplates`/`getTemplate` require the private key — the public key used by
|
|
7
|
+
* `monitor`/`logRequest` is write-only on this API and will 401 here.
|
|
8
|
+
*/
|
|
9
|
+
export async function getTemplateClient(opts = {}) {
|
|
10
|
+
const cfg = await loadConfig();
|
|
11
|
+
const client = await resolveClient(cfg, opts.clientId);
|
|
12
|
+
if (!client.private_key) {
|
|
13
|
+
throw new CliError('NO_PRIVATE_KEY', "No private key configured. Run 'coolhand login --scope private' first.");
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
// Coolhand validates baseUrl in its constructor (https required; http only for localhost).
|
|
17
|
+
return new Coolhand({ apiKey: client.private_key, baseUrl: client.base_url, silent: true });
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
throw new CliError('INVALID_BASE_URL', `Invalid base_url for client: ${client.base_url} (${err.message})`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Maps an error thrown by `Coolhand#searchTemplates`/`getTemplate` to a `CliError`.
|
|
25
|
+
*
|
|
26
|
+
* A `504` is not a crash: both endpoints aggregate `log_count` over `llm_request_logs` under a
|
|
27
|
+
* 10-second statement timeout, so a slow aggregate is expected and retryable. `timeoutHint` is the
|
|
28
|
+
* caller's advice for making the same call cheaper, which differs between list and show.
|
|
29
|
+
*/
|
|
30
|
+
export function mapTemplateHttpError(err, notFoundMessage, timeoutHint) {
|
|
31
|
+
if (err instanceof HttpError) {
|
|
32
|
+
if (err.status === 401) {
|
|
33
|
+
return new CliError('TEMPLATE_ERROR', "The stored private key was rejected. Run 'coolhand login --scope private' to re-authenticate.");
|
|
34
|
+
}
|
|
35
|
+
if (err.status === 404) {
|
|
36
|
+
return new CliError('TEMPLATE_ERROR', notFoundMessage);
|
|
37
|
+
}
|
|
38
|
+
if (err.status === 504) {
|
|
39
|
+
return new CliError('TEMPLATE_ERROR', `The server timed out counting logs (log_count) for this request. ${timeoutHint}`);
|
|
40
|
+
}
|
|
41
|
+
// coolhand-node already formats this as `Template request failed (<status>): <body>`;
|
|
42
|
+
// re-prefixing it here would print the status twice.
|
|
43
|
+
return new CliError('TEMPLATE_ERROR', err.message);
|
|
44
|
+
}
|
|
45
|
+
return new CliError('TEMPLATE_ERROR', `Template request failed: ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=template-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-client.js","sourceRoot":"","sources":["../../src/api/template-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAA8B,EAAE;IACtE,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAChB,gBAAgB,EAChB,wEAAwE,CACzE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,2FAA2F;QAC3F,OAAO,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,gCAAgC,MAAM,CAAC,QAAQ,KAAM,GAAa,CAAC,OAAO,GAAG,CAC9E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAY,EACZ,eAAuB,EACvB,WAAmB;IAEnB,IAAI,GAAG,YAAY,SAAS,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CACjB,gBAAgB,EAChB,+FAA+F,CAChG,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CACjB,gBAAgB,EAChB,oEAAoE,WAAW,EAAE,CAClF,CAAC;QACJ,CAAC;QACD,sFAAsF;QACtF,qDAAqD;QACrD,OAAO,IAAI,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,gBAAgB,EAAE,4BAA6B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9F,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/auth/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/auth/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAItC,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { logger } from '../logger.js';
|
|
3
|
+
import { resolveWrapSpawn } from '../win-spawn.js';
|
|
3
4
|
export async function openBrowser(url, deps = {}) {
|
|
4
5
|
const platform = deps.platform ?? process.platform;
|
|
5
6
|
const spawnFn = deps.spawnFn ?? spawn;
|
|
@@ -17,8 +18,9 @@ export async function openBrowser(url, deps = {}) {
|
|
|
17
18
|
command = 'xdg-open';
|
|
18
19
|
args = [url];
|
|
19
20
|
}
|
|
21
|
+
const { cmd, spawnArgs, windowsVerbatimArguments } = resolveWrapSpawn(command, args, platform);
|
|
20
22
|
try {
|
|
21
|
-
const child = spawnFn(
|
|
23
|
+
const child = spawnFn(cmd, spawnArgs, { detached: true, stdio: 'ignore', windowsVerbatimArguments });
|
|
22
24
|
child.on('error', (err) => {
|
|
23
25
|
logger.warn(`Failed to open browser (${command}): ${err.message}. Open the URL manually.`);
|
|
24
26
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../../src/auth/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../../src/auth/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAOnD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW,EAAE,OAAwB,EAAE;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;IAEtC,IAAI,OAAe,CAAC;IACpB,IAAI,IAAc,CAAC;IACnB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,MAAM,CAAC;QACjB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,GAAG,KAAK,CAAC;QAChB,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,UAAU,CAAC;QACrB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,wBAAwB,EAAE,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAE/F,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,wBAAwB,EAAE,CAAC,CAAC;QACrG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,MAAM,CAAC,IAAI,CAAC,2BAA2B,OAAO,MAAM,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,6BAA6B,OAAO,MAAO,GAAa,CAAC,OAAO,0BAA0B,CAAC,CAAC;IAC1G,CAAC;AACH,CAAC"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA6DA,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;CACpD;AAmZD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAgE7F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAiDpD;AA6xBD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAwJzD"}
|
package/dist/cli.js
CHANGED
|
@@ -16,19 +16,24 @@ import { run as runMonitor } from './commands/monitor.js';
|
|
|
16
16
|
import { run as runAnalyzeClaudeSessions } from './commands/analyze-claude-sessions.js';
|
|
17
17
|
import { run as runMapClaudeProjects } from './commands/map-claude-projects.js';
|
|
18
18
|
import { run as runUploadClientFile } from './commands/upload-client-file.js';
|
|
19
|
+
import { run as runSyncSkills } from './commands/sync-skills.js';
|
|
19
20
|
import { run as runListWorkloads } from './commands/list-workloads.js';
|
|
20
21
|
import { run as runGetWorkload } from './commands/get-workload.js';
|
|
21
22
|
import { run as runUpdateWorkload } from './commands/update-workload.js';
|
|
22
23
|
import { run as runFetchLog } from './commands/fetch-log.js';
|
|
23
24
|
import { run as runSearchLogs } from './commands/search-logs.js';
|
|
25
|
+
import { run as runSearchTemplates } from './commands/search-templates.js';
|
|
26
|
+
import { run as runGetTemplate } from './commands/get-template.js';
|
|
24
27
|
import { run as runFlushPending, spawnBackgroundFlush } from './commands/flush-pending.js';
|
|
25
28
|
import { run as runSearchFeedback } from './commands/search-feedback.js';
|
|
26
29
|
import { run as runGetFeedback } from './commands/get-feedback.js';
|
|
30
|
+
import { run as runSearchReferencedFiles } from './commands/search-referenced-files.js';
|
|
31
|
+
import { run as runListReferencedFileSessions } from './commands/list-referenced-file-sessions.js';
|
|
27
32
|
import { countPending, flushFailed } from './pending-store.js';
|
|
28
33
|
import { confirm } from './prompt.js';
|
|
29
|
-
const BOOLEAN_FLAGS = new Set(['all', 'help', 'h', 'json', 'version', 'v', 'dry-run', 'include-archived', 'include-system', 'include-templates', 'full', 'matched', 'unmatched', 'include-thinking', 'unmatched-only', 'include-prompts']);
|
|
34
|
+
const BOOLEAN_FLAGS = new Set(['all', 'help', 'h', 'json', 'version', 'v', 'dry-run', 'include-archived', 'include-system', 'include-templates', 'full', 'matched', 'unmatched', 'include-thinking', 'unmatched-only', 'include-prompts', 'include-deprecated', 'force']);
|
|
30
35
|
/** Flags whose repeated occurrences accumulate into an array instead of overwriting. */
|
|
31
|
-
const REPEATABLE_FLAGS = new Set(['project', 'exclude-project']);
|
|
36
|
+
const REPEATABLE_FLAGS = new Set(['project', 'exclude-project', 'skill', 'exclude-skill', 'source']);
|
|
32
37
|
const COMMANDS = [
|
|
33
38
|
{
|
|
34
39
|
name: 'login',
|
|
@@ -198,6 +203,7 @@ const COMMANDS = [
|
|
|
198
203
|
options: [
|
|
199
204
|
{ flag: '--root PATH', description: 'Search PATH instead of the home directory' },
|
|
200
205
|
{ flag: '--output PATH', description: 'Also write the generated markdown report to PATH, for local inspection' },
|
|
206
|
+
{ flag: '--force', description: 'Skip the confirmation prompt when --output already exists' },
|
|
201
207
|
{ flag: '--dry-run', description: 'Build the map and report its size without uploading' },
|
|
202
208
|
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
203
209
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
@@ -216,6 +222,21 @@ const COMMANDS = [
|
|
|
216
222
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
217
223
|
],
|
|
218
224
|
},
|
|
225
|
+
{
|
|
226
|
+
name: 'sync-skills',
|
|
227
|
+
oneLiner: 'Upload locally-built Claude skills (SKILL.md files) to Coolhand as client files',
|
|
228
|
+
usage: 'coolhand sync-skills [options]',
|
|
229
|
+
options: [
|
|
230
|
+
{ flag: '--root PATH', description: 'Search only PATH (recursively) instead of the 3 default roots' },
|
|
231
|
+
{ flag: '--source NAME', description: 'Restrict the default scan to: authored, installed, claude-code (repeatable, comma-separable)' },
|
|
232
|
+
{ flag: '--skill NAME', description: 'Only upload skills whose name matches (repeatable, comma-separable)' },
|
|
233
|
+
{ flag: '--exclude-skill NAME', description: 'Skip skills whose name matches (repeatable, comma-separable)' },
|
|
234
|
+
{ flag: '--force', description: 'Re-upload every matched skill, ignoring the unchanged-content skip (different meaning than map-claude-projects\' --force)' },
|
|
235
|
+
{ flag: '--dry-run', description: 'Scan and report what would be uploaded, without sending or updating local state' },
|
|
236
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
237
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
219
240
|
{
|
|
220
241
|
name: 'claude',
|
|
221
242
|
oneLiner: 'Run the Claude CLI through the Coolhand proxy (captures LLM calls)',
|
|
@@ -306,6 +327,54 @@ const COMMANDS = [
|
|
|
306
327
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
307
328
|
],
|
|
308
329
|
},
|
|
330
|
+
{
|
|
331
|
+
name: 'search-templates',
|
|
332
|
+
oneLiner: 'List and search LLM request templates (requires a private key)',
|
|
333
|
+
usage: 'coolhand search-templates [options]',
|
|
334
|
+
options: [
|
|
335
|
+
{ flag: '--search TEXT', description: 'Case-insensitive substring to match in the template name' },
|
|
336
|
+
{ flag: '--workload-id ID', description: 'Filter to a single workload, by workload hashid' },
|
|
337
|
+
{ flag: '--status VALUE', description: 'Filter by status: draft, published, or failure' },
|
|
338
|
+
{ flag: '--include-deprecated', description: 'Include deprecated templates (hidden by default)' },
|
|
339
|
+
{ flag: '--include-system', description: "Include the 'Unmatched' and 'Ignored API Calls' system buckets (hidden by default) — inspect 'Unmatched' when logs are misrouting" },
|
|
340
|
+
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
341
|
+
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
342
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
343
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
344
|
+
],
|
|
345
|
+
}, {
|
|
346
|
+
name: 'get-template',
|
|
347
|
+
oneLiner: 'Get a single LLM request template by ID, including its prompt patterns (requires a private key)',
|
|
348
|
+
usage: 'coolhand get-template <template-id> [options]',
|
|
349
|
+
options: [
|
|
350
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
351
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
352
|
+
],
|
|
353
|
+
}, {
|
|
354
|
+
name: 'search-referenced-files',
|
|
355
|
+
oneLiner: "List a client's referenced files, aggregated and ranked by reference count",
|
|
356
|
+
usage: 'coolhand search-referenced-files [options]',
|
|
357
|
+
options: [
|
|
358
|
+
{ flag: '--file-path-contains TEXT', description: 'Case-insensitive substring to match in the file path' },
|
|
359
|
+
{ flag: '--created-at-gteq DATE', description: 'Lower bound (inclusive) on created_at, ISO-8601' },
|
|
360
|
+
{ flag: '--created-at-lteq DATE', description: 'Upper bound (inclusive) on created_at, ISO-8601' },
|
|
361
|
+
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
362
|
+
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
363
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
364
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
365
|
+
],
|
|
366
|
+
}, {
|
|
367
|
+
name: 'list-referenced-file-sessions',
|
|
368
|
+
oneLiner: 'List the raw, un-aggregated sessions that referenced one exact file path',
|
|
369
|
+
usage: 'coolhand list-referenced-file-sessions --file-path PATH [options]',
|
|
370
|
+
options: [
|
|
371
|
+
{ flag: '--file-path PATH', description: 'Exact file_path to look up (required)' },
|
|
372
|
+
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
373
|
+
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
374
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
375
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
376
|
+
],
|
|
377
|
+
},
|
|
309
378
|
{
|
|
310
379
|
name: 'help',
|
|
311
380
|
oneLiner: 'Show this message or per-command help',
|
|
@@ -703,6 +772,40 @@ function mapClaudeProjectsOptions(parsed) {
|
|
|
703
772
|
if (typeof parsed.flags.output === 'string') {
|
|
704
773
|
opts.output = parsed.flags.output;
|
|
705
774
|
}
|
|
775
|
+
if (parsed.flags.force === true) {
|
|
776
|
+
opts.force = true;
|
|
777
|
+
}
|
|
778
|
+
if (parsed.flags['dry-run'] === true) {
|
|
779
|
+
opts.dryRun = true;
|
|
780
|
+
}
|
|
781
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
782
|
+
opts.clientId = parsed.flags['client-id'];
|
|
783
|
+
}
|
|
784
|
+
if (parsed.flags.json === true) {
|
|
785
|
+
opts.json = true;
|
|
786
|
+
}
|
|
787
|
+
return opts;
|
|
788
|
+
}
|
|
789
|
+
function syncSkillsOptions(parsed) {
|
|
790
|
+
const opts = {};
|
|
791
|
+
if (typeof parsed.flags.root === 'string') {
|
|
792
|
+
opts.root = parsed.flags.root;
|
|
793
|
+
}
|
|
794
|
+
const sources = stringListFlag(parsed.flags.source);
|
|
795
|
+
if (sources) {
|
|
796
|
+
opts.sources = sources;
|
|
797
|
+
}
|
|
798
|
+
const skills = stringListFlag(parsed.flags.skill);
|
|
799
|
+
if (skills) {
|
|
800
|
+
opts.skills = skills;
|
|
801
|
+
}
|
|
802
|
+
const excludeSkills = stringListFlag(parsed.flags['exclude-skill']);
|
|
803
|
+
if (excludeSkills) {
|
|
804
|
+
opts.excludeSkills = excludeSkills;
|
|
805
|
+
}
|
|
806
|
+
if (parsed.flags.force === true) {
|
|
807
|
+
opts.force = true;
|
|
808
|
+
}
|
|
706
809
|
if (parsed.flags['dry-run'] === true) {
|
|
707
810
|
opts.dryRun = true;
|
|
708
811
|
}
|
|
@@ -831,6 +934,66 @@ function fetchLogOptions(parsed) {
|
|
|
831
934
|
}
|
|
832
935
|
return opts;
|
|
833
936
|
}
|
|
937
|
+
const TEMPLATE_STATUSES = new Set(['draft', 'published', 'failure']);
|
|
938
|
+
function searchTemplatesOptions(parsed) {
|
|
939
|
+
const opts = {};
|
|
940
|
+
if (typeof parsed.flags['search'] === 'string') {
|
|
941
|
+
opts.search = parsed.flags['search'];
|
|
942
|
+
}
|
|
943
|
+
if (typeof parsed.flags['workload-id'] === 'string') {
|
|
944
|
+
opts.workloadId = parsed.flags['workload-id'];
|
|
945
|
+
}
|
|
946
|
+
if (typeof parsed.flags['status'] === 'string') {
|
|
947
|
+
const status = parsed.flags['status'];
|
|
948
|
+
if (!TEMPLATE_STATUSES.has(status)) {
|
|
949
|
+
throw new CliError('INVALID_ARGS', '--status must be one of: draft, published, failure');
|
|
950
|
+
}
|
|
951
|
+
opts.status = status;
|
|
952
|
+
}
|
|
953
|
+
if (parsed.flags['include-deprecated'] === true) {
|
|
954
|
+
opts.includeDeprecated = true;
|
|
955
|
+
}
|
|
956
|
+
if (parsed.flags['include-system'] === true) {
|
|
957
|
+
opts.includeSystem = true;
|
|
958
|
+
}
|
|
959
|
+
if (typeof parsed.flags['page'] === 'string') {
|
|
960
|
+
const raw = parsed.flags['page'];
|
|
961
|
+
const n = parseInt(raw, 10);
|
|
962
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1) {
|
|
963
|
+
throw new CliError('INVALID_ARGS', '--page must be a positive integer');
|
|
964
|
+
}
|
|
965
|
+
opts.page = n;
|
|
966
|
+
}
|
|
967
|
+
if (typeof parsed.flags['per-page'] === 'string') {
|
|
968
|
+
const raw = parsed.flags['per-page'];
|
|
969
|
+
const n = parseInt(raw, 10);
|
|
970
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1 || n > 100) {
|
|
971
|
+
throw new CliError('INVALID_ARGS', '--per-page must be an integer between 1 and 100');
|
|
972
|
+
}
|
|
973
|
+
opts.perPage = n;
|
|
974
|
+
}
|
|
975
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
976
|
+
opts.clientId = parsed.flags['client-id'];
|
|
977
|
+
}
|
|
978
|
+
if (parsed.flags.json === true) {
|
|
979
|
+
opts.json = true;
|
|
980
|
+
}
|
|
981
|
+
return opts;
|
|
982
|
+
}
|
|
983
|
+
function getTemplateOptions(parsed) {
|
|
984
|
+
const id = parsed.positional[0];
|
|
985
|
+
if (!id) {
|
|
986
|
+
throw new CliError('INVALID_ARGS', 'get-template requires a <template-id> argument');
|
|
987
|
+
}
|
|
988
|
+
const opts = { id };
|
|
989
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
990
|
+
opts.clientId = parsed.flags['client-id'];
|
|
991
|
+
}
|
|
992
|
+
if (parsed.flags.json === true) {
|
|
993
|
+
opts.json = true;
|
|
994
|
+
}
|
|
995
|
+
return opts;
|
|
996
|
+
}
|
|
834
997
|
function searchLogsOptions(parsed) {
|
|
835
998
|
const opts = {};
|
|
836
999
|
if (typeof parsed.flags['template-id'] === 'string') {
|
|
@@ -895,6 +1058,71 @@ function searchLogsOptions(parsed) {
|
|
|
895
1058
|
}
|
|
896
1059
|
return opts;
|
|
897
1060
|
}
|
|
1061
|
+
function searchReferencedFilesOptions(parsed) {
|
|
1062
|
+
const opts = {};
|
|
1063
|
+
if (typeof parsed.flags['file-path-contains'] === 'string') {
|
|
1064
|
+
opts.filePathContains = parsed.flags['file-path-contains'];
|
|
1065
|
+
}
|
|
1066
|
+
if (typeof parsed.flags['created-at-gteq'] === 'string') {
|
|
1067
|
+
opts.createdAtGteq = parsed.flags['created-at-gteq'];
|
|
1068
|
+
}
|
|
1069
|
+
if (typeof parsed.flags['created-at-lteq'] === 'string') {
|
|
1070
|
+
opts.createdAtLteq = parsed.flags['created-at-lteq'];
|
|
1071
|
+
}
|
|
1072
|
+
if (typeof parsed.flags['page'] === 'string') {
|
|
1073
|
+
const raw = parsed.flags['page'];
|
|
1074
|
+
const n = parseInt(raw, 10);
|
|
1075
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1) {
|
|
1076
|
+
throw new CliError('INVALID_ARGS', '--page must be a positive integer');
|
|
1077
|
+
}
|
|
1078
|
+
opts.page = n;
|
|
1079
|
+
}
|
|
1080
|
+
if (typeof parsed.flags['per-page'] === 'string') {
|
|
1081
|
+
const raw = parsed.flags['per-page'];
|
|
1082
|
+
const n = parseInt(raw, 10);
|
|
1083
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1 || n > 100) {
|
|
1084
|
+
throw new CliError('INVALID_ARGS', '--per-page must be an integer between 1 and 100');
|
|
1085
|
+
}
|
|
1086
|
+
opts.perPage = n;
|
|
1087
|
+
}
|
|
1088
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1089
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1090
|
+
}
|
|
1091
|
+
if (parsed.flags.json === true) {
|
|
1092
|
+
opts.json = true;
|
|
1093
|
+
}
|
|
1094
|
+
return opts;
|
|
1095
|
+
}
|
|
1096
|
+
function listReferencedFileSessionsOptions(parsed) {
|
|
1097
|
+
const filePath = parsed.flags['file-path'];
|
|
1098
|
+
if (typeof filePath !== 'string') {
|
|
1099
|
+
throw new CliError('INVALID_ARGS', 'list-referenced-file-sessions requires --file-path <value>');
|
|
1100
|
+
}
|
|
1101
|
+
const opts = { filePath };
|
|
1102
|
+
if (typeof parsed.flags['page'] === 'string') {
|
|
1103
|
+
const raw = parsed.flags['page'];
|
|
1104
|
+
const n = parseInt(raw, 10);
|
|
1105
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1) {
|
|
1106
|
+
throw new CliError('INVALID_ARGS', '--page must be a positive integer');
|
|
1107
|
+
}
|
|
1108
|
+
opts.page = n;
|
|
1109
|
+
}
|
|
1110
|
+
if (typeof parsed.flags['per-page'] === 'string') {
|
|
1111
|
+
const raw = parsed.flags['per-page'];
|
|
1112
|
+
const n = parseInt(raw, 10);
|
|
1113
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1 || n > 100) {
|
|
1114
|
+
throw new CliError('INVALID_ARGS', '--per-page must be an integer between 1 and 100');
|
|
1115
|
+
}
|
|
1116
|
+
opts.perPage = n;
|
|
1117
|
+
}
|
|
1118
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1119
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1120
|
+
}
|
|
1121
|
+
if (parsed.flags.json === true) {
|
|
1122
|
+
opts.json = true;
|
|
1123
|
+
}
|
|
1124
|
+
return opts;
|
|
1125
|
+
}
|
|
898
1126
|
function listWorkloadsOptions(parsed) {
|
|
899
1127
|
const opts = {};
|
|
900
1128
|
if (typeof parsed.flags.search === 'string') {
|
|
@@ -1180,6 +1408,8 @@ export async function run(argv) {
|
|
|
1180
1408
|
return await runMapClaudeProjects(mapClaudeProjectsOptions(parsed));
|
|
1181
1409
|
case 'upload-client-file':
|
|
1182
1410
|
return await runUploadClientFile(uploadClientFileOptions(parsed));
|
|
1411
|
+
case 'sync-skills':
|
|
1412
|
+
return await runSyncSkills(syncSkillsOptions(parsed));
|
|
1183
1413
|
case 'list-workloads':
|
|
1184
1414
|
return await runListWorkloads(listWorkloadsOptions(parsed));
|
|
1185
1415
|
case 'get-workload':
|
|
@@ -1190,6 +1420,14 @@ export async function run(argv) {
|
|
|
1190
1420
|
return await runFetchLog(fetchLogOptions(parsed));
|
|
1191
1421
|
case 'search-logs':
|
|
1192
1422
|
return await runSearchLogs(searchLogsOptions(parsed));
|
|
1423
|
+
case 'search-templates':
|
|
1424
|
+
return await runSearchTemplates(searchTemplatesOptions(parsed));
|
|
1425
|
+
case 'get-template':
|
|
1426
|
+
return await runGetTemplate(getTemplateOptions(parsed));
|
|
1427
|
+
case 'search-referenced-files':
|
|
1428
|
+
return await runSearchReferencedFiles(searchReferencedFilesOptions(parsed));
|
|
1429
|
+
case 'list-referenced-file-sessions':
|
|
1430
|
+
return await runListReferencedFileSessions(listReferencedFileSessionsOptions(parsed));
|
|
1193
1431
|
default:
|
|
1194
1432
|
logger.info(`Unknown command: ${parsed.command}`);
|
|
1195
1433
|
logger.info(buildSummaryHelp());
|