copillm 0.2.1 → 0.2.2
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/dist/agents/registry.js +72 -0
- package/dist/auth/credentials.js +80 -29
- package/dist/cli.js +62 -12
- package/package.json +2 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-agent capability registry. The single source of truth for agent-specific
|
|
3
|
+
* behaviour that copillm needs to know about (yolo mapping, future: model
|
|
4
|
+
* pinning, debug surfaces, etc.). Adding a new agent should be one row here
|
|
5
|
+
* plus wiring in `src/cli.ts` — never a new branch in the action handlers.
|
|
6
|
+
*
|
|
7
|
+
* Note: the `AgentName` union lives in `../integrations/registry.ts` (paired
|
|
8
|
+
* with npm package / bin name metadata). We reuse it here so the two
|
|
9
|
+
* registries can never diverge.
|
|
10
|
+
*/
|
|
11
|
+
export const AGENTS = {
|
|
12
|
+
claude: {
|
|
13
|
+
name: "claude",
|
|
14
|
+
yolo: { mode: "inject", flags: ["--dangerously-skip-permissions"] }
|
|
15
|
+
},
|
|
16
|
+
codex: {
|
|
17
|
+
name: "codex",
|
|
18
|
+
yolo: { mode: "inject", flags: ["--dangerously-bypass-approvals-and-sandbox"] }
|
|
19
|
+
},
|
|
20
|
+
copilot: {
|
|
21
|
+
name: "copilot",
|
|
22
|
+
yolo: { mode: "inject", flags: ["--allow-all"] }
|
|
23
|
+
},
|
|
24
|
+
pi: {
|
|
25
|
+
name: "pi",
|
|
26
|
+
yolo: {
|
|
27
|
+
mode: "unsupported",
|
|
28
|
+
reason: "pi has no blanket-approve flag; use its per-tool approvals instead"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Resolve `--yolo` for a given agent and return the (possibly transformed)
|
|
34
|
+
* argv to forward downstream. Pure function aside from the optional warning
|
|
35
|
+
* sink — easy to unit-test.
|
|
36
|
+
*/
|
|
37
|
+
export function applyYolo(options) {
|
|
38
|
+
const args = [...options.userArgs];
|
|
39
|
+
if (!options.yolo)
|
|
40
|
+
return args;
|
|
41
|
+
const spec = AGENTS[options.agent].yolo;
|
|
42
|
+
switch (spec.mode) {
|
|
43
|
+
case "inject": {
|
|
44
|
+
const alreadyPresent = spec.flags.some((flag) => args.includes(flag));
|
|
45
|
+
if (alreadyPresent)
|
|
46
|
+
return args;
|
|
47
|
+
return [...spec.flags, ...args];
|
|
48
|
+
}
|
|
49
|
+
case "passthrough": {
|
|
50
|
+
if (args.includes("--yolo"))
|
|
51
|
+
return args;
|
|
52
|
+
return ["--yolo", ...args];
|
|
53
|
+
}
|
|
54
|
+
case "unsupported": {
|
|
55
|
+
const warn = options.warn ?? ((line) => process.stderr.write(`${line}\n`));
|
|
56
|
+
warn(`copillm: --yolo ignored for ${options.agent} (${spec.reason})`);
|
|
57
|
+
return args;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read the `COPILLM_YOLO` env var as a boolean. Accepts "1", "true", "yes"
|
|
63
|
+
* (case-insensitive) as truthy; everything else (including unset) is false.
|
|
64
|
+
*/
|
|
65
|
+
export function yoloFromEnv(env = process.env) {
|
|
66
|
+
const raw = env.COPILLM_YOLO?.trim().toLowerCase();
|
|
67
|
+
return raw === "1" || raw === "true" || raw === "yes";
|
|
68
|
+
}
|
|
69
|
+
/** Combine the per-launch flag with the env var fallback. */
|
|
70
|
+
export function resolveYolo(flag, env = process.env) {
|
|
71
|
+
return Boolean(flag) || yoloFromEnv(env);
|
|
72
|
+
}
|
package/dist/auth/credentials.js
CHANGED
|
@@ -18,16 +18,57 @@ let sessionCredential = null;
|
|
|
18
18
|
function forceSessionBackend() {
|
|
19
19
|
return process.env.COPILLM_FORCE_SESSION_BACKEND === "1";
|
|
20
20
|
}
|
|
21
|
-
async function
|
|
21
|
+
async function tryImportKeyring() {
|
|
22
22
|
if (forceSessionBackend()) {
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
|
-
const mod = await import("
|
|
27
|
-
return
|
|
26
|
+
const mod = (await import("@napi-rs/keyring"));
|
|
27
|
+
// Test seam: mocks can return `null` or `{ default: null }` to simulate an
|
|
28
|
+
// unavailable backend without throwing from the vi.mock factory (which
|
|
29
|
+
// confuses vitest's hoisting and our isMissingKeyringError check).
|
|
30
|
+
if (!mod) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const AsyncEntry = mod.AsyncEntry ?? (mod.default && typeof mod.default === "object" ? mod.default.AsyncEntry : undefined);
|
|
34
|
+
if (typeof AsyncEntry !== "function") {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
async getPassword(service, account) {
|
|
39
|
+
const entry = new AsyncEntry(service, account);
|
|
40
|
+
try {
|
|
41
|
+
const value = await entry.getPassword();
|
|
42
|
+
return value ?? null;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (isNoEntryError(error)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
async setPassword(service, account, password) {
|
|
52
|
+
const entry = new AsyncEntry(service, account);
|
|
53
|
+
await entry.setPassword(password);
|
|
54
|
+
},
|
|
55
|
+
async deletePassword(service, account) {
|
|
56
|
+
const entry = new AsyncEntry(service, account);
|
|
57
|
+
try {
|
|
58
|
+
await entry.deletePassword();
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (isNoEntryError(error)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
28
69
|
}
|
|
29
70
|
catch (error) {
|
|
30
|
-
if (
|
|
71
|
+
if (isMissingKeyringError(error)) {
|
|
31
72
|
return null;
|
|
32
73
|
}
|
|
33
74
|
if (error instanceof Error) {
|
|
@@ -36,15 +77,25 @@ async function tryImportKeytar() {
|
|
|
36
77
|
throw new Error("Failed to initialize OS keychain backend: unknown error");
|
|
37
78
|
}
|
|
38
79
|
}
|
|
39
|
-
|
|
80
|
+
// keyring-rs returns a "NoEntry" error when an item doesn't exist. Map that to
|
|
81
|
+
// null/false to preserve the keytar-style "missing is not an error" semantics
|
|
82
|
+
// that the rest of credentials.ts is built around.
|
|
83
|
+
function isNoEntryError(error) {
|
|
84
|
+
if (!(error instanceof Error)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const message = error.message.toLowerCase();
|
|
88
|
+
return message.includes("no matching entry") || message.includes("no entry") || message.includes("noentry");
|
|
89
|
+
}
|
|
90
|
+
async function resolveKeyring() {
|
|
40
91
|
if (forceSessionBackend()) {
|
|
41
|
-
return {
|
|
92
|
+
return { keyring: null, reason: "forced_session_backend" };
|
|
42
93
|
}
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
return {
|
|
94
|
+
const keyring = await tryImportKeyring();
|
|
95
|
+
if (keyring) {
|
|
96
|
+
return { keyring, reason: null };
|
|
46
97
|
}
|
|
47
|
-
return {
|
|
98
|
+
return { keyring: null, reason: "keyring module is unavailable on this machine" };
|
|
48
99
|
}
|
|
49
100
|
function parseCredentialFile() {
|
|
50
101
|
const path = credentialsReadPath();
|
|
@@ -86,13 +137,13 @@ function canUsePlaintextFallback() {
|
|
|
86
137
|
}
|
|
87
138
|
return process.stdin.isTTY || process.env.COPILLM_ALLOW_PLAINTEXT_CREDENTIALS === "1";
|
|
88
139
|
}
|
|
89
|
-
function
|
|
140
|
+
function isMissingKeyringError(error) {
|
|
90
141
|
if (!(error instanceof Error)) {
|
|
91
142
|
return false;
|
|
92
143
|
}
|
|
93
144
|
const message = error.message.toLowerCase();
|
|
94
|
-
return (message.includes("cannot find package '
|
|
95
|
-
message.includes("cannot find module '
|
|
145
|
+
return (message.includes("cannot find package '@napi-rs/keyring") ||
|
|
146
|
+
message.includes("cannot find module '@napi-rs/keyring") ||
|
|
96
147
|
message.includes("module not found"));
|
|
97
148
|
}
|
|
98
149
|
/**
|
|
@@ -108,14 +159,14 @@ export async function inspectStoredCredential() {
|
|
|
108
159
|
if (fs.existsSync(credentialsReadPath())) {
|
|
109
160
|
return { stored: true, backend: "file" };
|
|
110
161
|
}
|
|
111
|
-
const {
|
|
112
|
-
if (!
|
|
162
|
+
const { keyring } = await resolveKeyring();
|
|
163
|
+
if (!keyring) {
|
|
113
164
|
return { stored: false, backend: null };
|
|
114
165
|
}
|
|
115
166
|
try {
|
|
116
|
-
const token = await
|
|
167
|
+
const token = await keyring.getPassword(SERVICE, ACCOUNT);
|
|
117
168
|
if (token) {
|
|
118
|
-
return { stored: true, backend: "
|
|
169
|
+
return { stored: true, backend: "keyring" };
|
|
119
170
|
}
|
|
120
171
|
return { stored: false, backend: null };
|
|
121
172
|
}
|
|
@@ -134,13 +185,13 @@ export async function loadStoredCredential() {
|
|
|
134
185
|
const parsed = parseCredentialFile();
|
|
135
186
|
return { token: parsed.token, accountType: parsed.accountType, source: "file" };
|
|
136
187
|
}
|
|
137
|
-
const {
|
|
138
|
-
if (!
|
|
188
|
+
const { keyring } = await resolveKeyring();
|
|
189
|
+
if (!keyring) {
|
|
139
190
|
return null;
|
|
140
191
|
}
|
|
141
192
|
let token;
|
|
142
193
|
try {
|
|
143
|
-
token = await
|
|
194
|
+
token = await keyring.getPassword(SERVICE, ACCOUNT);
|
|
144
195
|
}
|
|
145
196
|
catch (error) {
|
|
146
197
|
if (error instanceof Error) {
|
|
@@ -151,7 +202,7 @@ export async function loadStoredCredential() {
|
|
|
151
202
|
if (!token) {
|
|
152
203
|
return null;
|
|
153
204
|
}
|
|
154
|
-
return { token, accountType: "individual", source: "
|
|
205
|
+
return { token, accountType: "individual", source: "keyring" };
|
|
155
206
|
}
|
|
156
207
|
export async function saveStoredCredential(token, accountType, options = {}) {
|
|
157
208
|
const mode = options.mode ?? "auto";
|
|
@@ -164,11 +215,11 @@ export async function saveStoredCredential(token, accountType, options = {}) {
|
|
|
164
215
|
writeCredentialFile(token, accountType);
|
|
165
216
|
return "file";
|
|
166
217
|
}
|
|
167
|
-
const {
|
|
168
|
-
if (
|
|
218
|
+
const { keyring, reason } = await resolveKeyring();
|
|
219
|
+
if (keyring) {
|
|
169
220
|
try {
|
|
170
|
-
await
|
|
171
|
-
return "
|
|
221
|
+
await keyring.setPassword(SERVICE, ACCOUNT, token);
|
|
222
|
+
return "keyring";
|
|
172
223
|
}
|
|
173
224
|
catch (error) {
|
|
174
225
|
if (error instanceof Error) {
|
|
@@ -196,11 +247,11 @@ export async function clearStoredCredential() {
|
|
|
196
247
|
}
|
|
197
248
|
return { backend: "file", removed: true };
|
|
198
249
|
}
|
|
199
|
-
const {
|
|
200
|
-
if (
|
|
250
|
+
const { keyring, reason } = await resolveKeyring();
|
|
251
|
+
if (keyring) {
|
|
201
252
|
try {
|
|
202
|
-
const removed = await
|
|
203
|
-
return { backend: "
|
|
253
|
+
const removed = await keyring.deletePassword(SERVICE, ACCOUNT);
|
|
254
|
+
return { backend: "keyring", removed: removed || hadSession };
|
|
204
255
|
}
|
|
205
256
|
catch (error) {
|
|
206
257
|
if (error instanceof Error) {
|
package/dist/cli.js
CHANGED
|
@@ -24,6 +24,7 @@ import { isShellSyntax, renderEnvBlock } from "./cli/envBlock.js";
|
|
|
24
24
|
import { buildClaudeEnvBundle, buildCodexEnvBundle, buildPiEnvBundle } from "./cli/agentEnv.js";
|
|
25
25
|
import { launchAgent } from "./cli/launchAgent.js";
|
|
26
26
|
import { applyAgentConfig, formatApplyNotes } from "./agentconfig/apply.js";
|
|
27
|
+
import { applyYolo, resolveYolo } from "./agents/registry.js";
|
|
27
28
|
import { registerConfigCommands } from "./cli/configCommands.js";
|
|
28
29
|
import { installProcessSafetyNet } from "./cli/processSafetyNet.js";
|
|
29
30
|
const logger = createLogger();
|
|
@@ -304,11 +305,19 @@ program
|
|
|
304
305
|
.action(async (opts) => {
|
|
305
306
|
const debug = resolveCopillmDebug(opts.debug);
|
|
306
307
|
enableRuntimeDebug(debug);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
try {
|
|
309
|
+
const started = await runDaemon({ debug });
|
|
310
|
+
if (started.kind === "already_running") {
|
|
311
|
+
process.exit(0);
|
|
312
|
+
}
|
|
313
|
+
process.stdout.write(`copillm listening on http://127.0.0.1:${started.port}${debug ? " [debug]" : ""}\n`);
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
317
|
+
logger.fatal({ err }, "daemon failed to start");
|
|
318
|
+
process.stderr.write(`copillm daemon: ${message}\n`);
|
|
319
|
+
process.exit(1);
|
|
310
320
|
}
|
|
311
|
-
process.stdout.write(`copillm listening on http://127.0.0.1:${started.port}${debug ? " [debug]" : ""}\n`);
|
|
312
321
|
});
|
|
313
322
|
program
|
|
314
323
|
.command("stop")
|
|
@@ -666,6 +675,7 @@ program
|
|
|
666
675
|
.option("--copillm-debug", "Enable debug endpoints when auto-starting daemon")
|
|
667
676
|
.option("--copillm-profile <name>", "Override active profile from ~/.copillm/agent.toml for this launch")
|
|
668
677
|
.option("--copillm-no-config", "Skip agent.toml fan-out for this launch", false)
|
|
678
|
+
.option("--yolo", "Skip approvals/sandbox (injects --dangerously-bypass-approvals-and-sandbox). Env: COPILLM_YOLO")
|
|
669
679
|
.allowUnknownOption(true)
|
|
670
680
|
.passThroughOptions()
|
|
671
681
|
.helpOption(false)
|
|
@@ -691,9 +701,11 @@ program
|
|
|
691
701
|
process.stderr.write(`${line}\n`);
|
|
692
702
|
}
|
|
693
703
|
const env = { ...bundle.env, ...applyResult.envOverlay };
|
|
704
|
+
const baseArgs = [...(forwardedArgs ?? []), ...applyResult.cliArgs];
|
|
705
|
+
const args = applyYolo({ agent: "codex", userArgs: baseArgs, yolo: resolveYolo(opts.yolo) });
|
|
694
706
|
const exitCode = await launchAgent({
|
|
695
707
|
agent: "codex",
|
|
696
|
-
args
|
|
708
|
+
args,
|
|
697
709
|
env,
|
|
698
710
|
pinnedSpec
|
|
699
711
|
});
|
|
@@ -706,6 +718,7 @@ program
|
|
|
706
718
|
.option("--copillm-debug", "Enable debug endpoints when auto-starting daemon")
|
|
707
719
|
.option("--copillm-profile <name>", "Override active profile from ~/.copillm/agent.toml for this launch")
|
|
708
720
|
.option("--copillm-no-config", "Skip agent.toml fan-out for this launch", false)
|
|
721
|
+
.option("--yolo", "Skip permission prompts (injects --dangerously-skip-permissions). Env: COPILLM_YOLO")
|
|
709
722
|
.allowUnknownOption(true)
|
|
710
723
|
.passThroughOptions()
|
|
711
724
|
.helpOption(false)
|
|
@@ -730,9 +743,11 @@ program
|
|
|
730
743
|
process.stderr.write(`${line}\n`);
|
|
731
744
|
}
|
|
732
745
|
const env = { ...claude.bundle.env, ...applyResult.envOverlay };
|
|
746
|
+
const baseArgs = [...(forwardedArgs ?? []), ...applyResult.cliArgs];
|
|
747
|
+
const args = applyYolo({ agent: "claude", userArgs: baseArgs, yolo: resolveYolo(opts.yolo) });
|
|
733
748
|
const exitCode = await launchAgent({
|
|
734
749
|
agent: "claude",
|
|
735
|
-
args
|
|
750
|
+
args,
|
|
736
751
|
env,
|
|
737
752
|
pinnedSpec
|
|
738
753
|
});
|
|
@@ -745,6 +760,7 @@ program
|
|
|
745
760
|
.option("--copillm-debug", "Enable debug endpoints when auto-starting daemon")
|
|
746
761
|
.option("--copillm-profile <name>", "Override active profile from ~/.copillm/agent.toml for this launch")
|
|
747
762
|
.option("--copillm-no-config", "Skip agent.toml fan-out for this launch", false)
|
|
763
|
+
.option("--yolo", "Skip approvals if supported (pi has no equivalent; emits a warning). Env: COPILLM_YOLO")
|
|
748
764
|
.allowUnknownOption(true)
|
|
749
765
|
.passThroughOptions()
|
|
750
766
|
.helpOption(false)
|
|
@@ -769,9 +785,11 @@ program
|
|
|
769
785
|
process.stderr.write(`${line}\n`);
|
|
770
786
|
}
|
|
771
787
|
const env = { ...bundle.env, ...applyResult.envOverlay };
|
|
788
|
+
const baseArgs = [...(forwardedArgs ?? []), ...applyResult.cliArgs];
|
|
789
|
+
const args = applyYolo({ agent: "pi", userArgs: baseArgs, yolo: resolveYolo(opts.yolo) });
|
|
772
790
|
const exitCode = await launchAgent({
|
|
773
791
|
agent: "pi",
|
|
774
|
-
args
|
|
792
|
+
args,
|
|
775
793
|
env,
|
|
776
794
|
pinnedSpec
|
|
777
795
|
});
|
|
@@ -783,6 +801,7 @@ program
|
|
|
783
801
|
.option("--copillm-use <spec>", "Pin copilot package version (e.g. 1.0.52 or @github/copilot@1.0.52)")
|
|
784
802
|
.option("--copillm-profile <name>", "Override active profile from ~/.copillm/agent.toml for this launch")
|
|
785
803
|
.option("--copillm-no-config", "Skip agent.toml fan-out for this launch", false)
|
|
804
|
+
.option("--yolo", "Allow all tools/paths/URLs (injects --allow-all). Env: COPILLM_YOLO")
|
|
786
805
|
.allowUnknownOption(true)
|
|
787
806
|
.passThroughOptions()
|
|
788
807
|
.helpOption(false)
|
|
@@ -812,9 +831,11 @@ program
|
|
|
812
831
|
...applyResult.envOverlay,
|
|
813
832
|
COPILOT_GITHUB_TOKEN: credential.token
|
|
814
833
|
};
|
|
834
|
+
const baseArgs = [...(forwardedArgs ?? []), ...applyResult.cliArgs];
|
|
835
|
+
const args = applyYolo({ agent: "copilot", userArgs: baseArgs, yolo: resolveYolo(opts.yolo) });
|
|
815
836
|
const exitCode = await launchAgent({
|
|
816
837
|
agent: "copilot",
|
|
817
|
-
args
|
|
838
|
+
args,
|
|
818
839
|
env,
|
|
819
840
|
pinnedSpec
|
|
820
841
|
});
|
|
@@ -919,7 +940,7 @@ function candidatePorts(preferredPort) {
|
|
|
919
940
|
}
|
|
920
941
|
function describeBackend(backend) {
|
|
921
942
|
switch (backend) {
|
|
922
|
-
case "
|
|
943
|
+
case "keyring":
|
|
923
944
|
return "OS keychain";
|
|
924
945
|
case "file":
|
|
925
946
|
return "credentials file";
|
|
@@ -1274,6 +1295,12 @@ async function ensureDaemonRunningForLauncher(opts) {
|
|
|
1274
1295
|
await warnIfDebugRequestedButInactive(opts.debug, live.port);
|
|
1275
1296
|
return live;
|
|
1276
1297
|
}
|
|
1298
|
+
// Fail fast on missing credentials rather than spawning a detached daemon
|
|
1299
|
+
// that will die silently and surface as a generic "start timed out" error.
|
|
1300
|
+
const authState = await inspectStoredCredential();
|
|
1301
|
+
if (!authState.stored) {
|
|
1302
|
+
throw new Error("Not authenticated. Run `copillm auth login` first.");
|
|
1303
|
+
}
|
|
1277
1304
|
const debugLog = currentDebugLogPath(opts.debug);
|
|
1278
1305
|
process.stderr.write(opts.debug && debugLog
|
|
1279
1306
|
? `Starting copillm in background with debug logging at ${displayHomePath(debugLog)}...\n`
|
|
@@ -1283,17 +1310,40 @@ async function ensureDaemonRunningForLauncher(opts) {
|
|
|
1283
1310
|
daemonArgs.push("--debug");
|
|
1284
1311
|
const child = spawn(process.execPath, daemonArgs, {
|
|
1285
1312
|
detached: true,
|
|
1286
|
-
stdio: "ignore",
|
|
1313
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
1287
1314
|
env: daemonSpawnEnv(opts.debug)
|
|
1288
1315
|
});
|
|
1289
1316
|
child.unref();
|
|
1317
|
+
const stderrChunks = [];
|
|
1318
|
+
let stderrBytes = 0;
|
|
1319
|
+
const STDERR_TAIL_LIMIT = 8 * 1024;
|
|
1320
|
+
if (child.stderr) {
|
|
1321
|
+
child.stderr.on("data", (chunk) => {
|
|
1322
|
+
stderrChunks.push(chunk);
|
|
1323
|
+
stderrBytes += chunk.length;
|
|
1324
|
+
while (stderrBytes > STDERR_TAIL_LIMIT && stderrChunks.length > 1) {
|
|
1325
|
+
stderrBytes -= stderrChunks[0].length;
|
|
1326
|
+
stderrChunks.shift();
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
child.stderr.on("error", () => {
|
|
1330
|
+
// Ignore — best-effort capture only.
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
const formatStderrTail = () => {
|
|
1334
|
+
const tail = Buffer.concat(stderrChunks).toString("utf8").trim();
|
|
1335
|
+
return tail ? `\nDaemon stderr (tail):\n${tail}` : "";
|
|
1336
|
+
};
|
|
1290
1337
|
const started = await waitForDaemonReady(child.pid ?? null, 10_000);
|
|
1291
1338
|
if (!started) {
|
|
1292
|
-
|
|
1339
|
+
if (child.pid !== undefined && !isPidAlive(child.pid)) {
|
|
1340
|
+
throw new Error(`copillm daemon exited before becoming ready.${formatStderrTail()}`);
|
|
1341
|
+
}
|
|
1342
|
+
throw new Error(`Auto-start of copillm daemon timed out.${formatStderrTail()}`);
|
|
1293
1343
|
}
|
|
1294
1344
|
const inspection = inspectLock();
|
|
1295
1345
|
if (inspection.state !== "running") {
|
|
1296
|
-
throw new Error(
|
|
1346
|
+
throw new Error(`copillm daemon failed to register a lock after auto-start.${formatStderrTail()}`);
|
|
1297
1347
|
}
|
|
1298
1348
|
return inspection.lock;
|
|
1299
1349
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copillm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Local Copilot proxy CLI (OpenAI/Anthropic-compatible)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"prepack": "npm run build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
37
38
|
"commander": "^12.1.0",
|
|
38
|
-
"keytar": "^7.9.0",
|
|
39
39
|
"pino": "^9.4.0",
|
|
40
40
|
"pino-pretty": "^11.2.2",
|
|
41
41
|
"smol-toml": "^1.6.1",
|