coolhand-cli 0.7.0 → 0.9.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.
Files changed (101) hide show
  1. package/README.md +23 -2
  2. package/dist/api/feedback-client.d.ts +17 -0
  3. package/dist/api/feedback-client.d.ts.map +1 -0
  4. package/dist/api/feedback-client.js +40 -0
  5. package/dist/api/feedback-client.js.map +1 -0
  6. package/dist/api/last-sync.d.ts +8 -6
  7. package/dist/api/last-sync.d.ts.map +1 -1
  8. package/dist/api/last-sync.js +27 -16
  9. package/dist/api/last-sync.js.map +1 -1
  10. package/dist/api/log-client.d.ts +17 -0
  11. package/dist/api/log-client.d.ts.map +1 -0
  12. package/dist/api/log-client.js +40 -0
  13. package/dist/api/log-client.js.map +1 -0
  14. package/dist/bin.js +0 -0
  15. package/dist/cli.d.ts +1 -1
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/cli.js +326 -10
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/analyze-claude-sessions.d.ts +6 -0
  20. package/dist/commands/analyze-claude-sessions.d.ts.map +1 -1
  21. package/dist/commands/analyze-claude-sessions.js +60 -9
  22. package/dist/commands/analyze-claude-sessions.js.map +1 -1
  23. package/dist/commands/clients.d.ts.map +1 -1
  24. package/dist/commands/clients.js +2 -1
  25. package/dist/commands/clients.js.map +1 -1
  26. package/dist/commands/close-optimization.js +1 -1
  27. package/dist/commands/close-optimization.js.map +1 -1
  28. package/dist/commands/fetch-log.d.ts +3 -0
  29. package/dist/commands/fetch-log.d.ts.map +1 -0
  30. package/dist/commands/{create-optimization.js → fetch-log.js} +14 -10
  31. package/dist/commands/fetch-log.js.map +1 -0
  32. package/dist/commands/get-feedback.d.ts +3 -0
  33. package/dist/commands/get-feedback.d.ts.map +1 -0
  34. package/dist/commands/get-feedback.js +70 -0
  35. package/dist/commands/get-feedback.js.map +1 -0
  36. package/dist/commands/get-optimization.d.ts.map +1 -1
  37. package/dist/commands/get-optimization.js +61 -10
  38. package/dist/commands/get-optimization.js.map +1 -1
  39. package/dist/commands/mcp-call.d.ts.map +1 -1
  40. package/dist/commands/mcp-call.js +14 -50
  41. package/dist/commands/mcp-call.js.map +1 -1
  42. package/dist/commands/search-feedback.d.ts +3 -0
  43. package/dist/commands/search-feedback.d.ts.map +1 -0
  44. package/dist/commands/search-feedback.js +71 -0
  45. package/dist/commands/search-feedback.js.map +1 -0
  46. package/dist/commands/search-logs.d.ts +3 -0
  47. package/dist/commands/search-logs.d.ts.map +1 -0
  48. package/dist/commands/search-logs.js +77 -0
  49. package/dist/commands/search-logs.js.map +1 -0
  50. package/dist/commands/status.d.ts.map +1 -1
  51. package/dist/commands/status.js +2 -1
  52. package/dist/commands/status.js.map +1 -1
  53. package/dist/commands/whoami.js +1 -1
  54. package/dist/commands/whoami.js.map +1 -1
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +5 -1
  57. package/dist/config.js.map +1 -1
  58. package/dist/errors.d.ts +1 -1
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js.map +1 -1
  61. package/dist/parse-when.d.ts +17 -0
  62. package/dist/parse-when.d.ts.map +1 -0
  63. package/dist/parse-when.js +49 -0
  64. package/dist/parse-when.js.map +1 -0
  65. package/dist/sessions/claude-scanner.d.ts +14 -1
  66. package/dist/sessions/claude-scanner.d.ts.map +1 -1
  67. package/dist/sessions/claude-scanner.js +205 -41
  68. package/dist/sessions/claude-scanner.js.map +1 -1
  69. package/dist/sessions/cowork-scanner.d.ts +2 -0
  70. package/dist/sessions/cowork-scanner.d.ts.map +1 -1
  71. package/dist/sessions/cowork-scanner.js +22 -9
  72. package/dist/sessions/cowork-scanner.js.map +1 -1
  73. package/dist/sessions/redact-secrets.d.ts +14 -0
  74. package/dist/sessions/redact-secrets.d.ts.map +1 -0
  75. package/dist/sessions/redact-secrets.js +47 -0
  76. package/dist/sessions/redact-secrets.js.map +1 -0
  77. package/dist/sessions/session-filter.d.ts +28 -0
  78. package/dist/sessions/session-filter.d.ts.map +1 -0
  79. package/dist/sessions/session-filter.js +38 -0
  80. package/dist/sessions/session-filter.js.map +1 -0
  81. package/dist/types.d.ts +61 -0
  82. package/dist/types.d.ts.map +1 -1
  83. package/dist/types.js.map +1 -1
  84. package/dist/version.d.ts +2 -2
  85. package/dist/version.js +1 -1
  86. package/package.json +6 -6
  87. package/dist/commands/accounts.d.ts +0 -3
  88. package/dist/commands/accounts.d.ts.map +0 -1
  89. package/dist/commands/accounts.js +0 -59
  90. package/dist/commands/accounts.js.map +0 -1
  91. package/dist/commands/add-optimization-comment.d.ts +0 -3
  92. package/dist/commands/add-optimization-comment.d.ts.map +0 -1
  93. package/dist/commands/add-optimization-comment.js +0 -28
  94. package/dist/commands/add-optimization-comment.js.map +0 -1
  95. package/dist/commands/capture-sessions.d.ts +0 -3
  96. package/dist/commands/capture-sessions.d.ts.map +0 -1
  97. package/dist/commands/capture-sessions.js +0 -79
  98. package/dist/commands/capture-sessions.js.map +0 -1
  99. package/dist/commands/create-optimization.d.ts +0 -3
  100. package/dist/commands/create-optimization.d.ts.map +0 -1
  101. package/dist/commands/create-optimization.js.map +0 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@ Requires Node 20 or newer.
22
22
  ## Analyze Claude sessions
23
23
 
24
24
  ```bash
25
- coolhand analyze-claude-sessions [--dry-run] [--client-id ID] [--json]
25
+ coolhand analyze-claude-sessions [--dry-run] [--client-id ID] [--json] [filter options]
26
26
  ```
27
27
 
28
28
  Upload your historical Claude Code session transcripts to your Coolhand account for analysis. Coolhand analyzes the uploaded conversations to surface:
@@ -31,7 +31,7 @@ Upload your historical Claude Code session transcripts to your Coolhand account
31
31
  - **Efficiency gaps** — workflows with unnecessary back-and-forth or redundant steps
32
32
  - **Cost insights** — sessions with high token usage relative to their outcome
33
33
 
34
- > **What gets uploaded**: The conversation transcripts stored in `~/.claude/projects/` — the messages exchanged between you and Claude, including any code or context you shared in those conversations. Use `--dry-run` to preview exactly what would be sent before submitting anything.
34
+ > **What gets uploaded**: The conversation transcripts stored in `~/.claude/projects/` — the messages exchanged between you and Claude, including any code or context you shared in those conversations. Use `--dry-run` to preview exactly what would be sent before submitting anything. You control the scope: `--since`/`--until` bound the time period, and `--project`/`--exclude-project`/`--projects-dir` choose which folders are uploaded from — excluded sessions are never even read from disk.
35
35
 
36
36
  See [Session capture](./docs/session-capture.md) for capture logic, duplicate-avoidance details, and the full flag reference.
37
37
 
@@ -52,6 +52,23 @@ Set `COOLHAND_AGENT_NAME` to avoid passing `--agent-name` on every call. Optiona
52
52
 
53
53
  See [Your AI agent has notes](https://michael.carroll.io/talks/2026/your-ai-agent-has-notes) for a presentation on the research & best practices for using this pattern.
54
54
 
55
+ ## Optimizations
56
+
57
+ Coolhand surfaces optimizations found for your production agents — a way to search, inspect, and act on suggested improvements without leaving the terminal:
58
+
59
+ ```bash
60
+ # Find open, high-impact optimizations
61
+ coolhand search-optimizations --status proposed --sort-by impact_desc
62
+
63
+ # Inspect one in full (human-readable by default; add --json for scripting)
64
+ coolhand get-optimization opt-123
65
+
66
+ # Apply the fix, then close it out with a reason
67
+ coolhand close-optimization opt-123 "Added the suggested index; verified query latency dropped from 800ms to 120ms in staging."
68
+ ```
69
+
70
+ `get-optimization` prints a scannable summary by default — title, status, impact/complexity, analysis, and plan — not a raw JSON dump. Internal agent transcript data (`orchestrator_messages`) is only included with `--full`. See [docs/commands.md](./docs/commands.md) for the full flag reference, including `update-optimization` for editing an optimization's fields in place.
71
+
55
72
  ## Commands
56
73
 
57
74
  | Command | Description |
@@ -70,6 +87,10 @@ See [Your AI agent has notes](https://michael.carroll.io/talks/2026/your-ai-agen
70
87
  | `coolhand get-optimization` | Fetch a single optimization by ID |
71
88
  | `coolhand update-optimization` | Update an optimization's fields |
72
89
  | `coolhand close-optimization` | Close an optimization with a reason |
90
+ | `coolhand search-feedback` | Search and filter feedback records |
91
+ | `coolhand get-feedback` | Fetch a single feedback record by ID |
92
+ | `coolhand fetch-log` | Fetch the input/output content of a single LLM request log |
93
+ | `coolhand search-logs` | Search LLM request logs with flexible filters |
73
94
  | `coolhand analyze-claude-sessions` | Submit Claude sessions for pattern and cost analysis |
74
95
  | `coolhand wildcard` | Record an agent blocker and exit cleanly |
75
96
 
@@ -0,0 +1,17 @@
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. `searchFeedback`/`getFeedback` (coolhand-node#89) require the private key — the
6
+ * public key used by `wildcard`/`createFeedback` will 401 here.
7
+ */
8
+ export declare function getFeedbackClient(opts?: {
9
+ clientId?: string;
10
+ }): Promise<Coolhand>;
11
+ /**
12
+ * Maps an error thrown by `Coolhand#searchFeedback`/`getFeedback` to a `CliError` with a clear,
13
+ * actionable message — a 401 means the stored private key was rejected (mirrors `mcp-call.ts`'s
14
+ * hint), a 404 uses the caller-supplied message (e.g. "Feedback \"x\" not found").
15
+ */
16
+ export declare function mapFeedbackHttpError(err: unknown, notFoundMessage: string): CliError;
17
+ //# sourceMappingURL=feedback-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback-client.d.ts","sourceRoot":"","sources":["../../src/api/feedback-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;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,QAAQ,CAcpF"}
@@ -0,0 +1,40 @@
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. `searchFeedback`/`getFeedback` (coolhand-node#89) require the private key — the
7
+ * public key used by `wildcard`/`createFeedback` will 401 here.
8
+ */
9
+ export async function getFeedbackClient(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#searchFeedback`/`getFeedback` to a `CliError` with a clear,
25
+ * actionable message — a 401 means the stored private key was rejected (mirrors `mcp-call.ts`'s
26
+ * hint), a 404 uses the caller-supplied message (e.g. "Feedback \"x\" not found").
27
+ */
28
+ export function mapFeedbackHttpError(err, notFoundMessage) {
29
+ if (err instanceof HttpError) {
30
+ if (err.status === 401) {
31
+ return new CliError('FEEDBACK_ERROR', "The stored private key was rejected. Run 'coolhand login --scope private' to re-authenticate.");
32
+ }
33
+ if (err.status === 404) {
34
+ return new CliError('FEEDBACK_ERROR', notFoundMessage);
35
+ }
36
+ return new CliError('FEEDBACK_ERROR', `Feedback request failed (${err.status}): ${err.message}`);
37
+ }
38
+ return new CliError('FEEDBACK_ERROR', `Feedback request failed: ${err.message}`);
39
+ }
40
+ //# sourceMappingURL=feedback-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback-client.js","sourceRoot":"","sources":["../../src/api/feedback-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;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAY,EAAE,eAAuB;IACxE,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,OAAO,IAAI,QAAQ,CAAC,gBAAgB,EAAE,4BAA4B,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,gBAAgB,EAAE,4BAA6B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9F,CAAC"}
@@ -1,14 +1,16 @@
1
1
  /**
2
- * Ask the server for the timestamp of the most recent log it already holds from this collector, so
2
+ * Ask the server for the timestamp of the most recent Claude Code log it already holds, so
3
3
  * `capture-sessions` only re-scans files newer than that. This is a server-authoritative cutoff that
4
4
  * survives local state-file loss or a reinstall.
5
5
  *
6
- * It NEVER throws: any problem (no api key, bad base url, 404, network failure, non-JSON body, or a
7
- * missing/invalid `last_created_at`) returns `null` so the caller can fall back to local state. The
8
- * companion server endpoint may not exist yet; a 404 is therefore an expected, non-fatal outcome.
6
+ * Queries the `/api/v2/llm_request_logs` index for the single newest `claude_code` row. The server
7
+ * has no default sort, so newest-first is requested explicitly via `q[s]`. Index reads require the
8
+ * client's PRIVATE key (`private_key`, or the `COOLHAND_PRIVATE_KEY` env var); the public ingest
9
+ * key is write-only and the server answers it with a 401.
9
10
  *
10
- * Auth mirrors the ingest path (`log-request.ts`): the public `api_key` sent as `X-API-Key`, since
11
- * the endpoint lives on the same `/api/v2/llm_request_logs` path.
11
+ * It NEVER throws: any problem (no private key, bad base url, 401, network failure, non-JSON body,
12
+ * an empty result set, or an invalid `created_at`) returns `null` so the caller can fall back to
13
+ * local state.
12
14
  *
13
15
  * `opts.clientId` is expected to be an **already-resolved** `client_id` string (e.g. from
14
16
  * `resolveClient`), not a raw user input. `getClient` is used here deliberately: by the time this
@@ -1 +1 @@
1
- {"version":3,"file":"last-sync.d.ts","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAuD1F"}
1
+ {"version":3,"file":"last-sync.d.ts","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,aAAa,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAmE1F"}
@@ -1,19 +1,18 @@
1
1
  import { loadConfig, getClient } from '../config.js';
2
2
  import { DEFAULT_BASE_URL } from '../types.js';
3
- import { PACKAGE_IDENTIFIER } from '../version.js';
4
- /** Identifies this tool's logs in the server's collector field (matches the ingest collector). */
5
- const COLLECTOR = `${PACKAGE_IDENTIFIER}/claude-code`;
6
3
  /**
7
- * Ask the server for the timestamp of the most recent log it already holds from this collector, so
4
+ * Ask the server for the timestamp of the most recent Claude Code log it already holds, so
8
5
  * `capture-sessions` only re-scans files newer than that. This is a server-authoritative cutoff that
9
6
  * survives local state-file loss or a reinstall.
10
7
  *
11
- * It NEVER throws: any problem (no api key, bad base url, 404, network failure, non-JSON body, or a
12
- * missing/invalid `last_created_at`) returns `null` so the caller can fall back to local state. The
13
- * companion server endpoint may not exist yet; a 404 is therefore an expected, non-fatal outcome.
8
+ * Queries the `/api/v2/llm_request_logs` index for the single newest `claude_code` row. The server
9
+ * has no default sort, so newest-first is requested explicitly via `q[s]`. Index reads require the
10
+ * client's PRIVATE key (`private_key`, or the `COOLHAND_PRIVATE_KEY` env var); the public ingest
11
+ * key is write-only and the server answers it with a 401.
14
12
  *
15
- * Auth mirrors the ingest path (`log-request.ts`): the public `api_key` sent as `X-API-Key`, since
16
- * the endpoint lives on the same `/api/v2/llm_request_logs` path.
13
+ * It NEVER throws: any problem (no private key, bad base url, 401, network failure, non-JSON body,
14
+ * an empty result set, or an invalid `created_at`) returns `null` so the caller can fall back to
15
+ * local state.
17
16
  *
18
17
  * `opts.clientId` is expected to be an **already-resolved** `client_id` string (e.g. from
19
18
  * `resolveClient`), not a raw user input. `getClient` is used here deliberately: by the time this
@@ -26,7 +25,7 @@ export async function fetchLastSync(opts = {}) {
26
25
  try {
27
26
  const cfg = await loadConfig();
28
27
  const client = getClient(cfg, opts.clientId);
29
- apiKey = client?.api_key ?? process.env.COOLHAND_API_KEY;
28
+ apiKey = client?.private_key ?? process.env.COOLHAND_PRIVATE_KEY;
30
29
  baseUrl = client?.base_url ?? DEFAULT_BASE_URL;
31
30
  }
32
31
  catch {
@@ -41,8 +40,10 @@ export async function fetchLastSync(opts = {}) {
41
40
  if (parsedBaseUrl.protocol !== 'http:' && parsedBaseUrl.protocol !== 'https:') {
42
41
  return null;
43
42
  }
44
- const endpoint = new URL('/api/v2/llm_request_logs/last_sync', parsedBaseUrl);
45
- endpoint.searchParams.set('collector', COLLECTOR);
43
+ const endpoint = new URL('/api/v2/llm_request_logs', parsedBaseUrl);
44
+ endpoint.searchParams.set('q[source_api_in][]', 'claude_code');
45
+ endpoint.searchParams.set('q[s]', 'created_at desc');
46
+ endpoint.searchParams.set('per', '1');
46
47
  url = endpoint.toString();
47
48
  }
48
49
  catch {
@@ -60,20 +61,30 @@ export async function fetchLastSync(opts = {}) {
60
61
  return null;
61
62
  }
62
63
  if (!res.ok) {
63
- // 404 (endpoint not built yet) or any other non-2xx — degrade to local state.
64
+ // 401 (key lacks read access) or any other non-2xx — degrade to local state.
64
65
  return null;
65
66
  }
66
67
  let body;
67
68
  try {
68
- body = (await res.json());
69
+ body = await res.json();
69
70
  }
70
71
  catch {
71
72
  return null;
72
73
  }
73
- if (typeof body.last_created_at !== 'string') {
74
+ // The index returns a bare JSON array; an empty one means the server holds no
75
+ // claude_code logs for this client yet.
76
+ if (!Array.isArray(body) || body.length === 0) {
74
77
  return null;
75
78
  }
76
- const date = new Date(body.last_created_at);
79
+ const newest = body[0];
80
+ if (newest === null || typeof newest !== 'object') {
81
+ return null;
82
+ }
83
+ const createdAt = newest.created_at;
84
+ if (typeof createdAt !== 'string') {
85
+ return null;
86
+ }
87
+ const date = new Date(createdAt);
77
88
  return Number.isNaN(date.getTime()) ? null : date;
78
89
  }
79
90
  //# sourceMappingURL=last-sync.js.map
@@ -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,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,kGAAkG;AAClG,MAAM,SAAS,GAAG,GAAG,kBAAkB,cAAc,CAAC;AAEtD;;;;;;;;;;;;;;;;GAgBG;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,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QACzD,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,aAAa,CAAC,QAAQ,KAAK,OAAO,IAAI,aAAa,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC9E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,oCAAoC,EAAE,aAAa,CAAC,CAAC;QAC9E,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAClD,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;SAC7D,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,8EAA8E;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,IAAyC,CAAC;IAC9C,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"last-sync.js","sourceRoot":"","sources":["../../src/api/last-sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
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. `getLogContent`/`searchLogs` require the private key — the public key used by
6
+ * `monitor`/`logRequest` will 401 here.
7
+ */
8
+ export declare function getLogClient(opts?: {
9
+ clientId?: string;
10
+ }): Promise<Coolhand>;
11
+ /**
12
+ * Maps an error thrown by `Coolhand#getLogContent`/`searchLogs` to a `CliError` with a clear,
13
+ * actionable message — a 401 means the stored private key was rejected (mirrors `feedback-client.ts`'s
14
+ * hint), a 404 uses the caller-supplied message (e.g. `Log "x" not found`).
15
+ */
16
+ export declare function mapLogHttpError(err: unknown, notFoundMessage: string): CliError;
17
+ //# sourceMappingURL=log-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-client.d.ts","sourceRoot":"","sources":["../../src/api/log-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoBtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,QAAQ,CAc/E"}
@@ -0,0 +1,40 @@
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. `getLogContent`/`searchLogs` require the private key — the public key used by
7
+ * `monitor`/`logRequest` will 401 here.
8
+ */
9
+ export async function getLogClient(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#getLogContent`/`searchLogs` to a `CliError` with a clear,
25
+ * actionable message — a 401 means the stored private key was rejected (mirrors `feedback-client.ts`'s
26
+ * hint), a 404 uses the caller-supplied message (e.g. `Log "x" not found`).
27
+ */
28
+ export function mapLogHttpError(err, notFoundMessage) {
29
+ if (err instanceof HttpError) {
30
+ if (err.status === 401) {
31
+ return new CliError('LOG_ERROR', "The stored private key was rejected. Run 'coolhand login --scope private' to re-authenticate.");
32
+ }
33
+ if (err.status === 404) {
34
+ return new CliError('LOG_ERROR', notFoundMessage);
35
+ }
36
+ return new CliError('LOG_ERROR', `Log request failed (${err.status}): ${err.message}`);
37
+ }
38
+ return new CliError('LOG_ERROR', `Log request failed: ${err.message}`);
39
+ }
40
+ //# sourceMappingURL=log-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-client.js","sourceRoot":"","sources":["../../src/api/log-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,YAAY,CAAC,OAA8B,EAAE;IACjE,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;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY,EAAE,eAAuB;IACnE,IAAI,GAAG,YAAY,SAAS,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CACjB,WAAW,EACX,+FAA+F,CAChG,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,uBAAuB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,uBAAwB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;AACpF,CAAC"}
package/dist/bin.js CHANGED
File without changes
package/dist/cli.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  interface ParsedArgs {
2
2
  command: string;
3
3
  positional: string[];
4
- flags: Record<string, string | boolean>;
4
+ flags: Record<string, string | boolean | string[]>;
5
5
  }
6
6
  /**
7
7
  * Strips a leading `--client-id VALUE` or `--client-id=VALUE` from argv,
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAuCA,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;CACzC;AA4OD;;;;;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,CAqCpD;AA+VD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkIzD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA+CA,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;AAuTD;;;;;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;AAokBD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0IzD"}