coolhand-cli 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -121
- package/dist/api/last-sync.d.ts +5 -0
- package/dist/api/last-sync.d.ts.map +1 -1
- package/dist/api/last-sync.js +5 -0
- package/dist/api/last-sync.js.map +1 -1
- package/dist/auth/callback-server.js +3 -3
- package/dist/auth/callback-server.js.map +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +296 -37
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze-claude-sessions.d.ts.map +1 -1
- package/dist/commands/analyze-claude-sessions.js +37 -6
- package/dist/commands/analyze-claude-sessions.js.map +1 -1
- package/dist/commands/claude.d.ts +22 -3
- package/dist/commands/claude.d.ts.map +1 -1
- package/dist/commands/claude.js +117 -49
- package/dist/commands/claude.js.map +1 -1
- package/dist/commands/clients.js +1 -1
- package/dist/commands/clients.js.map +1 -1
- package/dist/commands/flush-pending.d.ts +25 -0
- package/dist/commands/flush-pending.d.ts.map +1 -0
- package/dist/commands/flush-pending.js +100 -0
- package/dist/commands/flush-pending.js.map +1 -0
- package/dist/commands/get-workload.d.ts +3 -0
- package/dist/commands/get-workload.d.ts.map +1 -0
- package/dist/commands/get-workload.js +28 -0
- package/dist/commands/get-workload.js.map +1 -0
- package/dist/commands/list-workloads.d.ts +3 -0
- package/dist/commands/list-workloads.d.ts.map +1 -0
- package/dist/commands/list-workloads.js +52 -0
- package/dist/commands/list-workloads.js.map +1 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +41 -6
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/mcp-call.d.ts.map +1 -1
- package/dist/commands/mcp-call.js +57 -10
- package/dist/commands/mcp-call.js.map +1 -1
- package/dist/commands/status.js +2 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/update-workload.d.ts +3 -0
- package/dist/commands/update-workload.d.ts.map +1 -0
- package/dist/commands/update-workload.js +35 -0
- package/dist/commands/update-workload.js.map +1 -0
- package/dist/commands/whoami.js +1 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/wildcard.d.ts.map +1 -1
- package/dist/commands/wildcard.js +118 -20
- package/dist/commands/wildcard.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +127 -6
- package/dist/config.js.map +1 -1
- package/dist/pending-store.d.ts +31 -0
- package/dist/pending-store.d.ts.map +1 -0
- package/dist/pending-store.js +81 -0
- package/dist/pending-store.js.map +1 -0
- package/dist/prompt.d.ts +13 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +30 -0
- package/dist/prompt.js.map +1 -0
- package/dist/proxy/certs.d.ts +14 -0
- package/dist/proxy/certs.d.ts.map +1 -0
- package/dist/proxy/certs.js +64 -0
- package/dist/proxy/certs.js.map +1 -0
- package/dist/proxy/interceptor.d.ts +16 -0
- package/dist/proxy/interceptor.d.ts.map +1 -0
- package/dist/proxy/interceptor.js +57 -0
- package/dist/proxy/interceptor.js.map +1 -0
- package/dist/proxy/proxy.d.ts +18 -0
- package/dist/proxy/proxy.d.ts.map +1 -0
- package/dist/proxy/proxy.js +113 -0
- package/dist/proxy/proxy.js.map +1 -0
- package/dist/proxy/sender.d.ts +31 -0
- package/dist/proxy/sender.d.ts.map +1 -0
- package/dist/proxy/sender.js +66 -0
- package/dist/proxy/sender.js.map +1 -0
- package/dist/types.d.ts +24 -2
- 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/package.json +2 -2
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { type ClaudeOptions } from '../types.js';
|
|
3
|
+
import { startProxy, type ProxyInstance } from '../proxy/proxy.js';
|
|
4
|
+
import { getOrCreateCA } from '../proxy/certs.js';
|
|
3
5
|
interface ClaudeDeps {
|
|
4
6
|
spawnFn?: typeof spawn;
|
|
5
|
-
|
|
7
|
+
startProxyFn?: (ca: Awaited<ReturnType<typeof getOrCreateCA>>, opts: Parameters<typeof startProxy>[1]) => Promise<ProxyInstance>;
|
|
6
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* On Windows, .cmd shims cannot be spawned without a shell. Rather than using
|
|
11
|
+
* shell:true (which forwards args as an unquoted string to cmd.exe, enabling
|
|
12
|
+
* metacharacter injection), we invoke cmd.exe /d /s /c directly with each arg
|
|
13
|
+
* double-quoted and windowsVerbatimArguments:true so Node does not re-quote them.
|
|
14
|
+
*
|
|
15
|
+
* Quoting rules applied per arg:
|
|
16
|
+
* 1. Double trailing backslashes (a trailing \ would escape the closing ")
|
|
17
|
+
* 2. Escape embedded " as ""
|
|
18
|
+
* 3. Escape % as %% to prevent cmd.exe environment-variable expansion
|
|
19
|
+
* 4. Wrap in double quotes unconditionally
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveSpawn(args: string[], platform?: NodeJS.Platform): {
|
|
22
|
+
cmd: string;
|
|
23
|
+
spawnArgs: string[];
|
|
24
|
+
windowsVerbatimArguments?: true;
|
|
25
|
+
};
|
|
7
26
|
/**
|
|
8
27
|
* `coolhand claude [args...]` — run the Claude CLI behind the Coolhand proxy with
|
|
9
|
-
* the stored API key filled in.
|
|
10
|
-
*
|
|
28
|
+
* the stored API key filled in. Starts an in-process HTTPS MITM proxy and spawns
|
|
29
|
+
* claude with proxy env vars set.
|
|
11
30
|
*/
|
|
12
31
|
export declare function run(opts: ClaudeOptions, deps?: ClaudeDeps): Promise<number>;
|
|
13
32
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/commands/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/commands/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAItC,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAe,MAAM,mBAAmB,CAAC;AAE/D,UAAU,UAAU;IAClB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CAClI;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,kBAAmB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,wBAAwB,CAAC,EAAE,IAAI,CAAA;CAAE,CAgB/I;AAcD;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA2FrF"}
|
package/dist/commands/claude.js
CHANGED
|
@@ -1,29 +1,43 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
import { createRequire } from 'module';
|
|
3
|
-
import { realpathSync } from 'fs';
|
|
4
2
|
import { ExitCode, CliError } from '../errors.js';
|
|
5
3
|
import { logger, redact } from '../logger.js';
|
|
6
|
-
import { loadConfig,
|
|
4
|
+
import { loadConfig, resolveClient } from '../config.js';
|
|
7
5
|
import { DEFAULT_BASE_URL } from '../types.js';
|
|
6
|
+
import { startProxy } from '../proxy/proxy.js';
|
|
7
|
+
import { getOrCreateCA, getCertPath } from '../proxy/certs.js';
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* On Windows, .cmd shims cannot be spawned without a shell. Rather than using
|
|
10
|
+
* shell:true (which forwards args as an unquoted string to cmd.exe, enabling
|
|
11
|
+
* metacharacter injection), we invoke cmd.exe /d /s /c directly with each arg
|
|
12
|
+
* double-quoted and windowsVerbatimArguments:true so Node does not re-quote them.
|
|
13
|
+
*
|
|
14
|
+
* Quoting rules applied per arg:
|
|
15
|
+
* 1. Double trailing backslashes (a trailing \ would escape the closing ")
|
|
16
|
+
* 2. Escape embedded " as ""
|
|
17
|
+
* 3. Escape % as %% to prevent cmd.exe environment-variable expansion
|
|
18
|
+
* 4. Wrap in double quotes unconditionally
|
|
13
19
|
*/
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
export function resolveSpawn(args, platform = process.platform) {
|
|
21
|
+
if (platform !== 'win32') {
|
|
22
|
+
return { cmd: 'claude', spawnArgs: args };
|
|
23
|
+
}
|
|
24
|
+
const escaped = args.map((a) => {
|
|
25
|
+
const s = a
|
|
26
|
+
.replace(/(\\+)$/, '$1$1') // double trailing backslashes
|
|
27
|
+
.replace(/"/g, '""') // escape embedded quotes
|
|
28
|
+
.replace(/%/g, '%%'); // prevent env-var expansion
|
|
29
|
+
return `"${s}"`;
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
cmd: process.env['ComSpec'] ?? 'cmd.exe',
|
|
33
|
+
spawnArgs: ['/d', '/s', '/c', ['"claude"', ...escaped].join(' ')],
|
|
34
|
+
windowsVerbatimArguments: true,
|
|
35
|
+
};
|
|
21
36
|
}
|
|
22
37
|
/**
|
|
23
|
-
* Build the full ingest endpoint
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
26
|
-
* base_url (the proxy already targets the right endpoint).
|
|
38
|
+
* Build the full ingest endpoint for a given client's base_url.
|
|
39
|
+
* The CLI stores only the site base_url, so append the ingest path here.
|
|
40
|
+
* Returns undefined for the default base_url (the proxy already uses the right endpoint).
|
|
27
41
|
*/
|
|
28
42
|
function endpointForBaseUrl(baseUrl) {
|
|
29
43
|
if (!baseUrl || baseUrl === DEFAULT_BASE_URL) {
|
|
@@ -33,43 +47,94 @@ function endpointForBaseUrl(baseUrl) {
|
|
|
33
47
|
}
|
|
34
48
|
/**
|
|
35
49
|
* `coolhand claude [args...]` — run the Claude CLI behind the Coolhand proxy with
|
|
36
|
-
* the stored API key filled in.
|
|
37
|
-
*
|
|
50
|
+
* the stored API key filled in. Starts an in-process HTTPS MITM proxy and spawns
|
|
51
|
+
* claude with proxy env vars set.
|
|
38
52
|
*/
|
|
39
53
|
export async function run(opts, deps = {}) {
|
|
40
54
|
const spawnFn = deps.spawnFn ?? spawn;
|
|
41
|
-
const resolveProxyCli = deps.resolveProxyCli ?? defaultResolveProxyCli;
|
|
42
55
|
try {
|
|
43
56
|
const cfg = await loadConfig();
|
|
44
|
-
const entry =
|
|
45
|
-
if (!entry) {
|
|
46
|
-
throw new CliError('NOT_CONFIGURED', '
|
|
47
|
-
}
|
|
48
|
-
let proxyCli;
|
|
49
|
-
try {
|
|
50
|
-
proxyCli = resolveProxyCli();
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
logger.info('Error: could not locate the coolhand-proxy dependency. Try reinstalling coolhand-cli.');
|
|
54
|
-
return ExitCode.INTERNAL;
|
|
55
|
-
}
|
|
56
|
-
const wrapArgs = ['wrap', '--silent'];
|
|
57
|
-
const endpoint = endpointForBaseUrl(entry.base_url);
|
|
58
|
-
if (endpoint) {
|
|
59
|
-
wrapArgs.push('--api-endpoint', endpoint);
|
|
57
|
+
const entry = await resolveClient(cfg, opts.clientId);
|
|
58
|
+
if (!entry.api_key) {
|
|
59
|
+
throw new CliError('NOT_CONFIGURED', 'This client has no public API key — LLM capture requires the public key. Run `coolhand login` to re-authenticate.');
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
61
|
+
const ca = await getOrCreateCA();
|
|
62
|
+
const certPath = getCertPath();
|
|
63
|
+
const apiEndpoint = endpointForBaseUrl(entry.base_url);
|
|
64
|
+
const proxy = await (deps.startProxyFn ?? startProxy)(ca, {
|
|
65
|
+
apiKey: entry.api_key,
|
|
66
|
+
apiEndpoint,
|
|
67
|
+
silent: true,
|
|
68
|
+
});
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
let stopped = false;
|
|
71
|
+
// stopped is set synchronously before the first await so concurrent
|
|
72
|
+
// callers (error + close can both fire) see it immediately and skip.
|
|
73
|
+
const stopOnce = () => {
|
|
74
|
+
if (stopped) {
|
|
75
|
+
return Promise.resolve();
|
|
76
|
+
}
|
|
77
|
+
stopped = true;
|
|
78
|
+
return proxy.stop();
|
|
79
|
+
};
|
|
80
|
+
let child;
|
|
81
|
+
try {
|
|
82
|
+
const { cmd, spawnArgs, windowsVerbatimArguments } = resolveSpawn(opts.args);
|
|
83
|
+
child = spawnFn(cmd, spawnArgs, {
|
|
84
|
+
stdio: 'inherit',
|
|
85
|
+
windowsVerbatimArguments,
|
|
86
|
+
env: {
|
|
87
|
+
...process.env,
|
|
88
|
+
COOLHAND_API_KEY: entry.api_key,
|
|
89
|
+
HTTP_PROXY: `http://127.0.0.1:${proxy.port}`,
|
|
90
|
+
HTTPS_PROXY: `http://127.0.0.1:${proxy.port}`,
|
|
91
|
+
NO_PROXY: 'localhost,127.0.0.1,::1',
|
|
92
|
+
SSL_CERT_FILE: certPath,
|
|
93
|
+
NODE_EXTRA_CA_CERTS: certPath,
|
|
94
|
+
REQUESTS_CA_BUNDLE: certPath,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (spawnErr) {
|
|
99
|
+
if (spawnErr instanceof Error) {
|
|
100
|
+
logger.info(`Error: ${redact(spawnErr.message)}`);
|
|
101
|
+
}
|
|
102
|
+
// Chain .catch → .then so resolve() is called only after stop() drains,
|
|
103
|
+
// mirroring the error/close handler pattern and avoiding abandoned sends.
|
|
104
|
+
stopOnce()
|
|
105
|
+
.catch((e) => {
|
|
106
|
+
const msg = e instanceof Error ? redact(e.message) : String(e);
|
|
107
|
+
logger.info(`Error: ${msg}`);
|
|
108
|
+
})
|
|
109
|
+
.then(() => resolve(ExitCode.INTERNAL));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
child.on('error', async (err) => {
|
|
113
|
+
try {
|
|
114
|
+
await stopOnce();
|
|
115
|
+
}
|
|
116
|
+
catch (stopErr) {
|
|
117
|
+
if (stopErr instanceof Error) {
|
|
118
|
+
logger.info(`Error: ${redact(stopErr.message)}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
logger.info(`Error: ${redact(err.message)}`);
|
|
123
|
+
resolve(ExitCode.INTERNAL);
|
|
124
|
+
}
|
|
70
125
|
});
|
|
71
|
-
child.on('close', (code) => {
|
|
72
|
-
|
|
126
|
+
child.on('close', async (code) => {
|
|
127
|
+
try {
|
|
128
|
+
await stopOnce();
|
|
129
|
+
}
|
|
130
|
+
catch (stopErr) {
|
|
131
|
+
if (stopErr instanceof Error) {
|
|
132
|
+
logger.info(`Error: ${redact(stopErr.message)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
resolve(code ?? ExitCode.INTERNAL);
|
|
137
|
+
}
|
|
73
138
|
});
|
|
74
139
|
});
|
|
75
140
|
}
|
|
@@ -78,7 +143,10 @@ export async function run(opts, deps = {}) {
|
|
|
78
143
|
logger.info(`Error: ${redact(err.message)} [${err.code}]`);
|
|
79
144
|
return err.exitCode;
|
|
80
145
|
}
|
|
81
|
-
|
|
146
|
+
if (err instanceof Error) {
|
|
147
|
+
logger.info(`Error: ${redact(err.message)}`);
|
|
148
|
+
}
|
|
149
|
+
return ExitCode.INTERNAL;
|
|
82
150
|
}
|
|
83
151
|
}
|
|
84
152
|
//# sourceMappingURL=claude.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/commands/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/commands/claude.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/clients.js
CHANGED
|
@@ -33,7 +33,7 @@ async function useClient(clientId, opts) {
|
|
|
33
33
|
client: {
|
|
34
34
|
client_id: entry.client_id,
|
|
35
35
|
client_name: entry.client_name,
|
|
36
|
-
masked_token: maskToken(entry.api_key),
|
|
36
|
+
masked_token: entry.api_key ? maskToken(entry.api_key) : '(no public key)',
|
|
37
37
|
base_url: entry.base_url,
|
|
38
38
|
},
|
|
39
39
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/commands/clients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,UAAU,WAAW,CAAC,IAAoB;IAC7C,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACvE,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACzE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,QAAQ,CAAC,EAAE,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAoB;IAC7D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,mCAAmC,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,IAAI;YACR,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;YACxC,MAAM,EAAE;gBACN,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,YAAY,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/commands/clients.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
export interface FlushResult {
|
|
3
|
+
uploaded: number;
|
|
4
|
+
failed: number;
|
|
5
|
+
}
|
|
6
|
+
interface SpawnDeps {
|
|
7
|
+
spawnFn?: typeof spawn;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Upload every pending record. Deletes each on a confirmed success; on any failure the
|
|
11
|
+
* file is kept and the "flush failed" marker is set so the next command can remind the
|
|
12
|
+
* user (Option B). Clears the marker when everything uploaded cleanly.
|
|
13
|
+
*/
|
|
14
|
+
export declare function flushPending(): Promise<FlushResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Launch the flush as a detached background process so the foreground command (login,
|
|
17
|
+
* or a retry prompt) returns immediately. Mirrors the detached-spawn pattern used to
|
|
18
|
+
* open the browser in `auth/open-browser.ts`. Best-effort: any failure just means the
|
|
19
|
+
* records wait and retry on the next command.
|
|
20
|
+
*/
|
|
21
|
+
export declare function spawnBackgroundFlush(deps?: SpawnDeps): void;
|
|
22
|
+
/** Hidden `__flush-pending` command run by the detached background process. */
|
|
23
|
+
export declare function run(): Promise<number>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=flush-pending.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush-pending.d.ts","sourceRoot":"","sources":["../../src/commands/flush-pending.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAetC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,SAAS;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AA+BD;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CA4BzD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,SAAc,GAAG,IAAI,CAkB/D;AAED,+EAA+E;AAC/E,wBAAsB,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAG3C"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { Coolhand } from 'coolhand-node';
|
|
3
|
+
import { ExitCode } from '../errors.js';
|
|
4
|
+
import { logger, redact } from '../logger.js';
|
|
5
|
+
import { loadConfig, getClient } from '../config.js';
|
|
6
|
+
import { DEFAULT_BASE_URL } from '../types.js';
|
|
7
|
+
import { listPendingFiles, readPendingRecord, removePendingRecord, markFlushFailed, clearFlushFailed, } from '../pending-store.js';
|
|
8
|
+
/**
|
|
9
|
+
* Send a single saved record to Coolhand. Resolves the api key/base url from the
|
|
10
|
+
* stored client (or env) at flush time — by now the user is logged in. Returns true
|
|
11
|
+
* only when the server confirms the write; the SDK returns null (it does not throw)
|
|
12
|
+
* on a failed write, so a falsy result means "keep the file and retry later".
|
|
13
|
+
*/
|
|
14
|
+
async function uploadRecord(record) {
|
|
15
|
+
const cfg = await loadConfig();
|
|
16
|
+
const client = getClient(cfg, record.clientId);
|
|
17
|
+
const apiKey = client?.api_key ?? process.env.COOLHAND_API_KEY;
|
|
18
|
+
if (!apiKey) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const baseUrl = client?.base_url ?? DEFAULT_BASE_URL;
|
|
22
|
+
let coolhand;
|
|
23
|
+
try {
|
|
24
|
+
coolhand = new Coolhand({ apiKey, baseUrl, silent: true });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (record.kind === 'feedback') {
|
|
30
|
+
const result = await coolhand.createFeedback(record.payload);
|
|
31
|
+
return result !== null && result !== undefined;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Upload every pending record. Deletes each on a confirmed success; on any failure the
|
|
37
|
+
* file is kept and the "flush failed" marker is set so the next command can remind the
|
|
38
|
+
* user (Option B). Clears the marker when everything uploaded cleanly.
|
|
39
|
+
*/
|
|
40
|
+
export async function flushPending() {
|
|
41
|
+
const files = await listPendingFiles();
|
|
42
|
+
let uploaded = 0;
|
|
43
|
+
let failed = 0;
|
|
44
|
+
for (const file of files) {
|
|
45
|
+
try {
|
|
46
|
+
const record = await readPendingRecord(file);
|
|
47
|
+
const ok = await uploadRecord(record);
|
|
48
|
+
if (ok) {
|
|
49
|
+
await removePendingRecord(file);
|
|
50
|
+
uploaded += 1;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
failed += 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
failed += 1;
|
|
58
|
+
logger.warn(`Failed to flush ${file}: ${redact(err.message)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (failed > 0) {
|
|
62
|
+
await markFlushFailed();
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await clearFlushFailed();
|
|
66
|
+
}
|
|
67
|
+
return { uploaded, failed };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Launch the flush as a detached background process so the foreground command (login,
|
|
71
|
+
* or a retry prompt) returns immediately. Mirrors the detached-spawn pattern used to
|
|
72
|
+
* open the browser in `auth/open-browser.ts`. Best-effort: any failure just means the
|
|
73
|
+
* records wait and retry on the next command.
|
|
74
|
+
*/
|
|
75
|
+
export function spawnBackgroundFlush(deps = {}) {
|
|
76
|
+
const spawnFn = deps.spawnFn ?? spawn;
|
|
77
|
+
const cliPath = process.argv[1];
|
|
78
|
+
if (!cliPath) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const child = spawnFn(process.execPath, [cliPath, '__flush-pending'], {
|
|
83
|
+
detached: true,
|
|
84
|
+
stdio: 'ignore',
|
|
85
|
+
});
|
|
86
|
+
child.on('error', () => {
|
|
87
|
+
// Swallow: the records remain in the pending store and retry next time.
|
|
88
|
+
});
|
|
89
|
+
child.unref();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Best-effort; nothing is lost because failed records stay in the store.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Hidden `__flush-pending` command run by the detached background process. */
|
|
96
|
+
export async function run() {
|
|
97
|
+
const { failed } = await flushPending();
|
|
98
|
+
return failed === 0 ? ExitCode.OK : ExitCode.USER_ERROR;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=flush-pending.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush-pending.js","sourceRoot":"","sources":["../../src/commands/flush-pending.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-workload.d.ts","sourceRoot":"","sources":["../../src/commands/get-workload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,wBAAsB,GAAG,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBnE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CliError, ExitCode } from '../errors.js';
|
|
2
|
+
import { logger, redact } from '../logger.js';
|
|
3
|
+
import { mcpCall } from './mcp-call.js';
|
|
4
|
+
export async function run(opts) {
|
|
5
|
+
try {
|
|
6
|
+
const result = await mcpCall('get_workload', { workload_id: opts.id }, { clientId: opts.clientId });
|
|
7
|
+
if (opts.json) {
|
|
8
|
+
logger.json({ ok: true, result });
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
logger.info(JSON.stringify(result, null, 2));
|
|
12
|
+
}
|
|
13
|
+
return ExitCode.OK;
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
if (err instanceof CliError) {
|
|
17
|
+
if (opts.json) {
|
|
18
|
+
logger.json({ ok: false, error: err.code, message: redact(err.message) });
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
logger.info(`Error: ${redact(err.message)} [${err.code}]`);
|
|
22
|
+
}
|
|
23
|
+
return err.exitCode;
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=get-workload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-workload.js","sourceRoot":"","sources":["../../src/commands/get-workload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAwB;IAChD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEpG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5E,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,GAAG,CAAC,QAAQ,CAAC;QACtB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-workloads.d.ts","sourceRoot":"","sources":["../../src/commands/list-workloads.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,wBAAsB,GAAG,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CrE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { CliError, ExitCode } from '../errors.js';
|
|
2
|
+
import { logger, redact } from '../logger.js';
|
|
3
|
+
import { mcpCall } from './mcp-call.js';
|
|
4
|
+
export async function run(opts) {
|
|
5
|
+
try {
|
|
6
|
+
const args = {};
|
|
7
|
+
if (opts.search !== undefined) {
|
|
8
|
+
args.search = opts.search;
|
|
9
|
+
}
|
|
10
|
+
if (opts.page !== undefined) {
|
|
11
|
+
args.page = opts.page;
|
|
12
|
+
}
|
|
13
|
+
if (opts.perPage !== undefined) {
|
|
14
|
+
args.per_page = opts.perPage;
|
|
15
|
+
}
|
|
16
|
+
if (opts.includeArchived === true) {
|
|
17
|
+
args.include_archived = true;
|
|
18
|
+
}
|
|
19
|
+
if (opts.includeSystem === true) {
|
|
20
|
+
args.include_system = true;
|
|
21
|
+
}
|
|
22
|
+
if (opts.includeTemplates === true) {
|
|
23
|
+
args.include_templates = true;
|
|
24
|
+
}
|
|
25
|
+
const result = await mcpCall('list_workloads', args, { clientId: opts.clientId });
|
|
26
|
+
if (opts.json) {
|
|
27
|
+
logger.json({ ok: true, result });
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const r = result;
|
|
31
|
+
if (r && typeof r.total === 'number' && typeof r.page === 'number' && typeof r.per_page === 'number') {
|
|
32
|
+
const totalPages = Math.ceil(r.total / r.per_page);
|
|
33
|
+
logger.info(`Page ${r.page} of ${totalPages} (${r.total} total) — use --page N to navigate`);
|
|
34
|
+
}
|
|
35
|
+
logger.info(JSON.stringify(result, null, 2));
|
|
36
|
+
}
|
|
37
|
+
return ExitCode.OK;
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err instanceof CliError) {
|
|
41
|
+
if (opts.json) {
|
|
42
|
+
logger.json({ ok: false, error: err.code, message: redact(err.message) });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
logger.info(`Error: ${redact(err.message)} [${err.code}]`);
|
|
46
|
+
}
|
|
47
|
+
return err.exitCode;
|
|
48
|
+
}
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=list-workloads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-workloads.js","sourceRoot":"","sources":["../../src/commands/list-workloads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAA0B;IAClD,IAAI,CAAC;QACH,MAAM,IAAI,GAA4B,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACxB,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;YAClC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC/B,CAAC;QACD,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,gBAAgB,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAElF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,MAAiC,CAAC;YAC5C,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACrG,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;gBACnD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,OAAO,UAAU,KAAK,CAAC,CAAC,KAAK,oCAAoC,CAAC,CAAC;YAC/F,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5E,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,GAAG,CAAC,QAAQ,CAAC;QACtB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAWA,OAAO,EAA0D,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAkDxG,wBAAsB,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAwH7D"}
|
package/dist/commands/login.js
CHANGED
|
@@ -6,7 +6,35 @@ import { writeEnvKey } from '../env-file.js';
|
|
|
6
6
|
import { startCallbackServer } from '../auth/callback-server.js';
|
|
7
7
|
import { openBrowser } from '../auth/open-browser.js';
|
|
8
8
|
import { generateState } from '../auth/state.js';
|
|
9
|
+
import { countPending } from '../pending-store.js';
|
|
10
|
+
import { confirm } from '../prompt.js';
|
|
11
|
+
import { spawnBackgroundFlush } from './flush-pending.js';
|
|
9
12
|
import { DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS } from '../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* After a successful login, offer to upload any blocker feedback saved while logged out.
|
|
15
|
+
* On yes, the upload runs in a detached background process so the user is not blocked.
|
|
16
|
+
* Skipped entirely when not interactive (`confirm` returns false with no TTY).
|
|
17
|
+
*/
|
|
18
|
+
async function offerPendingFlush() {
|
|
19
|
+
let count;
|
|
20
|
+
try {
|
|
21
|
+
count = await countPending();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (count === 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const send = await confirm(`You have ${count} locally-saved record(s) from while you were logged out. Send them now?`);
|
|
30
|
+
if (send) {
|
|
31
|
+
spawnBackgroundFlush();
|
|
32
|
+
logger.info(`Uploading ${count} saved record(s) in the background.`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
logger.info(`Keeping ${count} saved record(s) locally; you'll be offered again next time.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
10
38
|
function parseBaseUrl(input) {
|
|
11
39
|
const raw = input ?? DEFAULT_BASE_URL;
|
|
12
40
|
let parsed;
|
|
@@ -72,15 +100,18 @@ export async function run(opts) {
|
|
|
72
100
|
logger.info(`Waiting for browser authentication... (Ctrl+C to cancel; ${Math.round(timeoutMs / 1000)}s timeout)`);
|
|
73
101
|
await openBrowser(authUrl.toString());
|
|
74
102
|
const callback = await handle.result;
|
|
103
|
+
const privateToken = callback.private_token;
|
|
75
104
|
const wantsPrivate = opts.scope === 'private';
|
|
76
|
-
if (wantsPrivate && !
|
|
77
|
-
|
|
105
|
+
if (wantsPrivate && !privateToken) {
|
|
106
|
+
logger.info('Note: MCP access (private key) was not granted — re-authenticate and approve the private key on the consent page to enable it.');
|
|
107
|
+
}
|
|
108
|
+
if (!callback.token) {
|
|
109
|
+
logger.info('Note: LLM capture (public key) was not granted — `coolhand claude` will not work. Re-authenticate and approve the public key on the consent page to enable it.');
|
|
78
110
|
}
|
|
79
|
-
const privateToken = wantsPrivate ? callback.private_token : undefined;
|
|
80
111
|
const entry = {
|
|
81
112
|
client_id: callback.clientId,
|
|
82
113
|
client_name: callback.clientName,
|
|
83
|
-
api_key: callback.token,
|
|
114
|
+
...(callback.token ? { api_key: callback.token } : {}),
|
|
84
115
|
...(privateToken ? { private_key: privateToken } : {}),
|
|
85
116
|
base_url: baseUrl.origin,
|
|
86
117
|
saved_at: new Date().toISOString(),
|
|
@@ -89,7 +120,9 @@ export async function run(opts) {
|
|
|
89
120
|
let envResult;
|
|
90
121
|
let privateEnvResult;
|
|
91
122
|
if (opts.writeEnv) {
|
|
92
|
-
|
|
123
|
+
if (callback.token) {
|
|
124
|
+
envResult = await writeEnvKey(opts.writeEnv, 'COOLHAND_API_KEY', callback.token);
|
|
125
|
+
}
|
|
93
126
|
if (privateToken) {
|
|
94
127
|
privateEnvResult = await writeEnvKey(opts.writeEnv, 'COOLHAND_PRIVATE_KEY', privateToken);
|
|
95
128
|
}
|
|
@@ -97,7 +130,7 @@ export async function run(opts) {
|
|
|
97
130
|
if (opts.json) {
|
|
98
131
|
logger.json({
|
|
99
132
|
ok: true,
|
|
100
|
-
masked_token: maskToken(callback.token),
|
|
133
|
+
...(callback.token ? { masked_token: maskToken(callback.token) } : {}),
|
|
101
134
|
...(privateToken ? { masked_private_key: maskToken(privateToken) } : {}),
|
|
102
135
|
client_id: callback.clientId,
|
|
103
136
|
client_name: callback.clientName,
|
|
@@ -122,6 +155,8 @@ export async function run(opts) {
|
|
|
122
155
|
const action = privateEnvResult.created ? 'created' : privateEnvResult.replaced ? 'updated' : 'appended to';
|
|
123
156
|
logger.info(`COOLHAND_PRIVATE_KEY ${action} ${privateEnvResult.path}.`);
|
|
124
157
|
}
|
|
158
|
+
// Only prompt in human (non-JSON) mode; JSON output is for machine consumption.
|
|
159
|
+
await offerPendingFlush();
|
|
125
160
|
}
|
|
126
161
|
return ExitCode.OK;
|
|
127
162
|
}
|