@covro/covro 0.0.0-stage → 0.1.0-dev.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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -2
  3. package/README.zh-CN.md +174 -0
  4. package/bin/covro.mjs +8 -0
  5. package/dist/cli/control.js +66 -0
  6. package/dist/cli/control.js.map +7 -0
  7. package/dist/cli/daemon.js +118 -0
  8. package/dist/cli/daemon.js.map +7 -0
  9. package/dist/cli/main.js +152 -0
  10. package/dist/cli/main.js.map +7 -0
  11. package/dist/cli/paths.js +60 -0
  12. package/dist/cli/paths.js.map +7 -0
  13. package/dist/cli/service.js +87 -0
  14. package/dist/cli/service.js.map +7 -0
  15. package/dist/codex/adapter.js +1195 -0
  16. package/dist/codex/adapter.js.map +7 -0
  17. package/dist/codex/auto-review.js +102 -0
  18. package/dist/codex/auto-review.js.map +7 -0
  19. package/dist/codex/compatibility.js +7 -0
  20. package/dist/codex/compatibility.js.map +7 -0
  21. package/dist/codex/mapping.js +210 -0
  22. package/dist/codex/mapping.js.map +7 -0
  23. package/dist/codex/permissions.js +51 -0
  24. package/dist/codex/permissions.js.map +7 -0
  25. package/dist/codex/thread-title-policy.js +52 -0
  26. package/dist/codex/thread-title-policy.js.map +7 -0
  27. package/dist/codex/thread-titles.js +259 -0
  28. package/dist/codex/thread-titles.js.map +7 -0
  29. package/dist/codex/transport.js +236 -0
  30. package/dist/codex/transport.js.map +7 -0
  31. package/dist/codex/turn-token-usage.js +46 -0
  32. package/dist/codex/turn-token-usage.js.map +7 -0
  33. package/dist/codex/types.js +1 -0
  34. package/dist/codex/types.js.map +7 -0
  35. package/dist/drizzle/0000_past_wildside.sql +45 -0
  36. package/dist/drizzle/0001_shocking_ender_wiggin.sql +2 -0
  37. package/dist/drizzle/0002_allow_parallel_threads.sql +1 -0
  38. package/dist/drizzle/0003_project_registrations.sql +11 -0
  39. package/dist/drizzle/0004_turn_usage.sql +6 -0
  40. package/dist/drizzle/meta/0000_snapshot.json +309 -0
  41. package/dist/drizzle/meta/0001_snapshot.json +323 -0
  42. package/dist/drizzle/meta/0002_snapshot.json +315 -0
  43. package/dist/drizzle/meta/0003_snapshot.json +381 -0
  44. package/dist/drizzle/meta/0004_snapshot.json +420 -0
  45. package/dist/drizzle/meta/_journal.json +41 -0
  46. package/dist/server/auth.js +47 -0
  47. package/dist/server/auth.js.map +7 -0
  48. package/dist/server/compaction.js +40 -0
  49. package/dist/server/compaction.js.map +7 -0
  50. package/dist/server/config-store.js +147 -0
  51. package/dist/server/config-store.js.map +7 -0
  52. package/dist/server/config.js +124 -0
  53. package/dist/server/config.js.map +7 -0
  54. package/dist/server/db/index.js +21 -0
  55. package/dist/server/db/index.js.map +7 -0
  56. package/dist/server/db/migrate.js +26 -0
  57. package/dist/server/db/migrate.js.map +7 -0
  58. package/dist/server/db/operations.js +201 -0
  59. package/dist/server/db/operations.js.map +7 -0
  60. package/dist/server/db/schema.js +99 -0
  61. package/dist/server/db/schema.js.map +7 -0
  62. package/dist/server/db/turn-usage.js +31 -0
  63. package/dist/server/db/turn-usage.js.map +7 -0
  64. package/dist/server/events.js +123 -0
  65. package/dist/server/events.js.map +7 -0
  66. package/dist/server/http.js +305 -0
  67. package/dist/server/http.js.map +7 -0
  68. package/dist/server/images.js +49 -0
  69. package/dist/server/images.js.map +7 -0
  70. package/dist/server/instance-lock.js +25 -0
  71. package/dist/server/instance-lock.js.map +7 -0
  72. package/dist/server/local-files.js +100 -0
  73. package/dist/server/local-files.js.map +7 -0
  74. package/dist/server/main.js +125 -0
  75. package/dist/server/main.js.map +7 -0
  76. package/dist/server/markdown.js +28 -0
  77. package/dist/server/markdown.js.map +7 -0
  78. package/dist/server/projection.js +348 -0
  79. package/dist/server/projection.js.map +7 -0
  80. package/dist/server/projects.js +230 -0
  81. package/dist/server/projects.js.map +7 -0
  82. package/dist/server/service.js +1221 -0
  83. package/dist/server/service.js.map +7 -0
  84. package/dist/server/snapshot.js +98 -0
  85. package/dist/server/snapshot.js.map +7 -0
  86. package/dist/server/thread-export.js +97 -0
  87. package/dist/server/thread-export.js.map +7 -0
  88. package/dist/server/websocket.js +126 -0
  89. package/dist/server/websocket.js.map +7 -0
  90. package/dist/shared/async-questions.js +39 -0
  91. package/dist/shared/async-questions.js.map +7 -0
  92. package/dist/shared/codex-compatibility.js +56 -0
  93. package/dist/shared/codex-compatibility.js.map +7 -0
  94. package/dist/shared/elicitation.js +314 -0
  95. package/dist/shared/elicitation.js.map +7 -0
  96. package/dist/shared/errors.js +24 -0
  97. package/dist/shared/errors.js.map +7 -0
  98. package/dist/shared/gateway-config.js +1 -0
  99. package/dist/shared/gateway-config.js.map +7 -0
  100. package/dist/shared/images.js +21 -0
  101. package/dist/shared/images.js.map +7 -0
  102. package/dist/shared/package.js +5 -0
  103. package/dist/shared/package.js.map +7 -0
  104. package/dist/shared/permissions.js +63 -0
  105. package/dist/shared/permissions.js.map +7 -0
  106. package/dist/shared/thread-name.js +14 -0
  107. package/dist/shared/thread-name.js.map +7 -0
  108. package/dist/shared/thread-summary.js +24 -0
  109. package/dist/shared/thread-summary.js.map +7 -0
  110. package/dist/shared/types.js +1 -0
  111. package/dist/shared/types.js.map +7 -0
  112. package/dist/shared/updates.js +7 -0
  113. package/dist/shared/updates.js.map +7 -0
  114. package/dist/shared/validation.js +116 -0
  115. package/dist/shared/validation.js.map +7 -0
  116. package/dist/shared/versions.js +36 -0
  117. package/dist/shared/versions.js.map +7 -0
  118. package/dist/update-worker.cjs +21968 -0
  119. package/dist/updates/files.js +54 -0
  120. package/dist/updates/files.js.map +7 -0
  121. package/dist/updates/manager.js +315 -0
  122. package/dist/updates/manager.js.map +7 -0
  123. package/dist/updates/registry.js +71 -0
  124. package/dist/updates/registry.js.map +7 -0
  125. package/dist/updates/transaction.js +96 -0
  126. package/dist/updates/transaction.js.map +7 -0
  127. package/dist/updates/worker.js +345 -0
  128. package/dist/updates/worker.js.map +7 -0
  129. package/dist/web/assets/index-BW9afH_Y.js +83 -0
  130. package/dist/web/assets/index-L3S1ZiO0.css +1 -0
  131. package/dist/web/covro.svg +6 -0
  132. package/dist/web/index.html +15 -0
  133. package/package.json +88 -4
@@ -0,0 +1,54 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { dirname } from "node:path";
3
+ import { cp, mkdir, open, readFile, rename, rm } from "node:fs/promises";
4
+ async function readJson(filename) {
5
+ try {
6
+ return JSON.parse(await readFile(filename, "utf8"));
7
+ } catch (error) {
8
+ if (error.code === "ENOENT") return void 0;
9
+ throw error;
10
+ }
11
+ }
12
+ async function saveJson(filename, value) {
13
+ await mkdir(dirname(filename), { recursive: true, mode: 448 });
14
+ const temporary = `${filename}.${randomUUID()}.tmp`;
15
+ try {
16
+ const file = await open(temporary, "wx", 384);
17
+ try {
18
+ await file.writeFile(`${JSON.stringify(value, null, 2)}
19
+ `);
20
+ await file.sync();
21
+ } finally {
22
+ await file.close();
23
+ }
24
+ await rename(temporary, filename);
25
+ await syncDirectory(dirname(filename));
26
+ } finally {
27
+ await rm(temporary, { force: true });
28
+ }
29
+ }
30
+ async function syncDirectory(directory) {
31
+ if (process.platform === "win32") return;
32
+ const handle = await open(directory, "r");
33
+ try {
34
+ await handle.sync();
35
+ } finally {
36
+ await handle.close();
37
+ }
38
+ }
39
+ async function copyDurable(source, target) {
40
+ await cp(source, target);
41
+ const file = await open(target, "r");
42
+ try {
43
+ await file.sync();
44
+ } finally {
45
+ await file.close();
46
+ }
47
+ await syncDirectory(dirname(target));
48
+ }
49
+ export {
50
+ copyDurable,
51
+ readJson,
52
+ saveJson
53
+ };
54
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/updates/files.ts"],
4
+ "sourcesContent": ["import { randomUUID } from 'node:crypto';\nimport { dirname } from 'node:path';\nimport { cp, mkdir, open, readFile, rename, rm } from 'node:fs/promises';\nexport async function readJson<T>(filename: string): Promise<T | undefined> {\n try {\n return JSON.parse(await readFile(filename, 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\nexport async function saveJson(filename: string, value: unknown) {\n await mkdir(dirname(filename), { recursive: true, mode: 0o700 });\n const temporary = `${filename}.${randomUUID()}.tmp`;\n try {\n const file = await open(temporary, 'wx', 0o600);\n try {\n await file.writeFile(`${JSON.stringify(value, null, 2)}\\n`);\n await file.sync();\n } finally {\n await file.close();\n }\n await rename(temporary, filename);\n await syncDirectory(dirname(filename));\n } finally {\n await rm(temporary, { force: true });\n }\n}\n\nasync function syncDirectory(directory: string) {\n if (process.platform === 'win32') return;\n const handle = await open(directory, 'r');\n try {\n await handle.sync();\n } finally {\n await handle.close();\n }\n}\n\nexport async function copyDurable(source: string, target: string) {\n await cp(source, target);\n const file = await open(target, 'r');\n try {\n await file.sync();\n } finally {\n await file.close();\n }\n await syncDirectory(dirname(target));\n}\n"],
5
+ "mappings": "AAAA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,IAAI,OAAO,MAAM,UAAU,QAAQ,UAAU;AACtD,eAAsB,SAAY,UAA0C;AAC1E,MAAI;AACF,WAAO,KAAK,MAAM,MAAM,SAAS,UAAU,MAAM,CAAC;AAAA,EACpD,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;AACA,eAAsB,SAAS,UAAkB,OAAgB;AAC/D,QAAM,MAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC/D,QAAM,YAAY,GAAG,QAAQ,IAAI,WAAW,CAAC;AAC7C,MAAI;AACF,UAAM,OAAO,MAAM,KAAK,WAAW,MAAM,GAAK;AAC9C,QAAI;AACF,YAAM,KAAK,UAAU,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,CAAI;AAC1D,YAAM,KAAK,KAAK;AAAA,IAClB,UAAE;AACA,YAAM,KAAK,MAAM;AAAA,IACnB;AACA,UAAM,OAAO,WAAW,QAAQ;AAChC,UAAM,cAAc,QAAQ,QAAQ,CAAC;AAAA,EACvC,UAAE;AACA,UAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;AAAA,EACrC;AACF;AAEA,eAAe,cAAc,WAAmB;AAC9C,MAAI,QAAQ,aAAa,QAAS;AAClC,QAAM,SAAS,MAAM,KAAK,WAAW,GAAG;AACxC,MAAI;AACF,UAAM,OAAO,KAAK;AAAA,EACpB,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AAEA,eAAsB,YAAY,QAAgB,QAAgB;AAChE,QAAM,GAAG,QAAQ,MAAM;AACvB,QAAM,OAAO,MAAM,KAAK,QAAQ,GAAG;AACnC,MAAI;AACF,UAAM,KAAK,KAAK;AAAA,EAClB,UAAE;AACA,UAAM,KAAK,MAAM;AAAA,EACnB;AACA,QAAM,cAAc,QAAQ,MAAM,CAAC;AACrC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,315 @@
1
+ import { CODEX_COMPATIBILITY } from "../codex/compatibility.js";
2
+ import {
3
+ assessCodexCompatibility,
4
+ requireCodexCompatibility
5
+ } from "../shared/codex-compatibility.js";
6
+ import { createHash } from "node:crypto";
7
+ import { spawn } from "node:child_process";
8
+ import { copyFile, mkdir, open } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import lockfile from "proper-lockfile";
11
+ import { z } from "zod";
12
+ import { AppError } from "../shared/errors.js";
13
+ import {
14
+ releaseChannels,
15
+ updateActive
16
+ } from "../shared/updates.js";
17
+ import { packageInfo, packageRoot } from "../cli/paths.js";
18
+ import { readJson, saveJson } from "./files.js";
19
+ import { compareVersions, inspectInstallation, lookupRelease } from "./registry.js";
20
+ const settingsSchema = z.object({ channel: z.enum(releaseChannels), automatic: z.boolean() }).strict();
21
+ const updateRequestSchema = z.object({
22
+ clientOperationId: z.uuid(),
23
+ channel: z.enum(releaseChannels).optional(),
24
+ allowDowngrade: z.boolean().optional(),
25
+ codexConfirmation: z.object({ version: z.string().max(128), target: z.string().max(128) }).strict().optional()
26
+ }).strict();
27
+ class UpdateManager {
28
+ constructor(location, dataDir, codexVersion = () => null) {
29
+ this.codexVersion = codexVersion;
30
+ if (location && dataDir)
31
+ this.context = {
32
+ location,
33
+ dataDir,
34
+ root: packageRoot,
35
+ directory: join(
36
+ location.home,
37
+ "updates",
38
+ createHash("sha256").update(packageRoot).digest("hex").slice(0, 16)
39
+ )
40
+ };
41
+ }
42
+ codexVersion;
43
+ settings = {
44
+ channel: packageInfo.covroChannel ?? (packageInfo.version?.includes("-dev.") ? "dev" : packageInfo.version?.includes("-beta.") ? "beta" : "stable"),
45
+ automatic: false
46
+ };
47
+ available;
48
+ checkedAt;
49
+ checkError;
50
+ reason = "\u5F53\u524D\u670D\u52A1\u672A\u901A\u8FC7 covro \u542F\u52A8\uFF0C\u7F51\u9875\u4EC5\u652F\u6301\u7248\u672C\u68C0\u6D4B\uFF1B\u5347\u7EA7\u8BF7\u4F7F\u7528\u539F\u90E8\u7F72\u65B9\u5F0F\u3002";
51
+ timer;
52
+ checking;
53
+ network = new AbortController();
54
+ context;
55
+ async initialize() {
56
+ if (!this.context) return;
57
+ const saved = await readJson(join(this.context.directory, "settings.json"));
58
+ if (saved !== void 0) this.settings = settingsSchema.parse(saved);
59
+ this.reason = process.platform === "win32" ? "Windows \u6682\u4E0D\u652F\u6301\u53D7\u7BA1\u81EA\u52A8\u5347\u7EA7\uFF0C\u8BF7\u4F7F\u7528 npm \u66F4\u65B0\u5E76\u91CD\u542F\u3002" : await inspectInstallation(this.context.root) ?? "";
60
+ this.timer = setInterval(() => void this.automatic().catch(() => {
61
+ }), 30 * 6e4);
62
+ this.timer.unref();
63
+ void this.automatic().catch(() => {
64
+ });
65
+ }
66
+ async status() {
67
+ const run = this.context ? await readJson(join(this.context.directory, "run.json")) : void 0;
68
+ return {
69
+ version: packageInfo.version ?? "unknown",
70
+ commit: packageInfo.covroCommit,
71
+ source: "npm",
72
+ codex: assessCodexCompatibility(CODEX_COMPATIBILITY, this.codexVersion()),
73
+ settings: this.settings,
74
+ supported: !this.reason,
75
+ unsupportedReason: this.reason || void 0,
76
+ available: this.available ? {
77
+ ...this.available,
78
+ codex: assessCodexCompatibility(this.available.codex.manifest, this.codexVersion())
79
+ } : void 0,
80
+ checkedAt: this.checkedAt,
81
+ checkError: this.checkError,
82
+ run
83
+ };
84
+ }
85
+ async check(channel = this.settings.channel) {
86
+ try {
87
+ const release = await lookupRelease(channel, this.network.signal);
88
+ this.available = {
89
+ version: release.version,
90
+ channel,
91
+ commit: release.gitHead,
92
+ downgrade: compareVersions(release.version, packageInfo.version) < 0,
93
+ codex: assessCodexCompatibility(release.covroCodex, this.codexVersion())
94
+ };
95
+ this.checkError = void 0;
96
+ } catch (error) {
97
+ this.available = void 0;
98
+ this.checkError = error instanceof Error ? error.message : "\u65E0\u6CD5\u68C0\u67E5\u66F4\u65B0";
99
+ }
100
+ this.checkedAt = Date.now();
101
+ return this.status();
102
+ }
103
+ async configure(input) {
104
+ if (!this.context || this.reason) throw new AppError("UPDATE_UNSUPPORTED", this.reason, 409);
105
+ this.settings = settingsSchema.parse(input);
106
+ await saveJson(join(this.context.directory, "settings.json"), this.settings);
107
+ return this.status();
108
+ }
109
+ async activationPending() {
110
+ const run = (await this.status()).run;
111
+ if (run && !run.committed && run.phase === "failed" && run.from !== packageInfo.version)
112
+ throw new Error(
113
+ "\u4E0A\u6B21\u5347\u7EA7\u5C1A\u672A\u5B8C\u6210\u6062\u590D\uFF1B\u8BF7\u8FD0\u884C covro update repair \u540E\u518D\u542F\u52A8\uFF0C\u907F\u514D\u5199\u5165\u672A\u6FC0\u6D3B\u7248\u672C\u3002"
114
+ );
115
+ if (run && !run.committed && run.phase === "stopping")
116
+ throw new Error("\u5347\u7EA7\u6B63\u5728\u5207\u6362\u5B89\u88C5\uFF0C\u8BF7\u7B49\u5F85\u5347\u7EA7\u5B8C\u6210\uFF1B\u4E0D\u8981\u5E76\u884C\u542F\u52A8\u670D\u52A1\u3002");
117
+ if (run && !run.committed && run.phase === "verifying") {
118
+ if (run.target !== packageInfo.version)
119
+ throw new Error("\u5347\u7EA7\u5019\u9009\u8EAB\u4EFD\u4E0D\u5339\u914D\uFF0C\u8BF7\u8FD0\u884C covro update repair");
120
+ return true;
121
+ }
122
+ return false;
123
+ }
124
+ async commitActivation(id) {
125
+ if (!this.context) throw new Error("\u5F53\u524D\u4E0D\u662F\u53D7\u7BA1\u5B89\u88C5");
126
+ const run = (await this.status()).run;
127
+ if (!run || run.id !== id || run.phase !== "verifying" || run.target !== packageInfo.version)
128
+ throw new Error("\u5347\u7EA7\u8BB0\u5F55\u3001\u7248\u672C\u6216\u8FD0\u884C\u72B6\u6001\u4E0D\u5339\u914D");
129
+ if (!run.codex || run.codex.version !== this.codexVersion())
130
+ throw new Error("Codex \u7248\u672C\u5728\u5347\u7EA7\u671F\u95F4\u53D1\u751F\u53D8\u5316\u6216\u7F3A\u5C11\u6821\u9A8C\u8BB0\u5F55\uFF0C\u62D2\u7EDD\u6FC0\u6D3B");
131
+ requireCodexCompatibility(
132
+ CODEX_COMPATIBILITY,
133
+ this.codexVersion(),
134
+ run.codex.confirmedUnverified
135
+ );
136
+ this.settings = { ...this.settings, channel: run.channel };
137
+ await saveJson(join(this.context.directory, "settings.json"), this.settings);
138
+ await saveJson(join(this.context.directory, "run.json"), {
139
+ ...run,
140
+ committed: true,
141
+ phase: "succeeded",
142
+ message: "\u5347\u7EA7\u6210\u529F\uFF0C\u670D\u52A1\u5DF2\u6062\u590D",
143
+ updatedAt: Date.now()
144
+ });
145
+ }
146
+ async start(input) {
147
+ const request = updateRequestSchema.parse(input);
148
+ if (!this.context || this.reason) throw new AppError("UPDATE_UNSUPPORTED", this.reason, 409);
149
+ await mkdir(this.context.directory, { recursive: true, mode: 448 });
150
+ let releaseLock;
151
+ try {
152
+ releaseLock = await lockfile.lock(this.context.directory, {
153
+ retries: 0,
154
+ stale: 1e4,
155
+ update: 2e3
156
+ });
157
+ } catch {
158
+ return this.status();
159
+ }
160
+ try {
161
+ const before = await this.status();
162
+ if (await readJson(
163
+ join(this.context.directory, "requests", `${request.clientOperationId}.json`)
164
+ ))
165
+ return before;
166
+ if (before.run?.id === request.clientOperationId) return before;
167
+ if (updateActive(before.run)) {
168
+ if (Date.now() - before.run.updatedAt > 12e4)
169
+ throw new AppError(
170
+ "UPDATE_INTERRUPTED",
171
+ "\u4E0A\u6B21\u5347\u7EA7\u8FDB\u7A0B\u5DF2\u5931\u8054\u3002\u8BF7\u4F7F\u7528 covro update repair \u6062\u590D\uFF0C\u4FDD\u7559\u5F53\u524D\u5B89\u88C5\u548C\u5907\u4EFD\u3002",
172
+ 409
173
+ );
174
+ return before;
175
+ }
176
+ const channel = request.channel ?? this.settings.channel;
177
+ const checked = await this.check(channel);
178
+ if (!checked.available)
179
+ throw new AppError("UPDATE_CHECK_FAILED", checked.checkError ?? "\u672A\u627E\u5230\u53EF\u7528\u7248\u672C", 502);
180
+ if (checked.available.downgrade && !request.allowDowngrade)
181
+ throw new AppError(
182
+ "UPDATE_DOWNGRADE",
183
+ "\u76EE\u6807\u7248\u672C\u8F83\u65E7\uFF0C\u9700\u8981\u660E\u786E\u5141\u8BB8\u964D\u7EA7\uFF1B\u4ECD\u5C06\u68C0\u67E5\u6570\u636E\u5E93\u517C\u5BB9\u6027\u3002",
184
+ 409
185
+ );
186
+ if (checked.available.version === packageInfo.version) {
187
+ return this.configure({ ...this.settings, channel });
188
+ }
189
+ const confirmation = request.codexConfirmation;
190
+ const codex = checked.available.codex;
191
+ const confirmedUnverified = Boolean(
192
+ confirmation && confirmation.target === checked.available.version && confirmation.version === codex.version
193
+ );
194
+ if (confirmation && !confirmedUnverified)
195
+ throw new AppError(
196
+ "UPDATE_CODEX_CHANGED",
197
+ "\u76EE\u6807\u7248\u672C\u6216 Codex \u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u5E76\u786E\u8BA4\u3002",
198
+ 409
199
+ );
200
+ try {
201
+ requireCodexCompatibility(codex.manifest, codex.version, confirmedUnverified);
202
+ } catch (error) {
203
+ throw new AppError("UPDATE_CODEX_INCOMPATIBLE", error.message, 409);
204
+ }
205
+ const run = {
206
+ id: request.clientOperationId,
207
+ from: packageInfo.version,
208
+ target: checked.available.version,
209
+ channel,
210
+ codex: { version: codex.version, manifest: codex.manifest, confirmedUnverified },
211
+ phase: "installing",
212
+ message: "\u6B63\u5728\u5B89\u88C5\u5019\u9009 npm \u5305",
213
+ startedAt: Date.now(),
214
+ updatedAt: Date.now()
215
+ };
216
+ await saveJson(join(this.context.directory, "context.json"), this.context);
217
+ const worker = join(this.context.directory, "worker.cjs");
218
+ await copyFile(join(packageRoot, "dist/update-worker.cjs"), worker);
219
+ const log = await open(join(this.context.directory, "update.log"), "a", 384);
220
+ try {
221
+ await saveJson(join(this.context.directory, "run.json"), run);
222
+ await saveJson(
223
+ join(this.context.directory, "requests", `${request.clientOperationId}.json`),
224
+ { target: run.target, channel: run.channel }
225
+ );
226
+ const child = spawn(process.execPath, [worker, this.context.directory], {
227
+ detached: true,
228
+ stdio: ["ignore", log.fd, log.fd],
229
+ windowsHide: true
230
+ });
231
+ await new Promise((done, reject) => {
232
+ child.once("spawn", done);
233
+ child.once("error", reject);
234
+ });
235
+ child.unref();
236
+ } catch (error) {
237
+ await saveJson(join(this.context.directory, "run.json"), {
238
+ ...run,
239
+ phase: "failed",
240
+ message: String(error),
241
+ updatedAt: Date.now()
242
+ });
243
+ throw error;
244
+ } finally {
245
+ await log.close();
246
+ }
247
+ return this.status();
248
+ } finally {
249
+ await releaseLock();
250
+ }
251
+ }
252
+ async repair() {
253
+ if (!this.context) throw new Error("\u6CA1\u6709\u53EF\u6062\u590D\u7684\u53D7\u7BA1\u5B89\u88C5");
254
+ const directory = this.context.directory;
255
+ let releaseLock;
256
+ try {
257
+ releaseLock = await lockfile.lock(directory, { retries: 0, stale: 1e4, update: 2e3 });
258
+ } catch {
259
+ throw new Error("\u5347\u7EA7\u8FDB\u7A0B\u4ECD\u6301\u6709\u9501\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u540E\u518D\u6062\u590D");
260
+ }
261
+ try {
262
+ const run = (await this.status()).run;
263
+ if (!run) throw new Error("\u6CA1\u6709\u53EF\u6062\u590D\u7684\u5347\u7EA7\u8BB0\u5F55");
264
+ if (run.committed || run.phase === "succeeded") return this.status();
265
+ if (updateActive(run) && Date.now() - run.updatedAt < 3e4)
266
+ throw new Error("\u5347\u7EA7\u521A\u5F00\u59CB\uFF0C\u8BF7\u7B49\u5F85\u81F3\u5C11 30 \u79D2\u540E\u518D\u6062\u590D\u5931\u8054\u8FDB\u7A0B");
267
+ const log = await open(join(directory, "update.log"), "a", 384);
268
+ try {
269
+ const child = spawn(
270
+ process.execPath,
271
+ [join(directory, "worker.cjs"), directory, "--repair"],
272
+ {
273
+ detached: true,
274
+ stdio: ["ignore", log.fd, log.fd],
275
+ windowsHide: true
276
+ }
277
+ );
278
+ await new Promise((done, reject) => {
279
+ child.once("spawn", done);
280
+ child.once("error", reject);
281
+ });
282
+ child.unref();
283
+ } finally {
284
+ await log.close();
285
+ }
286
+ return this.status();
287
+ } finally {
288
+ await releaseLock();
289
+ }
290
+ }
291
+ async automatic() {
292
+ if (!this.context || this.reason || this.checking) return;
293
+ this.checking = (async () => {
294
+ const result = await this.check();
295
+ if (this.settings.automatic && result.available && result.available.codex.status === "verified" && !result.available.downgrade && result.available.version !== packageInfo.version && result.run?.target !== result.available.version && !updateActive(result.run))
296
+ return this.start({ clientOperationId: crypto.randomUUID() });
297
+ return result;
298
+ })();
299
+ try {
300
+ await this.checking;
301
+ } finally {
302
+ this.checking = void 0;
303
+ }
304
+ }
305
+ close() {
306
+ this.network.abort();
307
+ clearInterval(this.timer);
308
+ }
309
+ }
310
+ export {
311
+ UpdateManager,
312
+ settingsSchema,
313
+ updateRequestSchema
314
+ };
315
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/updates/manager.ts"],
4
+ "sourcesContent": ["import { CODEX_COMPATIBILITY } from '../codex/compatibility.js';\nimport {\n assessCodexCompatibility,\n requireCodexCompatibility,\n} from '../shared/codex-compatibility.js';\nimport { createHash } from 'node:crypto';\nimport { spawn } from 'node:child_process';\nimport { copyFile, mkdir, open } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport lockfile from 'proper-lockfile';\nimport { z } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport {\n releaseChannels,\n updateActive,\n type UpdateController,\n type UpdateSettings,\n type UpdateRun,\n type UpdateStatus,\n type ReleaseChannel,\n} from '../shared/updates.js';\nimport { packageInfo, packageRoot, type CovroPaths } from '../cli/paths.js';\nimport { readJson, saveJson } from './files.js';\nimport { compareVersions, inspectInstallation, lookupRelease } from './registry.js';\n\nexport const settingsSchema = z\n .object({ channel: z.enum(releaseChannels), automatic: z.boolean() })\n .strict();\nexport const updateRequestSchema = z\n .object({\n clientOperationId: z.uuid(),\n channel: z.enum(releaseChannels).optional(),\n allowDowngrade: z.boolean().optional(),\n codexConfirmation: z\n .object({ version: z.string().max(128), target: z.string().max(128) })\n .strict()\n .optional(),\n })\n .strict();\nexport interface UpdateContext {\n location: CovroPaths;\n dataDir: string;\n root: string;\n directory: string;\n}\nexport class UpdateManager implements UpdateController {\n private settings: UpdateSettings = {\n channel:\n packageInfo.covroChannel ??\n (packageInfo.version?.includes('-dev.')\n ? 'dev'\n : packageInfo.version?.includes('-beta.')\n ? 'beta'\n : 'stable'),\n automatic: false,\n };\n private available?: UpdateStatus['available'];\n private checkedAt?: number;\n private checkError?: string;\n private reason = '\u5F53\u524D\u670D\u52A1\u672A\u901A\u8FC7 covro \u542F\u52A8\uFF0C\u7F51\u9875\u4EC5\u652F\u6301\u7248\u672C\u68C0\u6D4B\uFF1B\u5347\u7EA7\u8BF7\u4F7F\u7528\u539F\u90E8\u7F72\u65B9\u5F0F\u3002';\n private timer?: ReturnType<typeof setInterval>;\n private checking?: Promise<UpdateStatus>;\n private readonly network = new AbortController();\n readonly context?: UpdateContext;\n constructor(\n location?: CovroPaths,\n dataDir?: string,\n private readonly codexVersion: () => string | null = () => null,\n ) {\n if (location && dataDir)\n this.context = {\n location,\n dataDir,\n root: packageRoot,\n directory: join(\n location.home,\n 'updates',\n createHash('sha256').update(packageRoot).digest('hex').slice(0, 16),\n ),\n };\n }\n async initialize() {\n if (!this.context) return;\n const saved = await readJson<unknown>(join(this.context.directory, 'settings.json'));\n if (saved !== undefined) this.settings = settingsSchema.parse(saved);\n this.reason =\n process.platform === 'win32'\n ? 'Windows \u6682\u4E0D\u652F\u6301\u53D7\u7BA1\u81EA\u52A8\u5347\u7EA7\uFF0C\u8BF7\u4F7F\u7528 npm \u66F4\u65B0\u5E76\u91CD\u542F\u3002'\n : ((await inspectInstallation(this.context.root)) ?? '');\n this.timer = setInterval(() => void this.automatic().catch(() => {}), 30 * 60_000);\n this.timer.unref();\n // No network request in the startup critical path.\n void this.automatic().catch(() => {});\n }\n async status(): Promise<UpdateStatus> {\n const run = this.context\n ? await readJson<UpdateRun>(join(this.context.directory, 'run.json'))\n : undefined;\n return {\n version: packageInfo.version ?? 'unknown',\n commit: packageInfo.covroCommit,\n source: 'npm',\n codex: assessCodexCompatibility(CODEX_COMPATIBILITY, this.codexVersion()),\n settings: this.settings,\n supported: !this.reason,\n unsupportedReason: this.reason || undefined,\n available: this.available\n ? {\n ...this.available,\n codex: assessCodexCompatibility(this.available.codex.manifest, this.codexVersion()),\n }\n : undefined,\n checkedAt: this.checkedAt,\n checkError: this.checkError,\n run,\n };\n }\n async check(channel: ReleaseChannel = this.settings.channel) {\n try {\n const release = await lookupRelease(channel, this.network.signal);\n this.available = {\n version: release.version,\n channel,\n commit: release.gitHead,\n downgrade: compareVersions(release.version, packageInfo.version) < 0,\n codex: assessCodexCompatibility(release.covroCodex, this.codexVersion()),\n };\n this.checkError = undefined;\n } catch (error) {\n this.available = undefined;\n this.checkError = error instanceof Error ? error.message : '\u65E0\u6CD5\u68C0\u67E5\u66F4\u65B0';\n }\n this.checkedAt = Date.now();\n return this.status();\n }\n async configure(input: UpdateSettings) {\n if (!this.context || this.reason) throw new AppError('UPDATE_UNSUPPORTED', this.reason, 409);\n this.settings = settingsSchema.parse(input);\n await saveJson(join(this.context.directory, 'settings.json'), this.settings);\n return this.status();\n }\n async activationPending() {\n const run = (await this.status()).run;\n if (run && !run.committed && run.phase === 'failed' && run.from !== packageInfo.version)\n throw new Error(\n '\u4E0A\u6B21\u5347\u7EA7\u5C1A\u672A\u5B8C\u6210\u6062\u590D\uFF1B\u8BF7\u8FD0\u884C covro update repair \u540E\u518D\u542F\u52A8\uFF0C\u907F\u514D\u5199\u5165\u672A\u6FC0\u6D3B\u7248\u672C\u3002',\n );\n if (run && !run.committed && run.phase === 'stopping')\n throw new Error('\u5347\u7EA7\u6B63\u5728\u5207\u6362\u5B89\u88C5\uFF0C\u8BF7\u7B49\u5F85\u5347\u7EA7\u5B8C\u6210\uFF1B\u4E0D\u8981\u5E76\u884C\u542F\u52A8\u670D\u52A1\u3002');\n if (run && !run.committed && run.phase === 'verifying') {\n if (run.target !== packageInfo.version)\n throw new Error('\u5347\u7EA7\u5019\u9009\u8EAB\u4EFD\u4E0D\u5339\u914D\uFF0C\u8BF7\u8FD0\u884C covro update repair');\n return true;\n }\n return false;\n }\n async commitActivation(id: string) {\n if (!this.context) throw new Error('\u5F53\u524D\u4E0D\u662F\u53D7\u7BA1\u5B89\u88C5');\n const run = (await this.status()).run;\n if (!run || run.id !== id || run.phase !== 'verifying' || run.target !== packageInfo.version)\n throw new Error('\u5347\u7EA7\u8BB0\u5F55\u3001\u7248\u672C\u6216\u8FD0\u884C\u72B6\u6001\u4E0D\u5339\u914D');\n if (!run.codex || run.codex.version !== this.codexVersion())\n throw new Error('Codex \u7248\u672C\u5728\u5347\u7EA7\u671F\u95F4\u53D1\u751F\u53D8\u5316\u6216\u7F3A\u5C11\u6821\u9A8C\u8BB0\u5F55\uFF0C\u62D2\u7EDD\u6FC0\u6D3B');\n requireCodexCompatibility(\n CODEX_COMPATIBILITY,\n this.codexVersion(),\n run.codex.confirmedUnverified,\n );\n this.settings = { ...this.settings, channel: run.channel };\n await saveJson(join(this.context.directory, 'settings.json'), this.settings);\n await saveJson(join(this.context.directory, 'run.json'), {\n ...run,\n committed: true,\n phase: 'succeeded',\n message: '\u5347\u7EA7\u6210\u529F\uFF0C\u670D\u52A1\u5DF2\u6062\u590D',\n updatedAt: Date.now(),\n });\n }\n async start(input: z.infer<typeof updateRequestSchema>) {\n const request = updateRequestSchema.parse(input);\n if (!this.context || this.reason) throw new AppError('UPDATE_UNSUPPORTED', this.reason, 409);\n await mkdir(this.context.directory, { recursive: true, mode: 0o700 });\n let releaseLock: () => Promise<void>;\n try {\n releaseLock = await lockfile.lock(this.context.directory, {\n retries: 0,\n stale: 10_000,\n update: 2_000,\n });\n } catch {\n return this.status();\n }\n try {\n const before = await this.status();\n if (\n await readJson(\n join(this.context.directory, 'requests', `${request.clientOperationId}.json`),\n )\n )\n return before;\n if (before.run?.id === request.clientOperationId) return before;\n if (updateActive(before.run)) {\n if (Date.now() - before.run!.updatedAt > 120_000)\n throw new AppError(\n 'UPDATE_INTERRUPTED',\n '\u4E0A\u6B21\u5347\u7EA7\u8FDB\u7A0B\u5DF2\u5931\u8054\u3002\u8BF7\u4F7F\u7528 covro update repair \u6062\u590D\uFF0C\u4FDD\u7559\u5F53\u524D\u5B89\u88C5\u548C\u5907\u4EFD\u3002',\n 409,\n );\n return before;\n }\n const channel = request.channel ?? this.settings.channel;\n const checked = await this.check(channel);\n if (!checked.available)\n throw new AppError('UPDATE_CHECK_FAILED', checked.checkError ?? '\u672A\u627E\u5230\u53EF\u7528\u7248\u672C', 502);\n if (checked.available.downgrade && !request.allowDowngrade)\n throw new AppError(\n 'UPDATE_DOWNGRADE',\n '\u76EE\u6807\u7248\u672C\u8F83\u65E7\uFF0C\u9700\u8981\u660E\u786E\u5141\u8BB8\u964D\u7EA7\uFF1B\u4ECD\u5C06\u68C0\u67E5\u6570\u636E\u5E93\u517C\u5BB9\u6027\u3002',\n 409,\n );\n if (checked.available.version === packageInfo.version) {\n return this.configure({ ...this.settings, channel });\n }\n const confirmation = request.codexConfirmation;\n const codex = checked.available.codex;\n const confirmedUnverified = Boolean(\n confirmation &&\n confirmation.target === checked.available.version &&\n confirmation.version === codex.version,\n );\n if (confirmation && !confirmedUnverified)\n throw new AppError(\n 'UPDATE_CODEX_CHANGED',\n '\u76EE\u6807\u7248\u672C\u6216 Codex \u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u5E76\u786E\u8BA4\u3002',\n 409,\n );\n try {\n requireCodexCompatibility(codex.manifest, codex.version, confirmedUnverified);\n } catch (error) {\n throw new AppError('UPDATE_CODEX_INCOMPATIBLE', (error as Error).message, 409);\n }\n const run: UpdateRun = {\n id: request.clientOperationId,\n from: packageInfo.version,\n target: checked.available.version,\n channel,\n codex: { version: codex.version!, manifest: codex.manifest!, confirmedUnverified },\n phase: 'installing',\n message: '\u6B63\u5728\u5B89\u88C5\u5019\u9009 npm \u5305',\n startedAt: Date.now(),\n updatedAt: Date.now(),\n };\n await saveJson(join(this.context.directory, 'context.json'), this.context);\n const worker = join(this.context.directory, 'worker.cjs');\n await copyFile(join(packageRoot, 'dist/update-worker.cjs'), worker);\n const log = await open(join(this.context.directory, 'update.log'), 'a', 0o600);\n try {\n await saveJson(join(this.context.directory, 'run.json'), run);\n await saveJson(\n join(this.context.directory, 'requests', `${request.clientOperationId}.json`),\n { target: run.target, channel: run.channel },\n );\n const child = spawn(process.execPath, [worker, this.context.directory], {\n detached: true,\n stdio: ['ignore', log.fd, log.fd],\n windowsHide: true,\n });\n await new Promise<void>((done, reject) => {\n child.once('spawn', done);\n child.once('error', reject);\n });\n child.unref();\n } catch (error) {\n await saveJson(join(this.context.directory, 'run.json'), {\n ...run,\n phase: 'failed',\n message: String(error),\n updatedAt: Date.now(),\n });\n throw error;\n } finally {\n await log.close();\n }\n return this.status();\n } finally {\n await releaseLock();\n }\n }\n async repair() {\n if (!this.context) throw new Error('\u6CA1\u6709\u53EF\u6062\u590D\u7684\u53D7\u7BA1\u5B89\u88C5');\n const directory = this.context.directory;\n let releaseLock: () => Promise<void>;\n try {\n releaseLock = await lockfile.lock(directory, { retries: 0, stale: 10_000, update: 2_000 });\n } catch {\n throw new Error('\u5347\u7EA7\u8FDB\u7A0B\u4ECD\u6301\u6709\u9501\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u540E\u518D\u6062\u590D');\n }\n try {\n const run = (await this.status()).run;\n if (!run) throw new Error('\u6CA1\u6709\u53EF\u6062\u590D\u7684\u5347\u7EA7\u8BB0\u5F55');\n if (run.committed || run.phase === 'succeeded') return this.status();\n if (updateActive(run) && Date.now() - run.updatedAt < 30_000)\n throw new Error('\u5347\u7EA7\u521A\u5F00\u59CB\uFF0C\u8BF7\u7B49\u5F85\u81F3\u5C11 30 \u79D2\u540E\u518D\u6062\u590D\u5931\u8054\u8FDB\u7A0B');\n const log = await open(join(directory, 'update.log'), 'a', 0o600);\n try {\n const child = spawn(\n process.execPath,\n [join(directory, 'worker.cjs'), directory, '--repair'],\n {\n detached: true,\n stdio: ['ignore', log.fd, log.fd],\n windowsHide: true,\n },\n );\n await new Promise<void>((done, reject) => {\n child.once('spawn', done);\n child.once('error', reject);\n });\n child.unref();\n } finally {\n await log.close();\n }\n return this.status();\n } finally {\n await releaseLock();\n }\n }\n private async automatic() {\n if (!this.context || this.reason || this.checking) return;\n this.checking = (async () => {\n const result = await this.check();\n if (\n this.settings.automatic &&\n result.available &&\n result.available.codex.status === 'verified' &&\n !result.available.downgrade &&\n result.available.version !== packageInfo.version &&\n result.run?.target !== result.available.version &&\n !updateActive(result.run)\n )\n return this.start({ clientOperationId: crypto.randomUUID() });\n return result;\n })();\n try {\n await this.checking;\n } finally {\n this.checking = undefined;\n }\n }\n close() {\n this.network.abort();\n clearInterval(this.timer);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,71 @@
1
+ import { npmPackageName } from "../shared/package.js";
2
+ import { validVersion } from "../shared/versions.js";
3
+ import { validVersion as validVersion2, compareVersions } from "../shared/versions.js";
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ import { realpath, access, lstat } from "node:fs/promises";
7
+ import { constants } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { z } from "zod";
10
+ const run = promisify(execFile);
11
+ const registry = "https://registry.npmjs.org";
12
+ const npmTags = {
13
+ stable: "latest",
14
+ beta: "beta",
15
+ dev: "dev"
16
+ };
17
+ const releaseSchema = z.object({
18
+ name: z.literal(npmPackageName),
19
+ version: z.string().refine(validVersion),
20
+ gitHead: z.string().optional(),
21
+ covroCodex: z.unknown().optional()
22
+ });
23
+ function packageMetadataUrl(selector) {
24
+ return `${registry}/${encodeURIComponent(npmPackageName)}/${encodeURIComponent(selector)}`;
25
+ }
26
+ async function lookupRelease(channel, signal) {
27
+ const response = await fetch(packageMetadataUrl(npmTags[channel]), {
28
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3),
29
+ headers: { accept: "application/json" }
30
+ });
31
+ if (!response.ok)
32
+ throw new Error(
33
+ response.status === 404 ? `npm \u5C1A\u672A\u53D1\u5E03 Covro \u7684 ${channel} \u6E20\u9053` : `npm \u67E5\u8BE2\u5931\u8D25\uFF08${response.status}\uFF09`
34
+ );
35
+ const body = await response.text();
36
+ if (body.length > 2 * 1024 * 1024) throw new Error("npm \u5143\u6570\u636E\u8FC7\u5927");
37
+ return releaseSchema.parse(JSON.parse(body));
38
+ }
39
+ async function inspectInstallation(root) {
40
+ try {
41
+ const { stdout } = await run("npm", ["root", "--global"], { timeout: 15e3 });
42
+ return await inspectPackageDirectory(root, stdout.trim());
43
+ } catch {
44
+ return "\u65E0\u6CD5\u786E\u8BA4\u53EF\u5199\u7684 npm \u5168\u5C40\u5B89\u88C5\uFF1B\u8BF7\u68C0\u67E5 npm\u3001\u5B89\u88C5\u76EE\u5F55\u6743\u9650\u53CA\u540E\u53F0 PATH\u3002";
45
+ }
46
+ }
47
+ async function inspectPackageDirectory(root, globalRoot) {
48
+ const expected = join(await realpath(globalRoot), npmPackageName);
49
+ if ((await lstat(expected)).isSymbolicLink() || await realpath(root) !== expected)
50
+ return "\u5F53\u524D\u4E3A\u6E90\u7801\u3001\u94FE\u63A5\u6216\u975E npm \u5168\u5C40\u5B89\u88C5\uFF1B\u8BF7\u4F7F\u7528\u539F\u90E8\u7F72\u65B9\u5F0F\u66F4\u65B0\u3002";
51
+ try {
52
+ await lstat(join(expected, ".git"));
53
+ return "\u5F53\u524D\u5B89\u88C5\u5305\u542B Git \u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u539F\u90E8\u7F72\u65B9\u5F0F\u66F4\u65B0\u3002";
54
+ } catch (error) {
55
+ if (error.code !== "ENOENT") throw error;
56
+ }
57
+ await access(root, constants.W_OK);
58
+ return void 0;
59
+ }
60
+ export {
61
+ compareVersions,
62
+ inspectInstallation,
63
+ inspectPackageDirectory,
64
+ lookupRelease,
65
+ npmTags,
66
+ packageMetadataUrl,
67
+ registry,
68
+ releaseSchema,
69
+ validVersion2 as validVersion
70
+ };
71
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/updates/registry.ts"],
4
+ "sourcesContent": ["import { npmPackageName } from '../shared/package.js';\nimport { validVersion } from '../shared/versions.js';\nexport { validVersion, compareVersions } from '../shared/versions.js';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { realpath, access, lstat } from 'node:fs/promises';\nimport { constants } from 'node:fs';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ReleaseChannel } from '../shared/updates.js';\n\nconst run = promisify(execFile);\nexport const registry = 'https://registry.npmjs.org';\nexport const npmTags: Record<ReleaseChannel, string> = {\n stable: 'latest',\n beta: 'beta',\n dev: 'dev',\n};\nexport const releaseSchema = z.object({\n name: z.literal(npmPackageName),\n version: z.string().refine(validVersion),\n gitHead: z.string().optional(),\n covroCodex: z.unknown().optional(),\n});\nexport function packageMetadataUrl(selector: string) {\n return `${registry}/${encodeURIComponent(npmPackageName)}/${encodeURIComponent(selector)}`;\n}\nexport async function lookupRelease(channel: ReleaseChannel, signal?: AbortSignal) {\n // Resolve a channel exactly once. Install the resulting immutable version, never the moving tag.\n const response = await fetch(packageMetadataUrl(npmTags[channel]), {\n signal: signal\n ? AbortSignal.any([signal, AbortSignal.timeout(15_000)])\n : AbortSignal.timeout(15_000),\n headers: { accept: 'application/json' },\n });\n if (!response.ok)\n throw new Error(\n response.status === 404\n ? `npm \u5C1A\u672A\u53D1\u5E03 Covro \u7684 ${channel} \u6E20\u9053`\n : `npm \u67E5\u8BE2\u5931\u8D25\uFF08${response.status}\uFF09`,\n );\n const body = await response.text();\n if (body.length > 2 * 1024 * 1024) throw new Error('npm \u5143\u6570\u636E\u8FC7\u5927');\n return releaseSchema.parse(JSON.parse(body));\n}\nexport async function inspectInstallation(root: string): Promise<string | undefined> {\n try {\n const { stdout } = await run('npm', ['root', '--global'], { timeout: 15_000 });\n return await inspectPackageDirectory(root, stdout.trim());\n } catch {\n return '\u65E0\u6CD5\u786E\u8BA4\u53EF\u5199\u7684 npm \u5168\u5C40\u5B89\u88C5\uFF1B\u8BF7\u68C0\u67E5 npm\u3001\u5B89\u88C5\u76EE\u5F55\u6743\u9650\u53CA\u540E\u53F0 PATH\u3002';\n }\n}\n\nexport async function inspectPackageDirectory(root: string, globalRoot: string) {\n const expected = join(await realpath(globalRoot), npmPackageName);\n // npm link and pnpm global point outside the package directory. Following\n // that link would let the worker rename a developer's source checkout.\n if ((await lstat(expected)).isSymbolicLink() || (await realpath(root)) !== expected)\n return '\u5F53\u524D\u4E3A\u6E90\u7801\u3001\u94FE\u63A5\u6216\u975E npm \u5168\u5C40\u5B89\u88C5\uFF1B\u8BF7\u4F7F\u7528\u539F\u90E8\u7F72\u65B9\u5F0F\u66F4\u65B0\u3002';\n try {\n await lstat(join(expected, '.git'));\n return '\u5F53\u524D\u5B89\u88C5\u5305\u542B Git \u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u539F\u90E8\u7F72\u65B9\u5F0F\u66F4\u65B0\u3002';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n await access(root, constants.W_OK);\n return undefined;\n}\n"],
5
+ "mappings": "AAAA,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,gBAAAA,eAAc,uBAAuB;AAC9C,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,UAAU,QAAQ,aAAa;AACxC,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,SAAS;AAGlB,MAAM,MAAM,UAAU,QAAQ;AACvB,MAAM,WAAW;AACjB,MAAM,UAA0C;AAAA,EACrD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AACP;AACO,MAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,QAAQ,cAAc;AAAA,EAC9B,SAAS,EAAE,OAAO,EAAE,OAAO,YAAY;AAAA,EACvC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,YAAY,EAAE,QAAQ,EAAE,SAAS;AACnC,CAAC;AACM,SAAS,mBAAmB,UAAkB;AACnD,SAAO,GAAG,QAAQ,IAAI,mBAAmB,cAAc,CAAC,IAAI,mBAAmB,QAAQ,CAAC;AAC1F;AACA,eAAsB,cAAc,SAAyB,QAAsB;AAEjF,QAAM,WAAW,MAAM,MAAM,mBAAmB,QAAQ,OAAO,CAAC,GAAG;AAAA,IACjE,QAAQ,SACJ,YAAY,IAAI,CAAC,QAAQ,YAAY,QAAQ,IAAM,CAAC,CAAC,IACrD,YAAY,QAAQ,IAAM;AAAA,IAC9B,SAAS,EAAE,QAAQ,mBAAmB;AAAA,EACxC,CAAC;AACD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,SAAS,WAAW,MAChB,6CAAoB,OAAO,kBAC3B,qCAAY,SAAS,MAAM;AAAA,IACjC;AACF,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,MAAI,KAAK,SAAS,IAAI,OAAO,KAAM,OAAM,IAAI,MAAM,oCAAW;AAC9D,SAAO,cAAc,MAAM,KAAK,MAAM,IAAI,CAAC;AAC7C;AACA,eAAsB,oBAAoB,MAA2C;AACnF,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,IAAI,OAAO,CAAC,QAAQ,UAAU,GAAG,EAAE,SAAS,KAAO,CAAC;AAC7E,WAAO,MAAM,wBAAwB,MAAM,OAAO,KAAK,CAAC;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,wBAAwB,MAAc,YAAoB;AAC9E,QAAM,WAAW,KAAK,MAAM,SAAS,UAAU,GAAG,cAAc;AAGhE,OAAK,MAAM,MAAM,QAAQ,GAAG,eAAe,KAAM,MAAM,SAAS,IAAI,MAAO;AACzE,WAAO;AACT,MAAI;AACF,UAAM,MAAM,KAAK,UAAU,MAAM,CAAC;AAClC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,OAAM;AAAA,EAChE;AACA,QAAM,OAAO,MAAM,UAAU,IAAI;AACjC,SAAO;AACT;",
6
+ "names": ["validVersion"]
7
+ }
@@ -0,0 +1,96 @@
1
+ import { npmPackageName } from "../shared/package.js";
2
+ import { mkdir, rename, rm, stat } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { copyDurable, saveJson } from "./files.js";
5
+ const exists = async (path) => stat(path).then(
6
+ () => true,
7
+ (error) => {
8
+ if (error.code === "ENOENT") return false;
9
+ throw error;
10
+ }
11
+ );
12
+ async function activateUpdate(paths, effects) {
13
+ let stopped = false;
14
+ let moved = false;
15
+ let backedUp = false;
16
+ let committed = false;
17
+ try {
18
+ await effects.phase("waiting", "\u5019\u9009\u5305\u5DF2\u51C6\u5907\u597D\uFF0C\u7B49\u5F85\u6240\u6709\u4EFB\u52A1\u3001\u5BA1\u6279\u4E0E\u5F85\u6838\u5BF9\u64CD\u4F5C\u7ED3\u675F");
19
+ await effects.waitForIdle();
20
+ await effects.phase("stopping", "\u5DF2\u5173\u95ED\u65B0\u4EFB\u52A1\u5165\u53E3\uFF0C\u6B63\u5728\u505C\u6B62\u65E7\u670D\u52A1\u5E76\u5907\u4EFD\u6570\u636E");
21
+ stopped = true;
22
+ await effects.stop();
23
+ await mkdir(paths.backup, { recursive: true, mode: 448 });
24
+ for (const name of ["app.sqlite", "app.sqlite-wal", "app.sqlite-shm"]) {
25
+ if (await exists(join(paths.dataDir, name)))
26
+ await copyDurable(join(paths.dataDir, name), join(paths.backup, name));
27
+ }
28
+ await copyDurable(paths.config, join(paths.backup, "config.json"));
29
+ await saveJson(join(paths.backup, "snapshot.json"), { complete: true });
30
+ backedUp = true;
31
+ await rename(paths.root, paths.previous);
32
+ moved = true;
33
+ await rename(paths.candidate, paths.root);
34
+ await effects.phase("verifying", "\u6B63\u5728\u542F\u52A8\u65B0\u7248\u3001\u8FC1\u79FB\u6570\u636E\u5E93\u5E76\u9A8C\u8BC1\uFF1B\u4E1A\u52A1\u5199\u5165\u4FDD\u6301\u6682\u505C");
35
+ await effects.start(true);
36
+ await effects.verify();
37
+ await effects.commit();
38
+ committed = true;
39
+ return;
40
+ } catch (error) {
41
+ if (committed || await effects.hasCommitted?.()) return;
42
+ if (!stopped) {
43
+ await effects.resume?.();
44
+ await effects.phase("failed", error instanceof Error ? error.message : String(error));
45
+ throw error;
46
+ }
47
+ try {
48
+ await effects.stop();
49
+ if (moved) {
50
+ await rm(paths.root, { recursive: true, force: true });
51
+ await rename(paths.previous, paths.root);
52
+ }
53
+ if (backedUp) {
54
+ for (const name of ["app.sqlite", "app.sqlite-wal", "app.sqlite-shm"]) {
55
+ await rm(join(paths.dataDir, name), { force: true });
56
+ if (await exists(join(paths.backup, name)))
57
+ await copyDurable(join(paths.backup, name), join(paths.dataDir, name));
58
+ }
59
+ await copyDurable(join(paths.backup, "config.json"), paths.config);
60
+ }
61
+ await effects.phase(
62
+ "rolledBack",
63
+ `\u65B0\u7248\u542F\u52A8\u5931\u8D25\uFF0C\u5DF2\u6062\u590D\u65E7\u7248\uFF1A${error instanceof Error ? error.message : String(error)}`
64
+ );
65
+ await effects.start(false);
66
+ } catch (recovery) {
67
+ await effects.phase(
68
+ "failed",
69
+ `\u5347\u7EA7\u5931\u8D25\u4E14\u6062\u590D\u5C1A\u672A\u5B8C\u6210\uFF1B\u4FDD\u7559\u5907\u4EFD ${paths.backup}\u3002${String(error)}\uFF1B\u6062\u590D\uFF1A${String(recovery)}`
70
+ );
71
+ throw recovery;
72
+ }
73
+ throw error;
74
+ }
75
+ }
76
+ function transactionPaths(root, id, dataDir, config, backup) {
77
+ const prefix = join(dirname(root), `.covro-stage-${id}`);
78
+ return {
79
+ root,
80
+ prefix,
81
+ candidate: join(
82
+ prefix,
83
+ process.platform === "win32" ? "node_modules" : "lib/node_modules",
84
+ npmPackageName
85
+ ),
86
+ previous: join(dirname(root), `.covro-previous-${id}`),
87
+ backup,
88
+ dataDir,
89
+ config
90
+ };
91
+ }
92
+ export {
93
+ activateUpdate,
94
+ transactionPaths
95
+ };
96
+ //# sourceMappingURL=transaction.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/updates/transaction.ts"],
4
+ "sourcesContent": ["import { npmPackageName } from '../shared/package.js';\nimport { mkdir, rename, rm, stat } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport type { UpdateRun } from '../shared/updates.js';\nimport { copyDurable, saveJson } from './files.js';\n\nexport interface TransactionPaths {\n root: string;\n prefix: string;\n candidate: string;\n previous: string;\n backup: string;\n dataDir: string;\n config: string;\n}\nexport interface UpdateEffects {\n waitForIdle(): Promise<void>;\n stop(): Promise<void>;\n start(candidate: boolean): Promise<void>;\n verify(): Promise<void>;\n commit(): Promise<void>;\n hasCommitted?(): Promise<boolean>;\n resume?(): Promise<void>;\n phase(phase: UpdateRun['phase'], message: string): Promise<void>;\n}\nconst exists = async (path: string) =>\n stat(path).then(\n () => true,\n (error: NodeJS.ErrnoException) => {\n if (error.code === 'ENOENT') return false;\n throw error;\n },\n );\n\n/** Rollback is confined to admission: no new business writes are accepted before commit. */\nexport async function activateUpdate(paths: TransactionPaths, effects: UpdateEffects) {\n let stopped = false;\n let moved = false;\n let backedUp = false;\n let committed = false;\n try {\n await effects.phase('waiting', '\u5019\u9009\u5305\u5DF2\u51C6\u5907\u597D\uFF0C\u7B49\u5F85\u6240\u6709\u4EFB\u52A1\u3001\u5BA1\u6279\u4E0E\u5F85\u6838\u5BF9\u64CD\u4F5C\u7ED3\u675F');\n await effects.waitForIdle();\n await effects.phase('stopping', '\u5DF2\u5173\u95ED\u65B0\u4EFB\u52A1\u5165\u53E3\uFF0C\u6B63\u5728\u505C\u6B62\u65E7\u670D\u52A1\u5E76\u5907\u4EFD\u6570\u636E');\n // stop may fail after the process has already exited; recovery must restart the old service.\n stopped = true;\n await effects.stop();\n await mkdir(paths.backup, { recursive: true, mode: 0o700 });\n for (const name of ['app.sqlite', 'app.sqlite-wal', 'app.sqlite-shm']) {\n if (await exists(join(paths.dataDir, name)))\n await copyDurable(join(paths.dataDir, name), join(paths.backup, name));\n }\n await copyDurable(paths.config, join(paths.backup, 'config.json'));\n await saveJson(join(paths.backup, 'snapshot.json'), { complete: true });\n backedUp = true;\n await rename(paths.root, paths.previous);\n moved = true;\n await rename(paths.candidate, paths.root);\n await effects.phase('verifying', '\u6B63\u5728\u542F\u52A8\u65B0\u7248\u3001\u8FC1\u79FB\u6570\u636E\u5E93\u5E76\u9A8C\u8BC1\uFF1B\u4E1A\u52A1\u5199\u5165\u4FDD\u6301\u6682\u505C');\n await effects.start(true);\n await effects.verify();\n await effects.commit();\n committed = true;\n // The new gateway persists the commit before admitting requests. Never restore its data later.\n return;\n } catch (error) {\n if (committed || (await effects.hasCommitted?.())) return;\n if (!stopped) {\n await effects.resume?.();\n await effects.phase('failed', error instanceof Error ? error.message : String(error));\n throw error;\n }\n try {\n await effects.stop();\n if (moved) {\n await rm(paths.root, { recursive: true, force: true });\n await rename(paths.previous, paths.root);\n }\n if (backedUp) {\n for (const name of ['app.sqlite', 'app.sqlite-wal', 'app.sqlite-shm']) {\n await rm(join(paths.dataDir, name), { force: true });\n if (await exists(join(paths.backup, name)))\n await copyDurable(join(paths.backup, name), join(paths.dataDir, name));\n }\n await copyDurable(join(paths.backup, 'config.json'), paths.config);\n }\n await effects.phase(\n 'rolledBack',\n `\u65B0\u7248\u542F\u52A8\u5931\u8D25\uFF0C\u5DF2\u6062\u590D\u65E7\u7248\uFF1A${error instanceof Error ? error.message : String(error)}`,\n );\n await effects.start(false);\n } catch (recovery) {\n await effects.phase(\n 'failed',\n `\u5347\u7EA7\u5931\u8D25\u4E14\u6062\u590D\u5C1A\u672A\u5B8C\u6210\uFF1B\u4FDD\u7559\u5907\u4EFD ${paths.backup}\u3002${String(error)}\uFF1B\u6062\u590D\uFF1A${String(recovery)}`,\n );\n throw recovery;\n }\n throw error;\n }\n}\n\nexport function transactionPaths(\n root: string,\n id: string,\n dataDir: string,\n config: string,\n backup: string,\n): TransactionPaths {\n const prefix = join(dirname(root), `.covro-stage-${id}`);\n return {\n root,\n prefix,\n candidate: join(\n prefix,\n process.platform === 'win32' ? 'node_modules' : 'lib/node_modules',\n npmPackageName,\n ),\n previous: join(dirname(root), `.covro-previous-${id}`),\n backup,\n dataDir,\n config,\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }