@covro/covro 0.1.1-dev.8 → 0.2.0-beta.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.
@@ -6,8 +6,8 @@
6
6
  <meta name="theme-color" content="#f7f8fa" />
7
7
  <link rel="icon" type="image/svg+xml" href="/covro.svg" />
8
8
  <title>Covro · 工作空间</title>
9
- <script type="module" crossorigin src="/assets/index-CAtxws9I.js"></script>
10
- <link rel="stylesheet" crossorigin href="/assets/index-BmbPtei8.css">
9
+ <script type="module" crossorigin src="/assets/index-z5RGzL8u.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/assets/index-Dr0Qkixk.css">
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@covro/covro",
3
- "version": "0.1.1-dev.8",
3
+ "version": "0.2.0-beta.1",
4
4
  "description": "A self-hosted browser workspace for your local Codex",
5
5
  "bin": {
6
6
  "covro": "bin/covro.mjs"
@@ -24,8 +24,6 @@
24
24
  },
25
25
  "scripts": {
26
26
  "dev": "concurrently -k -n api,web 'node --import tsx --watch src/server/main.ts --config config.local.json --dev' 'vite'",
27
- "format": "prettier --write .",
28
- "format:check": "prettier --check .",
29
27
  "typecheck": "tsc --noEmit",
30
28
  "lint": "eslint .",
31
29
  "test": "vitest run",
@@ -87,10 +85,10 @@
87
85
  ],
88
86
  "unsupported": []
89
87
  },
90
- "covroCommit": "9aa427ad5b5835f1b9a5be4936bab66567655442",
91
- "covroChannel": "dev",
88
+ "covroCommit": "143a52af6e24e6a953ebd6cbf049a5c851e4df92",
89
+ "covroChannel": "beta",
92
90
  "covroRelease": {
93
- "runId": "39372876914",
91
+ "runId": "39368655900",
94
92
  "runAttempt": "1"
95
93
  }
96
94
  }
@@ -1,103 +0,0 @@
1
- import { spawn, execFile } from "node:child_process";
2
- import { mkdir, open } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { promisify } from "node:util";
5
- import { setTimeout } from "node:timers/promises";
6
- import lockfile from "proper-lockfile";
7
- import { control, isNotRunning, stopAndWait } from "./control.js";
8
- import {
9
- configureLaunchdLogin,
10
- guiSessionAvailable,
11
- loginServiceInstalled,
12
- startLaunchd,
13
- unloadLaunchd
14
- } from "./launchd.js";
15
- import { readJson } from "../updates/files.js";
16
- const exec = promisify(execFile);
17
- async function lifecycle(location, action) {
18
- await mkdir(location.state, { recursive: true, mode: 448 });
19
- const release = await lockfile.lock(location.state, {
20
- lockfilePath: join(location.state, "lifecycle.lock"),
21
- stale: 1e4,
22
- update: 2e3,
23
- retries: { retries: 280, minTimeout: 250, maxTimeout: 250 }
24
- });
25
- try {
26
- return await action();
27
- } finally {
28
- await release();
29
- }
30
- }
31
- async function start(location, entry) {
32
- try {
33
- return await control(location, "status");
34
- } catch (error) {
35
- if (!isNotRunning(error)) throw error;
36
- }
37
- let child;
38
- const installed = await readJson(
39
- join(location.state, "service.json")
40
- );
41
- if (process.platform === "darwin" && await startLaunchd(location, entry)) {
42
- } else if (installed?.platform === "linux") {
43
- await exec("systemctl", ["--user", "start", `${installed.label}.service`]);
44
- } else {
45
- if (process.platform === "darwin")
46
- console.warn(
47
- "\u5F53\u524D\u6CA1\u6709 macOS \u684C\u9762\u767B\u5F55\u4F1A\u8BDD\uFF0C\u4F7F\u7528 headless \u540E\u53F0\u542F\u52A8\uFF1B\u7CFB\u7EDF\u94A5\u5319\u4E32\u51ED\u8BC1\u53EF\u80FD\u4E0D\u53EF\u7528\u3002"
48
- );
49
- const log = await open(location.log, "a", 384);
50
- try {
51
- child = spawn(process.execPath, [entry, "run", "--config", location.config], {
52
- detached: true,
53
- stdio: ["ignore", log.fd, log.fd],
54
- windowsHide: true,
55
- env: { ...process.env, COVRO_HOME: location.home }
56
- });
57
- await new Promise((resolve, reject) => {
58
- child.once("spawn", resolve);
59
- child.once("error", reject);
60
- });
61
- child.unref();
62
- } finally {
63
- await log.close();
64
- }
65
- }
66
- for (let attempt = 0; attempt < 240; attempt++) {
67
- try {
68
- return await control(location, "status");
69
- } catch (error) {
70
- if (!isNotRunning(error)) throw error;
71
- }
72
- if (child && child.exitCode !== null) throw new Error(`Covro \u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B ${location.log}`);
73
- await setTimeout(250);
74
- }
75
- throw new Error(`Covro \u5C1A\u672A\u786E\u8BA4\u542F\u52A8\uFF0C\u8BF7\u8FD0\u884C covro status \u5E76\u67E5\u770B ${location.log}`);
76
- }
77
- async function stop(location) {
78
- await stopAndWait(location);
79
- if (process.platform === "darwin") await unloadLaunchd(location);
80
- else {
81
- const installed = await readJson(
82
- join(location.state, "service.json")
83
- );
84
- if (installed?.platform === "linux")
85
- await exec("systemctl", ["--user", "stop", `${installed.label}.service`]);
86
- }
87
- }
88
- const startBackground = (location, entry) => lifecycle(location, () => start(location, entry));
89
- const stopBackground = (location) => lifecycle(location, () => stop(location));
90
- const macOSLoginService = (location, entry, install) => lifecycle(location, async () => {
91
- if (install && !await guiSessionAvailable(location))
92
- throw new Error("\u542F\u7528\u767B\u5F55\u81EA\u542F\u9700\u8981\u5F53\u524D\u7528\u6237\u5DF2\u767B\u5F55 macOS \u684C\u9762");
93
- if (!install && !await loginServiceInstalled(location)) return;
94
- await stop(location);
95
- await configureLaunchdLogin(location, entry, install);
96
- if (install) await start(location, entry);
97
- });
98
- export {
99
- macOSLoginService,
100
- startBackground,
101
- stopBackground
102
- };
103
- //# sourceMappingURL=background.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/cli/background.ts"],
4
- "sourcesContent": ["import { spawn, execFile } from 'node:child_process';\nimport { mkdir, open } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { setTimeout } from 'node:timers/promises';\nimport lockfile from 'proper-lockfile';\nimport type { CovroPaths } from './paths.js';\nimport { control, isNotRunning, stopAndWait } from './control.js';\nimport {\n configureLaunchdLogin,\n guiSessionAvailable,\n loginServiceInstalled,\n startLaunchd,\n unloadLaunchd,\n} from './launchd.js';\nimport { readJson } from '../updates/files.js';\n\nconst exec = promisify(execFile);\nasync function lifecycle<T>(location: CovroPaths, action: () => Promise<T>) {\n await mkdir(location.state, { recursive: true, mode: 0o700 });\n const release = await lockfile.lock(location.state, {\n lockfilePath: join(location.state, 'lifecycle.lock'),\n stale: 10_000,\n update: 2_000,\n retries: { retries: 280, minTimeout: 250, maxTimeout: 250 },\n });\n try {\n return await action();\n } finally {\n await release();\n }\n}\n\nasync function start(location: CovroPaths, entry: string) {\n try {\n return await control(location, 'status');\n } catch (error) {\n if (!isNotRunning(error)) throw error;\n }\n let child: ReturnType<typeof spawn> | undefined;\n const installed = await readJson<{ platform: string; label: string }>(\n join(location.state, 'service.json'),\n );\n if (process.platform === 'darwin' && (await startLaunchd(location, entry))) {\n // launchd owns the process; startup is confirmed through the same private control socket.\n } else if (installed?.platform === 'linux') {\n await exec('systemctl', ['--user', 'start', `${installed.label}.service`]);\n } else {\n if (process.platform === 'darwin')\n console.warn(\n '\u5F53\u524D\u6CA1\u6709 macOS \u684C\u9762\u767B\u5F55\u4F1A\u8BDD\uFF0C\u4F7F\u7528 headless \u540E\u53F0\u542F\u52A8\uFF1B\u7CFB\u7EDF\u94A5\u5319\u4E32\u51ED\u8BC1\u53EF\u80FD\u4E0D\u53EF\u7528\u3002',\n );\n const log = await open(location.log, 'a', 0o600);\n try {\n child = spawn(process.execPath, [entry, 'run', '--config', location.config], {\n detached: true,\n stdio: ['ignore', log.fd, log.fd],\n windowsHide: true,\n env: { ...process.env, COVRO_HOME: location.home },\n });\n await new Promise<void>((resolve, reject) => {\n child!.once('spawn', resolve);\n child!.once('error', reject);\n });\n child.unref();\n } finally {\n await log.close();\n }\n }\n for (let attempt = 0; attempt < 240; attempt++) {\n try {\n return await control(location, 'status');\n } catch (error) {\n if (!isNotRunning(error)) throw error;\n }\n if (child && child.exitCode !== null) throw new Error(`Covro \u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B ${location.log}`);\n await setTimeout(250);\n }\n throw new Error(`Covro \u5C1A\u672A\u786E\u8BA4\u542F\u52A8\uFF0C\u8BF7\u8FD0\u884C covro status \u5E76\u67E5\u770B ${location.log}`);\n}\n\nasync function stop(location: CovroPaths) {\n await stopAndWait(location);\n if (process.platform === 'darwin') await unloadLaunchd(location);\n else {\n const installed = await readJson<{ platform: string; label: string }>(\n join(location.state, 'service.json'),\n );\n if (installed?.platform === 'linux')\n await exec('systemctl', ['--user', 'stop', `${installed.label}.service`]);\n }\n}\n\nexport const startBackground = (location: CovroPaths, entry: string) =>\n lifecycle(location, () => start(location, entry));\nexport const stopBackground = (location: CovroPaths) => lifecycle(location, () => stop(location));\nexport const macOSLoginService = (location: CovroPaths, entry: string, install: boolean) =>\n lifecycle(location, async () => {\n if (install && !(await guiSessionAvailable(location)))\n throw new Error('\u542F\u7528\u767B\u5F55\u81EA\u542F\u9700\u8981\u5F53\u524D\u7528\u6237\u5DF2\u767B\u5F55 macOS \u684C\u9762');\n if (!install && !(await loginServiceInstalled(location))) return;\n await stop(location);\n await configureLaunchdLogin(location, entry, install);\n if (install) await start(location, entry);\n });\n"],
5
- "mappings": "AAAA,SAAS,OAAO,gBAAgB;AAChC,SAAS,OAAO,YAAY;AAC5B,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,OAAO,cAAc;AAErB,SAAS,SAAS,cAAc,mBAAmB;AACnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB;AAEzB,MAAM,OAAO,UAAU,QAAQ;AAC/B,eAAe,UAAa,UAAsB,QAA0B;AAC1E,QAAM,MAAM,SAAS,OAAO,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC5D,QAAM,UAAU,MAAM,SAAS,KAAK,SAAS,OAAO;AAAA,IAClD,cAAc,KAAK,SAAS,OAAO,gBAAgB;AAAA,IACnD,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS,EAAE,SAAS,KAAK,YAAY,KAAK,YAAY,IAAI;AAAA,EAC5D,CAAC;AACD,MAAI;AACF,WAAO,MAAM,OAAO;AAAA,EACtB,UAAE;AACA,UAAM,QAAQ;AAAA,EAChB;AACF;AAEA,eAAe,MAAM,UAAsB,OAAe;AACxD,MAAI;AACF,WAAO,MAAM,QAAQ,UAAU,QAAQ;AAAA,EACzC,SAAS,OAAO;AACd,QAAI,CAAC,aAAa,KAAK,EAAG,OAAM;AAAA,EAClC;AACA,MAAI;AACJ,QAAM,YAAY,MAAM;AAAA,IACtB,KAAK,SAAS,OAAO,cAAc;AAAA,EACrC;AACA,MAAI,QAAQ,aAAa,YAAa,MAAM,aAAa,UAAU,KAAK,GAAI;AAAA,EAE5E,WAAW,WAAW,aAAa,SAAS;AAC1C,UAAM,KAAK,aAAa,CAAC,UAAU,SAAS,GAAG,UAAU,KAAK,UAAU,CAAC;AAAA,EAC3E,OAAO;AACL,QAAI,QAAQ,aAAa;AACvB,cAAQ;AAAA,QACN;AAAA,MACF;AACF,UAAM,MAAM,MAAM,KAAK,SAAS,KAAK,KAAK,GAAK;AAC/C,QAAI;AACF,cAAQ,MAAM,QAAQ,UAAU,CAAC,OAAO,OAAO,YAAY,SAAS,MAAM,GAAG;AAAA,QAC3E,UAAU;AAAA,QACV,OAAO,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE;AAAA,QAChC,aAAa;AAAA,QACb,KAAK,EAAE,GAAG,QAAQ,KAAK,YAAY,SAAS,KAAK;AAAA,MACnD,CAAC;AACD,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,cAAO,KAAK,SAAS,OAAO;AAC5B,cAAO,KAAK,SAAS,MAAM;AAAA,MAC7B,CAAC;AACD,YAAM,MAAM;AAAA,IACd,UAAE;AACA,YAAM,IAAI,MAAM;AAAA,IAClB;AAAA,EACF;AACA,WAAS,UAAU,GAAG,UAAU,KAAK,WAAW;AAC9C,QAAI;AACF,aAAO,MAAM,QAAQ,UAAU,QAAQ;AAAA,IACzC,SAAS,OAAO;AACd,UAAI,CAAC,aAAa,KAAK,EAAG,OAAM;AAAA,IAClC;AACA,QAAI,SAAS,MAAM,aAAa,KAAM,OAAM,IAAI,MAAM,0DAAkB,SAAS,GAAG,EAAE;AACtF,UAAM,WAAW,GAAG;AAAA,EACtB;AACA,QAAM,IAAI,MAAM,sGAAqC,SAAS,GAAG,EAAE;AACrE;AAEA,eAAe,KAAK,UAAsB;AACxC,QAAM,YAAY,QAAQ;AAC1B,MAAI,QAAQ,aAAa,SAAU,OAAM,cAAc,QAAQ;AAAA,OAC1D;AACH,UAAM,YAAY,MAAM;AAAA,MACtB,KAAK,SAAS,OAAO,cAAc;AAAA,IACrC;AACA,QAAI,WAAW,aAAa;AAC1B,YAAM,KAAK,aAAa,CAAC,UAAU,QAAQ,GAAG,UAAU,KAAK,UAAU,CAAC;AAAA,EAC5E;AACF;AAEO,MAAM,kBAAkB,CAAC,UAAsB,UACpD,UAAU,UAAU,MAAM,MAAM,UAAU,KAAK,CAAC;AAC3C,MAAM,iBAAiB,CAAC,aAAyB,UAAU,UAAU,MAAM,KAAK,QAAQ,CAAC;AACzF,MAAM,oBAAoB,CAAC,UAAsB,OAAe,YACrE,UAAU,UAAU,YAAY;AAC9B,MAAI,WAAW,CAAE,MAAM,oBAAoB,QAAQ;AACjD,UAAM,IAAI,MAAM,+GAA0B;AAC5C,MAAI,CAAC,WAAW,CAAE,MAAM,sBAAsB,QAAQ,EAAI;AAC1D,QAAM,KAAK,QAAQ;AACnB,QAAM,sBAAsB,UAAU,OAAO,OAAO;AACpD,MAAI,QAAS,OAAM,MAAM,UAAU,KAAK;AAC1C,CAAC;",
6
- "names": []
7
- }
@@ -1,92 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
3
- const exec = promisify(execFile);
4
- async function probe(command, args) {
5
- try {
6
- const result = await exec(command, args, { timeout: 4e3, maxBuffer: 64 * 1024 });
7
- return { exitCode: 0, ...result, failure: void 0 };
8
- } catch (error) {
9
- const result = error;
10
- return {
11
- exitCode: typeof result.code === "number" ? result.code : null,
12
- stdout: result.stdout ?? "",
13
- stderr: result.stderr ?? "",
14
- failure: result.killed ? "timeout" : typeof result.code === "string" ? result.code : void 0
15
- };
16
- }
17
- }
18
- async function launchContext() {
19
- const manager = process.platform === "darwin" ? await probe("/bin/launchctl", ["managername"]) : void 0;
20
- return {
21
- platform: process.platform,
22
- manager: manager?.exitCode === 0 && /^(Aqua|Background|System|LoginWindow|StandardIO)$/.test(manager.stdout.trim()) ? manager.stdout.trim() : null,
23
- uid: process.getuid?.() ?? null
24
- };
25
- }
26
- async function doctor() {
27
- const context = await launchContext();
28
- const environmentToken = Boolean(process.env.GH_TOKEN || process.env.GITHUB_TOKEN);
29
- const credential = await probe("gh", ["auth", "token", "--hostname", "github.com"]);
30
- const found = credential.exitCode === 0 && Boolean(credential.stdout.trim());
31
- const credentialSource = environmentToken ? "environment" : "stored";
32
- const notes = [
33
- "\u68C0\u67E5\u6765\u81EA\u8FD0\u884C\u4E2D\u7684 Covro \u5B9E\u4F8B\uFF0C\u4E0D\u662F\u8C03\u7528 doctor \u7684\u7EC8\u7AEF\u3002",
34
- "\u672A\u63A2\u6D4B Agent \u6216 Shell \u7684 Codex \u6C99\u7BB1\uFF1B\u5B9E\u4F8B\u8BA4\u8BC1\u6210\u529F\u4E0D\u4EE3\u8868\u53D7\u9650\u547D\u4EE4\u5177\u6709\u76F8\u540C\u6743\u9650\u3002"
35
- ];
36
- if (context.platform === "darwin" && context.manager && context.manager !== "Aqua")
37
- notes.push(
38
- "\u5B9E\u4F8B\u4E0D\u5728 Aqua \u684C\u9762\u4F1A\u8BDD\uFF0C\u7CFB\u7EDF\u94A5\u5319\u4E32\u53EF\u80FD\u4E0D\u53EF\u8BBF\u95EE\uFF1B\u5728\u684C\u9762\u767B\u5F55\u540E\u7528 covro stop \u548C covro start \u91CD\u542F\uFF0C\u6216\u5B89\u88C5 GUI \u767B\u5F55\u670D\u52A1\u3002"
39
- );
40
- if (environmentToken)
41
- notes.push("GH_TOKEN \u6216 GITHUB_TOKEN \u5DF2\u8BBE\u7F6E\uFF0CGitHub CLI \u4F1A\u4F18\u5148\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684\u51ED\u8BC1\u3002");
42
- const base = {
43
- pid: process.pid,
44
- launchContext: context,
45
- scope: "daemon-child-process",
46
- sandbox: "not-tested",
47
- credentialSource,
48
- credentialReadable: found
49
- };
50
- if (credential.failure === "ENOENT")
51
- return {
52
- ...base,
53
- result: "gh-unavailable",
54
- notes: [...notes, "\u5B9E\u4F8B PATH \u4E2D\u6CA1\u6709 GitHub CLI\u3002"]
55
- };
56
- if (!found)
57
- return {
58
- ...base,
59
- result: "credential-unavailable",
60
- lookupExitCode: credential.exitCode,
61
- notes: [
62
- ...notes,
63
- "\u672A\u80FD\u8BFB\u53D6\u51ED\u8BC1\uFF0C\u5C1A\u672A\u9A8C\u8BC1\u4EE4\u724C\u662F\u5426\u6709\u6548\uFF1B\u8BF7\u5206\u522B\u68C0\u67E5\u542F\u52A8\u4F1A\u8BDD\u3001\u94A5\u5319\u4E32/\u6C99\u7BB1\u8BBF\u95EE\u548C\u662F\u5426\u5DF2\u914D\u7F6E\u51ED\u8BC1\uFF0C\u4E0D\u80FD\u636E\u6B64\u8BA4\u5B9A\u9700\u8981\u91CD\u65B0\u767B\u5F55\u3002"
64
- ]
65
- };
66
- const api = await probe("gh", [
67
- "api",
68
- "user",
69
- "--hostname",
70
- "github.com",
71
- "--include",
72
- "--silent"
73
- ]);
74
- const status = /HTTP\/[\d.]+\s+(\d{3})/.exec(`${api.stdout}
75
- ${api.stderr}`)?.[1];
76
- const httpStatus = status ? Number(status) : null;
77
- const result = api.exitCode === 0 ? "authenticated" : httpStatus === 401 ? "authentication-rejected" : httpStatus === 403 ? "request-forbidden" : "request-failed";
78
- if (result === "authentication-rejected")
79
- notes.push(
80
- "\u5DF2\u8BFB\u53D6\u51ED\u8BC1\uFF0C\u4F46 GitHub \u8FD4\u56DE 401\uFF1B\u8BF7\u68C0\u67E5\u64A4\u9500\u3001\u5931\u6548\u6216\u73AF\u5883\u53D8\u91CF\u8986\u76D6\uFF0C\u800C\u4E0D\u662F\u5C06\u5176\u5F52\u56E0\u4E8E\u94A5\u5319\u4E32\u8BFB\u53D6\u5931\u8D25\u3002"
81
- );
82
- else if (result === "request-forbidden")
83
- notes.push("GitHub \u8FD4\u56DE 403\uFF1B\u8BF7\u68C0\u67E5\u6743\u9650\u3001\u7EC4\u7EC7\u7B56\u7565\u6216\u8BF7\u6C42\u9650\u989D\uFF0C\u4E0D\u80FD\u636E\u6B64\u8BA4\u5B9A\u4EE4\u724C\u5931\u6548\u3002");
84
- else if (result === "request-failed")
85
- notes.push("\u5DF2\u8BFB\u53D6\u51ED\u8BC1\uFF0C\u4F46\u8BA4\u8BC1\u8BF7\u6C42\u672A\u6210\u529F\uFF1B\u8BF7\u68C0\u67E5\u7F51\u7EDC\u3001\u4EE3\u7406\u6216\u670D\u52A1\u72B6\u6001\uFF0C\u672A\u786E\u8BA4\u4EE4\u724C\u5931\u6548\u3002");
86
- return { ...base, result, httpStatus, requestExitCode: api.exitCode, notes };
87
- }
88
- export {
89
- doctor,
90
- launchContext
91
- };
92
- //# sourceMappingURL=doctor.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/cli/doctor.ts"],
4
- "sourcesContent": ["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst exec = promisify(execFile);\nasync function probe(command: string, args: string[]) {\n try {\n const result = await exec(command, args, { timeout: 4000, maxBuffer: 64 * 1024 });\n return { exitCode: 0, ...result, failure: undefined };\n } catch (error) {\n const result = error as {\n code?: string | number;\n stdout?: string;\n stderr?: string;\n killed?: boolean;\n };\n return {\n exitCode: typeof result.code === 'number' ? result.code : null,\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n failure: result.killed\n ? 'timeout'\n : typeof result.code === 'string'\n ? result.code\n : undefined,\n };\n }\n}\n\nexport async function launchContext() {\n const manager =\n process.platform === 'darwin' ? await probe('/bin/launchctl', ['managername']) : undefined;\n return {\n platform: process.platform,\n manager:\n manager?.exitCode === 0 &&\n /^(Aqua|Background|System|LoginWindow|StandardIO)$/.test(manager.stdout.trim())\n ? manager.stdout.trim()\n : null,\n uid: process.getuid?.() ?? null,\n };\n}\n\n/** Runs inside the daemon. Return classifications only: command errors can contain credentials. */\nexport async function doctor() {\n const context = await launchContext();\n const environmentToken = Boolean(process.env.GH_TOKEN || process.env.GITHUB_TOKEN);\n const credential = await probe('gh', ['auth', 'token', '--hostname', 'github.com']);\n const found = credential.exitCode === 0 && Boolean(credential.stdout.trim());\n const credentialSource = environmentToken ? 'environment' : 'stored';\n const notes = [\n '\u68C0\u67E5\u6765\u81EA\u8FD0\u884C\u4E2D\u7684 Covro \u5B9E\u4F8B\uFF0C\u4E0D\u662F\u8C03\u7528 doctor \u7684\u7EC8\u7AEF\u3002',\n '\u672A\u63A2\u6D4B Agent \u6216 Shell \u7684 Codex \u6C99\u7BB1\uFF1B\u5B9E\u4F8B\u8BA4\u8BC1\u6210\u529F\u4E0D\u4EE3\u8868\u53D7\u9650\u547D\u4EE4\u5177\u6709\u76F8\u540C\u6743\u9650\u3002',\n ];\n if (context.platform === 'darwin' && context.manager && context.manager !== 'Aqua')\n notes.push(\n '\u5B9E\u4F8B\u4E0D\u5728 Aqua \u684C\u9762\u4F1A\u8BDD\uFF0C\u7CFB\u7EDF\u94A5\u5319\u4E32\u53EF\u80FD\u4E0D\u53EF\u8BBF\u95EE\uFF1B\u5728\u684C\u9762\u767B\u5F55\u540E\u7528 covro stop \u548C covro start \u91CD\u542F\uFF0C\u6216\u5B89\u88C5 GUI \u767B\u5F55\u670D\u52A1\u3002',\n );\n if (environmentToken)\n notes.push('GH_TOKEN \u6216 GITHUB_TOKEN \u5DF2\u8BBE\u7F6E\uFF0CGitHub CLI \u4F1A\u4F18\u5148\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684\u51ED\u8BC1\u3002');\n const base = {\n pid: process.pid,\n launchContext: context,\n scope: 'daemon-child-process',\n sandbox: 'not-tested',\n credentialSource,\n credentialReadable: found,\n };\n if (credential.failure === 'ENOENT')\n return {\n ...base,\n result: 'gh-unavailable',\n notes: [...notes, '\u5B9E\u4F8B PATH \u4E2D\u6CA1\u6709 GitHub CLI\u3002'],\n };\n if (!found)\n return {\n ...base,\n result: 'credential-unavailable',\n lookupExitCode: credential.exitCode,\n notes: [\n ...notes,\n '\u672A\u80FD\u8BFB\u53D6\u51ED\u8BC1\uFF0C\u5C1A\u672A\u9A8C\u8BC1\u4EE4\u724C\u662F\u5426\u6709\u6548\uFF1B\u8BF7\u5206\u522B\u68C0\u67E5\u542F\u52A8\u4F1A\u8BDD\u3001\u94A5\u5319\u4E32/\u6C99\u7BB1\u8BBF\u95EE\u548C\u662F\u5426\u5DF2\u914D\u7F6E\u51ED\u8BC1\uFF0C\u4E0D\u80FD\u636E\u6B64\u8BA4\u5B9A\u9700\u8981\u91CD\u65B0\u767B\u5F55\u3002',\n ],\n };\n const api = await probe('gh', [\n 'api',\n 'user',\n '--hostname',\n 'github.com',\n '--include',\n '--silent',\n ]);\n const status = /HTTP\\/[\\d.]+\\s+(\\d{3})/.exec(`${api.stdout}\\n${api.stderr}`)?.[1];\n const httpStatus = status ? Number(status) : null;\n const result =\n api.exitCode === 0\n ? 'authenticated'\n : httpStatus === 401\n ? 'authentication-rejected'\n : httpStatus === 403\n ? 'request-forbidden'\n : 'request-failed';\n if (result === 'authentication-rejected')\n notes.push(\n '\u5DF2\u8BFB\u53D6\u51ED\u8BC1\uFF0C\u4F46 GitHub \u8FD4\u56DE 401\uFF1B\u8BF7\u68C0\u67E5\u64A4\u9500\u3001\u5931\u6548\u6216\u73AF\u5883\u53D8\u91CF\u8986\u76D6\uFF0C\u800C\u4E0D\u662F\u5C06\u5176\u5F52\u56E0\u4E8E\u94A5\u5319\u4E32\u8BFB\u53D6\u5931\u8D25\u3002',\n );\n else if (result === 'request-forbidden')\n notes.push('GitHub \u8FD4\u56DE 403\uFF1B\u8BF7\u68C0\u67E5\u6743\u9650\u3001\u7EC4\u7EC7\u7B56\u7565\u6216\u8BF7\u6C42\u9650\u989D\uFF0C\u4E0D\u80FD\u636E\u6B64\u8BA4\u5B9A\u4EE4\u724C\u5931\u6548\u3002');\n else if (result === 'request-failed')\n notes.push('\u5DF2\u8BFB\u53D6\u51ED\u8BC1\uFF0C\u4F46\u8BA4\u8BC1\u8BF7\u6C42\u672A\u6210\u529F\uFF1B\u8BF7\u68C0\u67E5\u7F51\u7EDC\u3001\u4EE3\u7406\u6216\u670D\u52A1\u72B6\u6001\uFF0C\u672A\u786E\u8BA4\u4EE4\u724C\u5931\u6548\u3002');\n return { ...base, result, httpStatus, requestExitCode: api.exitCode, notes };\n}\n"],
5
- "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAE1B,MAAM,OAAO,UAAU,QAAQ;AAC/B,eAAe,MAAM,SAAiB,MAAgB;AACpD,MAAI;AACF,UAAM,SAAS,MAAM,KAAK,SAAS,MAAM,EAAE,SAAS,KAAM,WAAW,KAAK,KAAK,CAAC;AAChF,WAAO,EAAE,UAAU,GAAG,GAAG,QAAQ,SAAS,OAAU;AAAA,EACtD,SAAS,OAAO;AACd,UAAM,SAAS;AAMf,WAAO;AAAA,MACL,UAAU,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,MAC1D,QAAQ,OAAO,UAAU;AAAA,MACzB,QAAQ,OAAO,UAAU;AAAA,MACzB,SAAS,OAAO,SACZ,YACA,OAAO,OAAO,SAAS,WACrB,OAAO,OACP;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,gBAAgB;AACpC,QAAM,UACJ,QAAQ,aAAa,WAAW,MAAM,MAAM,kBAAkB,CAAC,aAAa,CAAC,IAAI;AACnF,SAAO;AAAA,IACL,UAAU,QAAQ;AAAA,IAClB,SACE,SAAS,aAAa,KACtB,oDAAoD,KAAK,QAAQ,OAAO,KAAK,CAAC,IAC1E,QAAQ,OAAO,KAAK,IACpB;AAAA,IACN,KAAK,QAAQ,SAAS,KAAK;AAAA,EAC7B;AACF;AAGA,eAAsB,SAAS;AAC7B,QAAM,UAAU,MAAM,cAAc;AACpC,QAAM,mBAAmB,QAAQ,QAAQ,IAAI,YAAY,QAAQ,IAAI,YAAY;AACjF,QAAM,aAAa,MAAM,MAAM,MAAM,CAAC,QAAQ,SAAS,cAAc,YAAY,CAAC;AAClF,QAAM,QAAQ,WAAW,aAAa,KAAK,QAAQ,WAAW,OAAO,KAAK,CAAC;AAC3E,QAAM,mBAAmB,mBAAmB,gBAAgB;AAC5D,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,YAAY,QAAQ,WAAW,QAAQ,YAAY;AAC1E,UAAM;AAAA,MACJ;AAAA,IACF;AACF,MAAI;AACF,UAAM,KAAK,sJAAuD;AACpE,QAAM,OAAO;AAAA,IACX,KAAK,QAAQ;AAAA,IACb,eAAe;AAAA,IACf,OAAO;AAAA,IACP,SAAS;AAAA,IACT;AAAA,IACA,oBAAoB;AAAA,EACtB;AACA,MAAI,WAAW,YAAY;AACzB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,OAAO,CAAC,GAAG,OAAO,uDAAyB;AAAA,IAC7C;AACF,MAAI,CAAC;AACH,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,gBAAgB,WAAW;AAAA,MAC3B,OAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACF,QAAM,MAAM,MAAM,MAAM,MAAM;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,SAAS,yBAAyB,KAAK,GAAG,IAAI,MAAM;AAAA,EAAK,IAAI,MAAM,EAAE,IAAI,CAAC;AAChF,QAAM,aAAa,SAAS,OAAO,MAAM,IAAI;AAC7C,QAAM,SACJ,IAAI,aAAa,IACb,kBACA,eAAe,MACb,4BACA,eAAe,MACb,sBACA;AACV,MAAI,WAAW;AACb,UAAM;AAAA,MACJ;AAAA,IACF;AAAA,WACO,WAAW;AAClB,UAAM,KAAK,iMAA2C;AAAA,WAC/C,WAAW;AAClB,UAAM,KAAK,gOAAuC;AACpD,SAAO,EAAE,GAAG,MAAM,QAAQ,YAAY,iBAAiB,IAAI,UAAU,MAAM;AAC7E;",
6
- "names": []
7
- }
@@ -1,85 +0,0 @@
1
- import { createServer } from "node:net";
2
- import { chmod, rm } from "node:fs/promises";
3
- import { randomBytes } from "node:crypto";
4
- import { join } from "node:path";
5
- async function environmentBridge(home) {
6
- const path = join(home, `e-${randomBytes(6).toString("hex")}.sock`);
7
- const clients = /* @__PURE__ */ new Set();
8
- let accept;
9
- const consumed = new Promise((resolve) => {
10
- accept = resolve;
11
- });
12
- const payload = JSON.stringify({ env: { ...process.env, COVRO_HOME: home }, cwd: process.cwd() });
13
- const server = createServer((socket) => {
14
- clients.add(socket);
15
- socket.setTimeout(5e3, () => socket.destroy());
16
- socket.on("error", () => {
17
- });
18
- socket.on("close", () => clients.delete(socket));
19
- socket.end(payload, () => accept?.());
20
- });
21
- try {
22
- await new Promise((resolve, reject) => {
23
- server.once("error", reject);
24
- server.listen(path, resolve);
25
- });
26
- await chmod(path, 384);
27
- return {
28
- path,
29
- async wait() {
30
- let timer;
31
- try {
32
- await Promise.race([
33
- consumed,
34
- new Promise((_, reject) => {
35
- timer = setTimeout(
36
- () => reject(new Error("launchd \u542F\u52A8\u73AF\u5883\u4F20\u9012\u8D85\u65F6\uFF0C\u8BF7\u67E5\u770B Covro \u65E5\u5FD7")),
37
- 1e4
38
- );
39
- })
40
- ]);
41
- } finally {
42
- clearTimeout(timer);
43
- }
44
- },
45
- async close() {
46
- for (const socket of clients) socket.destroy();
47
- await new Promise(
48
- (resolve, reject) => server.close((error) => error ? reject(error) : resolve())
49
- );
50
- await rm(path, { force: true });
51
- }
52
- };
53
- } catch (error) {
54
- server.close();
55
- await rm(path, { force: true });
56
- throw error;
57
- }
58
- }
59
- const launchRunner = `import { connect } from 'node:net';
60
- const [socketPath, entry, config] = process.argv.slice(2);
61
- try {
62
- const input = await new Promise((resolve, reject) => {
63
- const socket = connect(socketPath);
64
- let body = '';
65
- socket.setTimeout(10000, () => socket.destroy(new Error('launch environment timed out')));
66
- socket.setEncoding('utf8');
67
- socket.on('data', chunk => {
68
- body += chunk;
69
- if (Buffer.byteLength(body) > 2 * 1024 * 1024) socket.destroy(new Error('launch environment too large'));
70
- });
71
- socket.on('error', reject);
72
- socket.on('end', () => { try { resolve(JSON.parse(body)); } catch { reject(new Error('invalid launch environment')); } });
73
- });
74
- process.chdir(input.cwd);
75
- process.execve(process.execPath, [process.execPath, entry, 'run', '--config', config], input.env);
76
- } catch {
77
- console.error('Covro launch environment unavailable; restart with covro start.');
78
- process.exitCode = 1;
79
- }
80
- `;
81
- export {
82
- environmentBridge,
83
- launchRunner
84
- };
85
- //# sourceMappingURL=launch-environment.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/cli/launch-environment.ts"],
4
- "sourcesContent": ["import { createServer, type Socket } from 'node:net';\nimport { chmod, rm } from 'node:fs/promises';\nimport { randomBytes } from 'node:crypto';\nimport { join } from 'node:path';\n\n// The launchd job receives only this socket path. Credentials stay in memory, never in a plist.\nexport async function environmentBridge(home: string) {\n const path = join(home, `e-${randomBytes(6).toString('hex')}.sock`);\n const clients = new Set<Socket>();\n let accept: (() => void) | undefined;\n const consumed = new Promise<void>((resolve) => {\n accept = resolve;\n });\n const payload = JSON.stringify({ env: { ...process.env, COVRO_HOME: home }, cwd: process.cwd() });\n const server = createServer((socket) => {\n clients.add(socket);\n socket.setTimeout(5000, () => socket.destroy());\n socket.on('error', () => {});\n socket.on('close', () => clients.delete(socket));\n socket.end(payload, () => accept?.());\n });\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(path, resolve);\n });\n await chmod(path, 0o600);\n return {\n path,\n async wait() {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n consumed,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error('launchd \u542F\u52A8\u73AF\u5883\u4F20\u9012\u8D85\u65F6\uFF0C\u8BF7\u67E5\u770B Covro \u65E5\u5FD7')),\n 10_000,\n );\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n },\n async close() {\n for (const socket of clients) socket.destroy();\n await new Promise<void>((resolve, reject) =>\n server.close((error) => (error ? reject(error) : resolve())),\n );\n await rm(path, { force: true });\n },\n };\n } catch (error) {\n server.close();\n await rm(path, { force: true });\n throw error;\n }\n}\n\n// Standalone Node runner also works when the update worker restores a version predating this fix.\n// execve preserves launchd's process identity and signal handling without an extra supervisor.\nexport const launchRunner = `import { connect } from 'node:net';\nconst [socketPath, entry, config] = process.argv.slice(2);\ntry {\n const input = await new Promise((resolve, reject) => {\n const socket = connect(socketPath);\n let body = '';\n socket.setTimeout(10000, () => socket.destroy(new Error('launch environment timed out')));\n socket.setEncoding('utf8');\n socket.on('data', chunk => {\n body += chunk;\n if (Buffer.byteLength(body) > 2 * 1024 * 1024) socket.destroy(new Error('launch environment too large'));\n });\n socket.on('error', reject);\n socket.on('end', () => { try { resolve(JSON.parse(body)); } catch { reject(new Error('invalid launch environment')); } });\n });\n process.chdir(input.cwd);\n process.execve(process.execPath, [process.execPath, entry, 'run', '--config', config], input.env);\n} catch {\n console.error('Covro launch environment unavailable; restart with covro start.');\n process.exitCode = 1;\n}\n`;\n"],
5
- "mappings": "AAAA,SAAS,oBAAiC;AAC1C,SAAS,OAAO,UAAU;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AAGrB,eAAsB,kBAAkB,MAAc;AACpD,QAAM,OAAO,KAAK,MAAM,KAAK,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC,OAAO;AAClE,QAAM,UAAU,oBAAI,IAAY;AAChC,MAAI;AACJ,QAAM,WAAW,IAAI,QAAc,CAAC,YAAY;AAC9C,aAAS;AAAA,EACX,CAAC;AACD,QAAM,UAAU,KAAK,UAAU,EAAE,KAAK,EAAE,GAAG,QAAQ,KAAK,YAAY,KAAK,GAAG,KAAK,QAAQ,IAAI,EAAE,CAAC;AAChG,QAAM,SAAS,aAAa,CAAC,WAAW;AACtC,YAAQ,IAAI,MAAM;AAClB,WAAO,WAAW,KAAM,MAAM,OAAO,QAAQ,CAAC;AAC9C,WAAO,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAC3B,WAAO,GAAG,SAAS,MAAM,QAAQ,OAAO,MAAM,CAAC;AAC/C,WAAO,IAAI,SAAS,MAAM,SAAS,CAAC;AAAA,EACtC,CAAC;AACD,MAAI;AACF,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,aAAO,KAAK,SAAS,MAAM;AAC3B,aAAO,OAAO,MAAM,OAAO;AAAA,IAC7B,CAAC;AACD,UAAM,MAAM,MAAM,GAAK;AACvB,WAAO;AAAA,MACL;AAAA,MACA,MAAM,OAAO;AACX,YAAI;AACJ,YAAI;AACF,gBAAM,QAAQ,KAAK;AAAA,YACjB;AAAA,YACA,IAAI,QAAe,CAAC,GAAG,WAAW;AAChC,sBAAQ;AAAA,gBACN,MAAM,OAAO,IAAI,MAAM,qGAA+B,CAAC;AAAA,gBACvD;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH,CAAC;AAAA,QACH,UAAE;AACA,uBAAa,KAAK;AAAA,QACpB;AAAA,MACF;AAAA,MACA,MAAM,QAAQ;AACZ,mBAAW,UAAU,QAAS,QAAO,QAAQ;AAC7C,cAAM,IAAI;AAAA,UAAc,CAAC,SAAS,WAChC,OAAO,MAAM,CAAC,UAAW,QAAQ,OAAO,KAAK,IAAI,QAAQ,CAAE;AAAA,QAC7D;AACA,cAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,WAAO,MAAM;AACb,UAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B,UAAM;AAAA,EACR;AACF;AAIO,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
- "names": []
7
- }
@@ -1,172 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import { createHash } from "node:crypto";
4
- import { homedir } from "node:os";
5
- import { dirname, join } from "node:path";
6
- import { mkdir, rm, writeFile } from "node:fs/promises";
7
- import { environmentBridge, launchRunner } from "./launch-environment.js";
8
- import { readJson, saveJson } from "../updates/files.js";
9
- const exec = promisify(execFile);
10
- const launchctl = (args) => exec("/bin/launchctl", args, { timeout: 5e3, maxBuffer: 2 * 1024 * 1024 });
11
- const xml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
12
- function failureDetail(error) {
13
- const failure = error;
14
- if (failure.killed) return "launchctl \u8BF7\u6C42\u8D85\u65F6";
15
- return (failure.stderr?.trim() || (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF")).slice(
16
- 0,
17
- 1024
18
- );
19
- }
20
- function launchdPaths(location, label = `dev.covro.${location.key}.${createHash("sha256").update(location.home).digest("hex").slice(0, 12)}`) {
21
- const domain = `gui/${process.getuid()}`;
22
- return {
23
- label,
24
- domain,
25
- target: `${domain}/${label}`,
26
- temporary: join(location.state, "launchd.plist"),
27
- login: join(homedir(), "Library/LaunchAgents", `${label}.plist`)
28
- };
29
- }
30
- async function loaded(target) {
31
- try {
32
- await launchctl(["print", target]);
33
- return true;
34
- } catch (error) {
35
- const stderr = error.stderr ?? "";
36
- if (/Could not find (?:domain|service|specified service)/i.test(stderr)) return false;
37
- throw new Error(
38
- `\u65E0\u6CD5\u67E5\u8BE2 launchd ${target}\uFF1A${failureDetail(error)}\uFF1B\u8BF7\u68C0\u67E5\u5F53\u524D\u7528\u6237\u7684 launchd \u6743\u9650`,
39
- { cause: error }
40
- );
41
- }
42
- }
43
- async function guiSessionAvailable(location) {
44
- return loaded(launchdPaths(location).domain);
45
- }
46
- async function loginRegistration(location) {
47
- const installed = await readJson(
48
- join(location.state, "service.json")
49
- );
50
- if (installed?.platform !== "darwin") return void 0;
51
- if (installed.label !== launchdPaths(location).label && installed.label !== `dev.covro.${location.key}`)
52
- throw new Error("\u767B\u5F55\u670D\u52A1\u8EAB\u4EFD\u4E0E\u5F53\u524D\u914D\u7F6E\u4E0D\u5339\u914D");
53
- return installed;
54
- }
55
- async function loginServiceInstalled(location) {
56
- return Boolean(await loginRegistration(location));
57
- }
58
- function launchdPlist(location, args, login) {
59
- const { label } = launchdPaths(location, login ? `dev.covro.${location.key}` : void 0);
60
- const environment = {
61
- PATH: process.env.PATH ?? "/usr/bin:/bin",
62
- COVRO_HOME: location.home,
63
- ...process.env.CODEX_HOME ? { CODEX_HOME: process.env.CODEX_HOME } : {}
64
- };
65
- return `<?xml version="1.0" encoding="UTF-8"?>
66
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
67
- <plist version="1.0"><dict>
68
- <key>Label</key><string>${label}</string>
69
- <key>ProgramArguments</key><array>${args.map((arg) => `<string>${xml(arg)}</string>`).join("")}</array>
70
- <key>LimitLoadToSessionType</key><string>Aqua</string>
71
- <key>RunAtLoad</key><true/>
72
- ${login ? "<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict><key>ThrottleInterval</key><integer>30</integer>" : ""}
73
- <key>EnvironmentVariables</key><dict>${Object.entries(environment).map(([key, value]) => `<key>${key}</key><string>${xml(value)}</string>`).join("")}</dict>
74
- <key>StandardOutPath</key><string>${xml(location.log)}</string>
75
- <key>StandardErrorPath</key><string>${xml(location.log)}</string>
76
- </dict></plist>
77
- `;
78
- }
79
- async function unloadLaunchd(location) {
80
- const { target, temporary } = launchdPaths(location);
81
- const installed = await loginRegistration(location);
82
- const targets = /* @__PURE__ */ new Set([
83
- target,
84
- ...installed ? [launchdPaths(location, installed.label).target] : []
85
- ]);
86
- for (const serviceTarget of targets) {
87
- if (await loaded(serviceTarget)) {
88
- try {
89
- await launchctl(["bootout", serviceTarget]);
90
- } catch (error) {
91
- if (await loaded(serviceTarget)) throw error;
92
- }
93
- }
94
- }
95
- await rm(temporary, { force: true });
96
- }
97
- async function configureLaunchdLogin(location, entry, install) {
98
- const { label, login } = launchdPaths(location, `dev.covro.${location.key}`);
99
- const previous = await loginRegistration(location);
100
- if (install) {
101
- if (!await guiSessionAvailable(location))
102
- throw new Error("\u542F\u7528\u767B\u5F55\u81EA\u542F\u9700\u8981\u5F53\u524D\u7528\u6237\u5DF2\u767B\u5F55 macOS \u684C\u9762");
103
- await mkdir(dirname(login), { recursive: true });
104
- await writeFile(
105
- login,
106
- launchdPlist(location, [process.execPath, entry, "run", "--config", location.config], true),
107
- { mode: 384 }
108
- );
109
- await saveJson(join(location.state, "service.json"), { platform: "darwin", label });
110
- if (previous && previous.label !== label)
111
- await rm(launchdPaths(location, previous.label).login, { force: true });
112
- } else {
113
- if (previous) await rm(launchdPaths(location, previous.label).login, { force: true });
114
- await rm(login, { force: true });
115
- await rm(join(location.state, "service.json"), { force: true });
116
- }
117
- }
118
- async function startLaunchd(location, entry) {
119
- const installed = await loginRegistration(location);
120
- const { domain, target, temporary, login } = launchdPaths(location, installed?.label);
121
- if (!await guiSessionAvailable(location)) {
122
- if (installed) throw new Error("\u5F53\u524D\u6CA1\u6709 macOS \u684C\u9762\u767B\u5F55\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u542F\u52A8 GUI \u767B\u5F55\u670D\u52A1");
123
- return false;
124
- }
125
- await unloadLaunchd(location);
126
- if (installed) {
127
- await launchctl(["enable", target]);
128
- await launchctl(["bootstrap", domain, login]);
129
- return true;
130
- }
131
- const bridge = await environmentBridge(location.home);
132
- try {
133
- const runner = join(location.state, "launch-runner.mjs");
134
- await writeFile(runner, launchRunner, { mode: 384 });
135
- await writeFile(
136
- temporary,
137
- launchdPlist(
138
- location,
139
- [process.execPath, runner, bridge.path, entry, location.config],
140
- false
141
- ),
142
- { mode: 384 }
143
- );
144
- await launchctl(["enable", target]);
145
- await launchctl(["bootstrap", domain, temporary]);
146
- await bridge.wait();
147
- return true;
148
- } catch (error) {
149
- let cleanup = "";
150
- try {
151
- await unloadLaunchd(location);
152
- } catch (cleanupError) {
153
- cleanup = `\uFF1B\u6E05\u7406\u4EFB\u52A1\u5931\u8D25\uFF1A${failureDetail(cleanupError)}`;
154
- }
155
- throw new Error(
156
- `Covro GUI \u540E\u53F0\u542F\u52A8\u5931\u8D25\uFF1A${failureDetail(error)}${cleanup}\uFF1B\u8FDB\u7A0B\u65E5\u5FD7\u4F4D\u4E8E ${location.log}`,
157
- { cause: error }
158
- );
159
- } finally {
160
- await bridge.close();
161
- }
162
- }
163
- export {
164
- configureLaunchdLogin,
165
- guiSessionAvailable,
166
- launchdPaths,
167
- launchdPlist,
168
- loginServiceInstalled,
169
- startLaunchd,
170
- unloadLaunchd
171
- };
172
- //# sourceMappingURL=launchd.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/cli/launchd.ts"],
4
- "sourcesContent": ["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { createHash } from 'node:crypto';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { mkdir, rm, writeFile } from 'node:fs/promises';\nimport type { CovroPaths } from './paths.js';\nimport { environmentBridge, launchRunner } from './launch-environment.js';\nimport { readJson, saveJson } from '../updates/files.js';\n\nconst exec = promisify(execFile);\nconst launchctl = (args: string[]) =>\n exec('/bin/launchctl', args, { timeout: 5000, maxBuffer: 2 * 1024 * 1024 });\nconst xml = (value: string) =>\n value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;');\n\nfunction failureDetail(error: unknown) {\n const failure = error as { killed?: boolean; stderr?: string };\n if (failure.killed) return 'launchctl \u8BF7\u6C42\u8D85\u65F6';\n return (failure.stderr?.trim() || (error instanceof Error ? error.message : '\u672A\u77E5\u9519\u8BEF')).slice(\n 0,\n 1024,\n );\n}\n\nexport function launchdPaths(\n location: CovroPaths,\n label = `dev.covro.${location.key}.${createHash('sha256').update(location.home).digest('hex').slice(0, 12)}`,\n) {\n const domain = `gui/${process.getuid!()}`;\n return {\n label,\n domain,\n target: `${domain}/${label}`,\n temporary: join(location.state, 'launchd.plist'),\n login: join(homedir(), 'Library/LaunchAgents', `${label}.plist`),\n };\n}\n\nasync function loaded(target: string) {\n try {\n await launchctl(['print', target]);\n return true;\n } catch (error) {\n const stderr = (error as { stderr?: string }).stderr ?? '';\n if (/Could not find (?:domain|service|specified service)/i.test(stderr)) return false;\n throw new Error(\n `\u65E0\u6CD5\u67E5\u8BE2 launchd ${target}\uFF1A${failureDetail(error)}\uFF1B\u8BF7\u68C0\u67E5\u5F53\u524D\u7528\u6237\u7684 launchd \u6743\u9650`,\n { cause: error },\n );\n }\n}\n\nexport async function guiSessionAvailable(location: CovroPaths) {\n return loaded(launchdPaths(location).domain);\n}\n\nasync function loginRegistration(location: CovroPaths) {\n const installed = await readJson<{ platform: string; label: string }>(\n join(location.state, 'service.json'),\n );\n if (installed?.platform !== 'darwin') return undefined;\n // Older releases used a config-only label; keep those installed login services operable.\n if (\n installed.label !== launchdPaths(location).label &&\n installed.label !== `dev.covro.${location.key}`\n )\n throw new Error('\u767B\u5F55\u670D\u52A1\u8EAB\u4EFD\u4E0E\u5F53\u524D\u914D\u7F6E\u4E0D\u5339\u914D');\n return installed;\n}\n\nexport async function loginServiceInstalled(location: CovroPaths) {\n return Boolean(await loginRegistration(location));\n}\n\nexport function launchdPlist(location: CovroPaths, args: string[], login: boolean) {\n const { label } = launchdPaths(location, login ? `dev.covro.${location.key}` : undefined);\n const environment = {\n PATH: process.env.PATH ?? '/usr/bin:/bin',\n COVRO_HOME: location.home,\n ...(process.env.CODEX_HOME ? { CODEX_HOME: process.env.CODEX_HOME } : {}),\n };\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\"><dict>\n<key>Label</key><string>${label}</string>\n<key>ProgramArguments</key><array>${args.map((arg) => `<string>${xml(arg)}</string>`).join('')}</array>\n<key>LimitLoadToSessionType</key><string>Aqua</string>\n<key>RunAtLoad</key><true/>\n${login ? '<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict><key>ThrottleInterval</key><integer>30</integer>' : ''}\n<key>EnvironmentVariables</key><dict>${Object.entries(environment)\n .map(([key, value]) => `<key>${key}</key><string>${xml(value)}</string>`)\n .join('')}</dict>\n<key>StandardOutPath</key><string>${xml(location.log)}</string>\n<key>StandardErrorPath</key><string>${xml(location.log)}</string>\n</dict></plist>\n`;\n}\n\nexport async function unloadLaunchd(location: CovroPaths) {\n const { target, temporary } = launchdPaths(location);\n const installed = await loginRegistration(location);\n const targets = new Set([\n target,\n ...(installed ? [launchdPaths(location, installed.label).target] : []),\n ]);\n for (const serviceTarget of targets) {\n if (await loaded(serviceTarget)) {\n try {\n await launchctl(['bootout', serviceTarget]);\n } catch (error) {\n if (await loaded(serviceTarget)) throw error;\n }\n }\n }\n await rm(temporary, { force: true });\n}\n\nexport async function configureLaunchdLogin(location: CovroPaths, entry: string, install: boolean) {\n // Preserve the installed label so older CLIs can still uninstall it after a rollback.\n const { label, login } = launchdPaths(location, `dev.covro.${location.key}`);\n const previous = await loginRegistration(location);\n if (install) {\n if (!(await guiSessionAvailable(location)))\n throw new Error('\u542F\u7528\u767B\u5F55\u81EA\u542F\u9700\u8981\u5F53\u524D\u7528\u6237\u5DF2\u767B\u5F55 macOS \u684C\u9762');\n await mkdir(dirname(login), { recursive: true });\n await writeFile(\n login,\n launchdPlist(location, [process.execPath, entry, 'run', '--config', location.config], true),\n { mode: 0o600 },\n );\n await saveJson(join(location.state, 'service.json'), { platform: 'darwin', label });\n if (previous && previous.label !== label)\n await rm(launchdPaths(location, previous.label).login, { force: true });\n } else {\n if (previous) await rm(launchdPaths(location, previous.label).login, { force: true });\n await rm(login, { force: true });\n await rm(join(location.state, 'service.json'), { force: true });\n }\n}\n\n/** Caller holds the per-instance lifecycle lock and has verified the daemon is stopped. */\nexport async function startLaunchd(location: CovroPaths, entry: string) {\n const installed = await loginRegistration(location);\n const { domain, target, temporary, login } = launchdPaths(location, installed?.label);\n if (!(await guiSessionAvailable(location))) {\n if (installed) throw new Error('\u5F53\u524D\u6CA1\u6709 macOS \u684C\u9762\u767B\u5F55\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u542F\u52A8 GUI \u767B\u5F55\u670D\u52A1');\n return false;\n }\n await unloadLaunchd(location);\n if (installed) {\n await launchctl(['enable', target]);\n await launchctl(['bootstrap', domain, login]);\n return true;\n }\n const bridge = await environmentBridge(location.home);\n try {\n const runner = join(location.state, 'launch-runner.mjs');\n await writeFile(runner, launchRunner, { mode: 0o600 });\n await writeFile(\n temporary,\n launchdPlist(\n location,\n [process.execPath, runner, bridge.path, entry, location.config],\n false,\n ),\n { mode: 0o600 },\n );\n await launchctl(['enable', target]);\n await launchctl(['bootstrap', domain, temporary]);\n await bridge.wait();\n return true;\n } catch (error) {\n let cleanup = '';\n try {\n await unloadLaunchd(location);\n } catch (cleanupError) {\n cleanup = `\uFF1B\u6E05\u7406\u4EFB\u52A1\u5931\u8D25\uFF1A${failureDetail(cleanupError)}`;\n }\n throw new Error(\n `Covro GUI \u540E\u53F0\u542F\u52A8\u5931\u8D25\uFF1A${failureDetail(error)}${cleanup}\uFF1B\u8FDB\u7A0B\u65E5\u5FD7\u4F4D\u4E8E ${location.log}`,\n { cause: error },\n );\n } finally {\n await bridge.close();\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }