@my-life-buddies/cli-windowscompat 0.0.0-stage → 0.29.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,20 @@
1
- # Temporary Holding Version
1
+ # @my-life-buddies/cli-windowscompat
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Dedicated Windows compatibility lane test CLI. **This release supports macOS only (mac-config-only); Windows native Job artifacts and acceptance are still pending.**
4
+
5
+ Platform: https://buddies-windowscompat.devops.sl.sit.xiaohongshu.com
6
+ Developer Web: https://buddies-windowscompat.devops.sl.sit.xiaohongshu.com/developer/
7
+ Command: buddy-cli-windowscompat
8
+ Runtime: @my-life-buddies/buddy-runtime-windowscompat@dev (projects use an exact npm alias at the original Runtime import key).
9
+
10
+ Versions retain x.y.z-dev.N; this independent package uses its own dev tag. The original CLI package and its latest/dev tags are unchanged.
11
+
12
+ Install or manually update on macOS:
13
+
14
+ ```sh
15
+ npm install --global --prefix ~/.local/share/buddy-cli/windowscompat @my-life-buddies/cli-windowscompat@dev --registry=https://registry.npmjs.org/ --@my-life-buddies:registry=https://registry.npmjs.org/ --engine-strict --no-audit --no-fund
16
+ export PATH=~/.local/share/buddy-cli/windowscompat/bin:$PATH
17
+ buddy-cli-windowscompat --version --json
18
+ ```
19
+
20
+ Automatic updates, update and setup are disabled for this lane; use the same independent npm package to update. Do not copy buddyId, .env or credentials from another platform. Overrides only assert the fixed target. Windows npm installation and forced execution are rejected. Private local kits remain a separate, non-publishable distribution.
@@ -0,0 +1,542 @@
1
+ #!/usr/bin/env node
2
+
3
+ // apps/cli/src/bin/buddy.ts
4
+ import { CLI_COMMAND_NAME as CLI_COMMAND_NAME3, CLI_PACKAGE_NAME as CLI_PACKAGE_NAME2 } from "./platform.js";
5
+ import { stderr, stdout } from "node:process";
6
+ import { createRequire } from "node:module";
7
+ import { dirname as dirname3 } from "node:path";
8
+
9
+ // apps/cli/src/platform-support.ts
10
+ import { assertSupportedDeveloperPlatform, UnsupportedDeveloperPlatformError } from "./platform.js";
11
+
12
+ // apps/cli/src/self-update.ts
13
+ import { CLI_COMMAND_NAME, CLI_PACKAGE_NAME } from "./platform.js";
14
+ import { runNpm as executeNpm, LANE_BUILD, CLI_UPDATE_GUIDANCE } from "./platform.js";
15
+ import { readFile as readFile2 } from "node:fs/promises";
16
+ import { homedir as homedir2 } from "node:os";
17
+ import { join as join3 } from "node:path";
18
+ import { npmPackageManifest } from "./platform.js";
19
+
20
+ // apps/cli/src/update-lock.ts
21
+ import { mkdir, rm, writeFile } from "node:fs/promises";
22
+ import { dirname, join } from "node:path";
23
+ import { homedir } from "node:os";
24
+ import { createHash } from "node:crypto";
25
+ import { setTimeout as delay } from "node:timers/promises";
26
+ var defaultUpdateCache = () => join(homedir(), ".cache", "buddy-cli-sit", "updates", `${process.platform}-${process.arch}`);
27
+ var globalUpdateLock = (cache, prefix) => join(cache, "locks", createHash("sha256").update(prefix).digest("hex"));
28
+ async function withUpdateLock(path, signal, action) {
29
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
30
+ const deadline = Date.now() + 2e5;
31
+ for (; ; ) {
32
+ signal?.throwIfAborted();
33
+ try {
34
+ await mkdir(path, { mode: 448 });
35
+ break;
36
+ } catch (cause) {
37
+ if (cause.code !== "EEXIST") throw cause;
38
+ if (Date.now() >= deadline) throw new Error(`\u7B49\u5F85\u5176\u4ED6 CLI \u66F4\u65B0\u8D85\u65F6\u3002\u8BF7\u786E\u8BA4\u66F4\u65B0\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u518D\u6E05\u7406\u9501\u76EE\u5F55\u5E76\u91CD\u8BD5\uFF1A${path}`);
39
+ await delay(100, void 0, { signal });
40
+ }
41
+ }
42
+ try {
43
+ await writeFile(join(path, "owner.json"), JSON.stringify({ pid: process.pid, startedAt: Date.now() }), { mode: 384 });
44
+ signal?.throwIfAborted();
45
+ return await action();
46
+ } finally {
47
+ await rm(path, { recursive: true, force: true });
48
+ }
49
+ }
50
+
51
+ // apps/cli/src/npm-update-install.ts
52
+ import { stripVTControlCharacters } from "node:util";
53
+ function redact(text) {
54
+ return text.replace(/https?:\/\/[^\s<>"']+/giu, (value) => {
55
+ try {
56
+ const url = new URL(value);
57
+ url.username = "";
58
+ url.password = "";
59
+ url.search = "";
60
+ url.hash = "";
61
+ return url.toString();
62
+ } catch {
63
+ return "[URL]";
64
+ }
65
+ }).replace(/\bBearer\s+\S+/giu, "Bearer [REDACTED]").replace(/\b(?:_authToken|_auth|token|password|authorization)\s*[:=]\s*\S+/giu, "[REDACTED]").replace(/\bnpm_[A-Za-z0-9]+/gu, "[REDACTED]");
66
+ }
67
+ function npmFailure(cause) {
68
+ const error = cause;
69
+ const stderr2 = stripVTControlCharacters(typeof error?.stderr === "string" ? error.stderr.slice(-32768) : "");
70
+ const lines = stderr2.split(/\r?\n/u).map((line) => line.match(/^npm (?:error|ERR!)\s+(.*)$/u)?.[1]).filter((line) => !!line);
71
+ const code = lines.map((line) => line.match(/^code\s+(E[A-Z0-9_]+)\b/u)?.[1]).find(Boolean) ?? (typeof error?.code === "string" && /^E[A-Z0-9_]+$/u.test(error.code) ? error.code : error?.killed ? "ETIMEDOUT" : "UNKNOWN");
72
+ const reason = lines.filter((line) => !/^(?:code |A complete log |A complete log can|Log files |In most cases|If you |Note:)/u.test(line) && !/debug[^\r\n]*\.log\s*$/u.test(line)).slice(0, 3).map(redact).join(" ").slice(0, 700);
73
+ return { code, reason };
74
+ }
75
+ async function installCliWithNpm(args, settings, runNpm2) {
76
+ const deadline = Date.now() + settings.timeout;
77
+ for (let attempt = 0; attempt < 2; attempt++) {
78
+ settings.signal?.throwIfAborted();
79
+ try {
80
+ const timeout = deadline - Date.now();
81
+ if (timeout <= 0) throw Object.assign(new Error("install timeout"), { code: "ETIMEDOUT" });
82
+ await runNpm2([...args, "--prefer-online"], { ...settings, timeout });
83
+ return;
84
+ } catch (cause) {
85
+ settings.signal?.throwIfAborted();
86
+ const { code, reason } = npmFailure(cause);
87
+ if (code === "ETARGET" && attempt === 0 && Date.now() < deadline) continue;
88
+ const hint = code === "ETARGET" ? `npm \u5C1A\u672A\u63D0\u4F9B\u6240\u9700\u7248\u672C\uFF1B${attempt > 0 ? "\u5DF2\u8054\u7F51\u5237\u65B0\u5E76\u91CD\u8BD5\u4E00\u6B21" : "\u8054\u7F51\u5237\u65B0\u540E\u4ECD\u627E\u4E0D\u5230\u8BE5\u7248\u672C"}\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002` : code === "EACCES" || code === "EPERM" ? "\u8BF7\u68C0\u67E5 npm \u5B89\u88C5\u76EE\u5F55\u548C\u7F13\u5B58\u76EE\u5F55\u7684\u5199\u5165\u6743\u9650\u3002" : code === "EBADENGINE" ? "\u8BF7\u68C0\u67E5 Node.js \u7248\u672C\u662F\u5426\u6EE1\u8DB3\u5305\u7684\u8981\u6C42\u3002" : ["ENOTFOUND", "ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "EAI_AGAIN"].includes(code) ? "\u8BF7\u68C0\u67E5\u5230 npm \u7684\u7F51\u7EDC\u8FDE\u63A5\u540E\u91CD\u8BD5\u3002" : "\u8BF7\u6839\u636E\u4EE5\u4E0A\u539F\u56E0\u5904\u7406\u540E\u91CD\u8BD5\u3002";
89
+ throw new Error(`npm \u5B89\u88C5\u672A\u5B8C\u6210\uFF08${code}\uFF09\u3002${reason ? `${reason}
90
+ ` : ""}${hint}`);
91
+ }
92
+ }
93
+ }
94
+
95
+ // apps/cli/src/install-location.ts
96
+ import { lstat, readFile, realpath } from "node:fs/promises";
97
+ import { dirname as dirname2, join as join2, resolve } from "node:path";
98
+ async function globalInstallation(packageRoot, packageName, platform = process.platform) {
99
+ try {
100
+ if ((await lstat(packageRoot)).isSymbolicLink()) return void 0;
101
+ const root = await realpath(packageRoot);
102
+ let prefix = root;
103
+ const layout = platform === "win32" ? ["node_modules"] : ["lib", "node_modules"];
104
+ for (const _part of [...layout, ...packageName.split("/")]) prefix = dirname2(prefix);
105
+ if (root !== resolve(prefix, ...layout, packageName)) return void 0;
106
+ if ((await lstat(join2(prefix, ...layout, packageName))).isSymbolicLink()) return void 0;
107
+ if (platform === "win32") {
108
+ const manifest = JSON.parse(await readFile(join2(root, "package.json"), "utf8"));
109
+ const bins = Object.entries(manifest.bin ?? {});
110
+ if (!bins.length || manifest.name !== packageName) return void 0;
111
+ for (const [name, entry] of bins) {
112
+ if (!/^[a-zA-Z0-9_-]+$/u.test(name) || typeof entry !== "string" || entry.includes("..")) return void 0;
113
+ const shim = join2(prefix, `${name}.cmd`);
114
+ if (!(await lstat(shim)).isFile() || (await lstat(shim)).isSymbolicLink()) return void 0;
115
+ const source = (await readFile(shim, "utf8")).replaceAll("\\", "/").toLowerCase();
116
+ if (!source.includes(`node_modules/${packageName}/${entry}`.toLowerCase())) return void 0;
117
+ }
118
+ }
119
+ return { root, prefix };
120
+ } catch {
121
+ return void 0;
122
+ }
123
+ }
124
+
125
+ // apps/cli/src/self-update.ts
126
+ var PACKAGE = CLI_PACKAGE_NAME;
127
+ var REGISTRY = "https://registry.npmjs.org/";
128
+ var MANUAL_UPDATE = process.platform === "win32" ? `npx.cmd --yes --registry=${REGISTRY} --package=${PACKAGE}@dev ${CLI_COMMAND_NAME} setup` : `npm install --global --prefix "$HOME/.local/share/buddy-cli/sit" ${PACKAGE}@dev --registry=${REGISTRY} --prefer-online`;
129
+ var HELP = `\u7528\u6CD5:
130
+ ${CLI_COMMAND_NAME} --version [--json]
131
+ ${CLI_COMMAND_NAME} update --check [--json]
132
+ ${CLI_COMMAND_NAME} update [--json]
133
+
134
+ --version \u67E5\u770B\u5F53\u524D\u8FD0\u884C\u7684 CLI \u7248\u672C\uFF08\u65E0\u9700\u8054\u7F51\uFF09
135
+ --check \u68C0\u67E5\u516C\u5171 npm \u7684 dev \u7248\u672C\uFF0C\u4E0D\u5B89\u88C5
136
+ --json \u8F93\u51FA JSON\uFF0C\u4F9B Coding Agent \u6216\u811A\u672C\u8BFB\u53D6
137
+
138
+ \u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\uFF0C\u4E5F\u65E0\u9700\u8FDB\u5165\u642D\u5B50\u5DE5\u7A0B\u3002
139
+ \u6B63\u5E38\u547D\u4EE4\uFF08\u542B guide\uFF09\u4F1A\u5148\u8054\u7F51\u81EA\u68C0\uFF0C\u81EA\u52A8\u66F4\u65B0\u540E\u7531\u65B0\u7248\u7EE7\u7EED\u6267\u884C\uFF1B\u7B49\u5F85\u5B8C\u6210\u5373\u53EF\u3002
140
+ \u5B89\u88C5\u4E3B\u52A8\u5237\u65B0 npm \u7248\u672C\u7F13\u5B58\uFF0C\u627E\u4E0D\u5230\u7248\u672C\uFF08ETARGET\uFF09\u65F6\u6700\u591A\u91CD\u8BD5\u4E00\u6B21\uFF1B\u5931\u8D25\u8BF4\u660E\u5177\u4F53\u539F\u56E0\u3002
141
+ --help\u3001\u5404\u547D\u4EE4 --help \u4E0E --version \u79BB\u7EBF\u53EF\u7528\uFF1B\u67E5\u8BE2\u6216\u66F4\u65B0\u5931\u8D25\u65F6\u505C\u6B62\u4E1A\u52A1\u64CD\u4F5C\u3002
142
+ update \u66F4\u65B0\u5F53\u524D CLI \u6240\u5728\u7684 npm \u5B89\u88C5\u76EE\u5F55\uFF08\u542B --global --prefix \u81EA\u5B9A\u4E49\u76EE\u5F55\uFF09\uFF0C\u4E0D\u4FEE\u6539\u5176\u4ED6\u5B89\u88C5\u6216\u5DE5\u7A0B Runtime \u4F9D\u8D56\u3002
143
+ \u540C\u4E00\u5305 latest/dev \u9700\u5206\u76EE\u5F55\u5B89\u88C5\u624D\u80FD\u5171\u5B58\uFF1BSIT \u547D\u4EE4\u4E3A ${CLI_COMMAND_NAME}\uFF0C\u6B63\u5F0F\u547D\u4EE4\u4E3A buddy-cli\u3002
144
+ \u5DF2\u7ECF\u662F\u6700\u65B0\u7248\u6216\u5F53\u524D\u7248\u672C\u66F4\u9AD8\u65F6\u4E0D\u5B89\u88C5\u3001\u4E0D\u964D\u7EA7\u3002
145
+ \u6E90\u7801\u3001npm link\u3001\u5C40\u90E8\u5B89\u88C5\u6216 npx \u7684\u6B63\u5E38\u547D\u4EE4\u4F7F\u7528\u7F13\u5B58\u7684\u65B0\u7248\u6267\u884C\uFF0C\u4E0D\u8986\u76D6\u6E90\u7801\u6216\u5DE5\u7A0B\u4F9D\u8D56\u3002
146
+ \u4E0A\u8FF0\u5B89\u88C5\u65B9\u5F0F\u4E0D\u80FD\u7528 update \u5355\u72EC\u4FEE\u6539\u539F\u5B89\u88C5\uFF0C\u8BF7\u6CBF\u7528\u539F\u6765\u7684\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002
147
+ \u65E7\u7248 CLI \u5C1A\u65E0 update \u547D\u4EE4\u65F6\uFF0C\u8FD0\u884C\uFF1A
148
+ ${MANUAL_UPDATE}`;
149
+ function parseVersion(version) {
150
+ const numeric = "(?:0|[1-9][0-9]*)";
151
+ const identifier = `(?:${numeric}|[0-9]*[A-Za-z-][0-9A-Za-z-]*)`;
152
+ const pattern = new RegExp(`^(${numeric})\\.(${numeric})\\.(${numeric})(?:-(${identifier}(?:\\.${identifier})*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$`, "u");
153
+ const match = typeof version === "string" && version.length <= 256 ? pattern.exec(version) : null;
154
+ if (!match || match[0] !== version) throw new Error("\u7248\u672C\u4FE1\u606F\u4E0D\u662F\u6709\u6548\u7684 SemVer \u7248\u672C\u53F7\u3002");
155
+ return [match[1], match[2], match[3], ...match[4]?.split(".") ?? []];
156
+ }
157
+ function compareVersions(left, right) {
158
+ const a = parseVersion(left);
159
+ const b = parseVersion(right);
160
+ for (let i = 0; i < 3; i++) {
161
+ if (BigInt(a[i]) !== BigInt(b[i])) return BigInt(a[i]) > BigInt(b[i]) ? 1 : -1;
162
+ }
163
+ if (a.length === 3 || b.length === 3) return a.length === b.length ? 0 : a.length === 3 ? 1 : -1;
164
+ for (let i = 3; i < Math.max(a.length, b.length); i++) {
165
+ const x = a[i];
166
+ const y = b[i];
167
+ if (x === y) continue;
168
+ if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
169
+ const xn = /^[0-9]+$/u.test(x);
170
+ const yn = /^[0-9]+$/u.test(y);
171
+ if (xn && yn) return BigInt(x) > BigInt(y) ? 1 : -1;
172
+ if (xn !== yn) return xn ? -1 : 1;
173
+ return x > y ? 1 : -1;
174
+ }
175
+ return 0;
176
+ }
177
+ async function latestVersion(options) {
178
+ const timeout = AbortSignal.timeout(1e4);
179
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
180
+ try {
181
+ const metadata = await npmPackageManifest(PACKAGE, "dev", { fetch: options.fetch, signal });
182
+ if (metadata?.name !== PACKAGE) throw new Error("npm \u8FD4\u56DE\u7684\u5305\u540D\u4E0D\u5339\u914D");
183
+ parseVersion(metadata.version);
184
+ if (typeof metadata.version !== "string" || !/^\d+\.\d+\.\d+-dev\.\d+$/.test(metadata.version)) throw new Error("npm dev \u672A\u6307\u5411 x.y.z-dev.N \u7248\u672C\uFF1BSIT CLI \u4E0D\u4F1A\u8DE8\u6E20\u9053\u66F4\u65B0\uFF0C\u8BF7\u8054\u7CFB\u53D1\u5E03\u8005\u4FEE\u6B63 dev \u6807\u7B7E");
185
+ return metadata.version;
186
+ } catch (cause) {
187
+ if (options.signal?.aborted) throw new Error("\u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002");
188
+ if (timeout.aborted) throw new Error("\u68C0\u67E5\u66F4\u65B0\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002");
189
+ throw new Error(`\u65E0\u6CD5\u68C0\u67E5 npm \u66F4\u65B0\uFF1A${cause instanceof Error ? cause.message : String(cause)}\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`);
190
+ }
191
+ }
192
+ async function installUpdate(version, options) {
193
+ if (!/^\d+\.\d+\.\d+-dev\.\d+$/.test(version)) throw new Error("SIT CLI \u53EA\u80FD\u66F4\u65B0 x.y.z-dev.N \u7248\u672C\uFF0C\u4E0D\u4F1A\u8DE8\u6E20\u9053\u5B89\u88C5\u3002");
194
+ const runNpm2 = options.runNpm ?? ((args, settings2) => executeNpm(args, { ...settings2, maxBuffer: 4 * 1024 * 1024 }));
195
+ const settings = { cwd: homedir2(), signal: options.signal, timeout: 1e4 };
196
+ const installation = await globalInstallation(options.packageRoot, PACKAGE);
197
+ if (!installation) {
198
+ throw new Error("\u5F53\u524D CLI \u662F\u6E90\u7801\u3001\u94FE\u63A5\u6216\u9879\u76EE\u5185\u5B89\u88C5\uFF0C\u4E0D\u80FD\u76F4\u63A5\u6539\u5199\u539F\u5B89\u88C5\u3002\u6B63\u5E38\u547D\u4EE4\u4F1A\u4ECE SIT \u72EC\u7ACB\u7F13\u5B58\u8FD0\u884C\u65B0\u7248\uFF1B\u5355\u72EC\u66F4\u65B0\u8BF7\u6CBF\u7528\u539F\u6765\u7684\u5B89\u88C5\u65B9\u5F0F\u3002");
199
+ }
200
+ const { root: installedRoot, prefix } = installation;
201
+ const manualUpdate = process.platform === "win32" ? MANUAL_UPDATE : `npm install --global --prefix '${prefix.replace(/'/gu, "'\\''")}' ${PACKAGE}@dev --registry=${REGISTRY} --prefer-online`;
202
+ await withUpdateLock(globalUpdateLock(options.cacheDirectory ?? defaultUpdateCache(), prefix), options.signal, async () => {
203
+ const current = JSON.parse(await readFile2(join3(installedRoot, "package.json"), "utf8"));
204
+ if (current.name !== PACKAGE || !/^\d+\.\d+\.\d+-dev\.\d+$/.test(current.version)) {
205
+ throw new Error("\u5F53\u524D\u5B89\u88C5\u76EE\u5F55\u5DF2\u88AB\u5176\u4ED6\u6E20\u9053\u66FF\u6362\uFF0C\u5DF2\u505C\u6B62\u66F4\u65B0\u3002\u8BF7\u5C06 latest \u548C dev \u5B89\u88C5\u5230\u4E0D\u540C\u76EE\u5F55\uFF0C\u518D\u91CD\u65B0\u8FD0\u884C " + CLI_COMMAND_NAME + "\u3002");
206
+ }
207
+ if (current.name === PACKAGE && current.version === version) return;
208
+ if (current.name === PACKAGE && compareVersions(current.version, version) > 0) throw new Error("\u53E6\u4E00\u4E2A\u8FDB\u7A0B\u5DF2\u5B89\u88C5\u66F4\u65B0\u7684 CLI\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C\u547D\u4EE4\u3002");
209
+ try {
210
+ await installCliWithNpm([
211
+ "install",
212
+ "--global",
213
+ `${PACKAGE}@${version}`,
214
+ "--prefix",
215
+ prefix,
216
+ `--registry=${REGISTRY}`,
217
+ `--@my-life-buddies:registry=${REGISTRY}`,
218
+ "--engine-strict",
219
+ "--no-audit",
220
+ "--no-fund"
221
+ ], { ...settings, timeout: 18e4 }, runNpm2);
222
+ } catch (cause) {
223
+ if (options.signal?.aborted) throw new Error("\u66F4\u65B0\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u8FD0\u884C " + CLI_COMMAND_NAME + " --version \u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u3002");
224
+ throw new Error(`${cause instanceof Error ? cause.message : "npm \u5B89\u88C5\u672A\u5B8C\u6210\u3002"}
225
+ \u4E5F\u53EF\u624B\u52A8\u5B89\u88C5\uFF1A${manualUpdate}`);
226
+ }
227
+ const installed = JSON.parse(await readFile2(join3(installedRoot, "package.json"), "utf8"));
228
+ if (installed.name !== PACKAGE || installed.version !== version) {
229
+ throw new Error(`npm \u5DF2\u7ED3\u675F\uFF0C\u4F46\u672A\u786E\u8BA4\u5B89\u88C5\u5230 ${version}\u3002\u8BF7\u8FD0\u884C ${CLI_COMMAND_NAME} --version \u68C0\u67E5\uFF0C\u5FC5\u8981\u65F6\u91CD\u8BD5\uFF1A${manualUpdate}`);
230
+ }
231
+ });
232
+ }
233
+ async function runSelfUpdateCommand(args, io, options) {
234
+ const versionOnly = args[0] === "--version" || args[0] === "-v";
235
+ if (!versionOnly && args[0] !== "update") return void 0;
236
+ const json = args.includes("--json");
237
+ const fail = (message, exitCode) => {
238
+ if (json) io.write(JSON.stringify({ package: PACKAGE, currentVersion: options.version, error: message }));
239
+ else io.writeError(`${CLI_COMMAND_NAME}: ${message}`);
240
+ return { exitCode };
241
+ };
242
+ const flags = args.slice(1);
243
+ const allowed = versionOnly ? ["--json", "--help", "-h"] : ["--check", "--json", "--help", "-h"];
244
+ if (flags.some((flag) => !allowed.includes(flag)) || new Set(flags).size !== flags.length) {
245
+ return fail("\u53C2\u6570\u65E0\u6548\uFF0C\u8BF7\u8FD0\u884C " + CLI_COMMAND_NAME + " update --help \u67E5\u770B\u7528\u6CD5\u3002", 2);
246
+ }
247
+ if (flags.includes("--help") || flags.includes("-h")) {
248
+ const help = LANE_BUILD ? `${LANE_BUILD.command} update\uFF1A${CLI_UPDATE_GUIDANCE}` : HELP;
249
+ if (json) io.write(JSON.stringify({ help }));
250
+ else io.write(help);
251
+ return { exitCode: 0 };
252
+ }
253
+ try {
254
+ parseVersion(options.version);
255
+ if (versionOnly) {
256
+ io.write(json ? JSON.stringify({ package: PACKAGE, version: options.version, ...LANE_BUILD ? { lane: LANE_BUILD } : {} }) : options.version);
257
+ return { exitCode: 0 };
258
+ }
259
+ if (LANE_BUILD) throw new Error(CLI_UPDATE_GUIDANCE);
260
+ const pin = process.env.BUDDY_CLI_PIN_VERSION;
261
+ if (pin !== void 0) throw new Error("\u5F53\u524D\u5DF2\u7528 BUDDY_CLI_PIN_VERSION \u9501\u5B9A CLI\uFF1B\u5982\u9700\u66F4\u65B0\uFF0C\u8BF7\u5148\u79FB\u9664 BUDDY_CLI_PIN_VERSION \u73AF\u5883\u53D8\u91CF\u3002");
262
+ const latest = await latestVersion(options);
263
+ const comparison = compareVersions(latest, options.version);
264
+ const check = flags.includes("--check");
265
+ const status = comparison > 0 ? "update_available" : comparison < 0 ? "ahead" : "up_to_date";
266
+ if (!check && comparison > 0) {
267
+ if (!json) io.write(`\u6B63\u5728\u66F4\u65B0 CLI\uFF1A${options.version} \u2192 ${latest}\u2026`);
268
+ await installUpdate(latest, options);
269
+ }
270
+ const updated = !check && comparison > 0;
271
+ if (json) {
272
+ io.write(JSON.stringify({
273
+ package: PACKAGE,
274
+ currentVersion: options.version,
275
+ latestVersion: latest,
276
+ updateAvailable: comparison > 0 && !updated,
277
+ status: updated ? "updated" : status,
278
+ ...updated ? { installedVersion: latest } : {}
279
+ }));
280
+ } else if (updated) io.write(`CLI \u5DF2\u66F4\u65B0\uFF1A${options.version} \u2192 ${latest}\u3002`);
281
+ else if (comparison > 0) io.write(`\u53D1\u73B0\u65B0\u7248\u672C\uFF1A${options.version} \u2192 ${latest}\u3002\u8FD0\u884C ${CLI_COMMAND_NAME} update \u66F4\u65B0\u3002`);
282
+ else if (comparison < 0) io.write(`\u5F53\u524D\u7248\u672C ${options.version} \u9AD8\u4E8E npm dev ${latest}\uFF0C\u65E0\u9700\u66F4\u65B0\u3002`);
283
+ else io.write(`\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\uFF1A${options.version}\u3002`);
284
+ return { exitCode: 0 };
285
+ } catch (cause) {
286
+ return fail(cause instanceof Error ? cause.message : String(cause), 1);
287
+ }
288
+ }
289
+
290
+ // apps/cli/src/auto-update.ts
291
+ import { runNpm as executeNpm2, LANE_BUILD as LANE_BUILD2 } from "./platform.js";
292
+ import { spawn } from "node:child_process";
293
+ import { lstat as lstat2, mkdir as mkdir2, mkdtemp, readFile as readFile3, realpath as realpath2, rename, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
294
+ import { constants } from "node:os";
295
+ import { join as join4 } from "node:path";
296
+ var PACKAGE2 = "@my-life-buddies/cli";
297
+ var REGISTRY2 = "https://registry.npmjs.org/";
298
+ var HANDOFF_ENV = "BUDDY_CLI_SIT_UPDATE_HANDOFF";
299
+ async function installedVersion(root) {
300
+ try {
301
+ const pkg = JSON.parse(await readFile3(join4(root, "package.json"), "utf8"));
302
+ return pkg.name === PACKAGE2 && typeof pkg.version === "string" ? pkg.version : void 0;
303
+ } catch {
304
+ return void 0;
305
+ }
306
+ }
307
+ async function checkedEntry(root, version) {
308
+ if (await installedVersion(root) !== version) throw new Error(`CLI \u5B89\u88C5\u7ED3\u679C\u4E0E\u672C\u6B21\u8981\u6C42\u7684 ${version} \u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u8BD5\u3002`);
309
+ const entry = join4(root, "dist", "bin", "buddy.js");
310
+ if (!(await lstat2(entry)).isFile()) throw new Error("\u65B0\u7248 CLI \u7F3A\u5C11\u6709\u6548\u7684\u6267\u884C\u5165\u53E3\u3002");
311
+ return entry;
312
+ }
313
+ async function prepareLatest(version, options) {
314
+ const cache = options.cacheDirectory ?? defaultUpdateCache();
315
+ const runNpm2 = options.runNpm ?? ((args, settings) => executeNpm2(args, { ...settings, maxBuffer: 4 * 1024 * 1024 }));
316
+ const installation = await globalInstallation(options.packageRoot, PACKAGE2);
317
+ if (installation) {
318
+ await installUpdate(version, options);
319
+ return checkedEntry(installation.root, version);
320
+ }
321
+ const destination = join4(cache, "releases", version);
322
+ return withUpdateLock(join4(cache, "locks", `release-${version}`), options.signal, async () => {
323
+ const root = join4(destination, "node_modules", PACKAGE2);
324
+ try {
325
+ return await checkedEntry(root, version);
326
+ } catch {
327
+ }
328
+ await mkdir2(join4(cache, "releases"), { recursive: true, mode: 448 });
329
+ const temporary = await mkdtemp(join4(cache, "releases", ".install-"));
330
+ try {
331
+ await writeFile2(join4(temporary, "package.json"), JSON.stringify({ private: true }));
332
+ await installCliWithNpm(
333
+ [
334
+ "install",
335
+ "--prefix",
336
+ temporary,
337
+ "--save-exact",
338
+ `${PACKAGE2}@${version}`,
339
+ `--registry=${REGISTRY2}`,
340
+ `--@my-life-buddies:registry=${REGISTRY2}`,
341
+ "--engine-strict",
342
+ "--no-audit",
343
+ "--no-fund"
344
+ ],
345
+ { cwd: temporary, signal: options.signal, timeout: 18e4 },
346
+ runNpm2
347
+ );
348
+ await checkedEntry(join4(temporary, "node_modules", PACKAGE2), version);
349
+ options.signal?.throwIfAborted();
350
+ await rm2(destination, { recursive: true, force: true });
351
+ await rename(temporary, destination);
352
+ return checkedEntry(root, version);
353
+ } finally {
354
+ await rm2(temporary, { recursive: true, force: true });
355
+ }
356
+ });
357
+ }
358
+ async function runUpdatedCli(entry, args, options) {
359
+ options.signal?.throwIfAborted();
360
+ return new Promise((resolve2, reject) => {
361
+ const child = spawn(process.execPath, [entry, ...args], { cwd: options.cwd, env: options.env, stdio: "inherit" });
362
+ let cancellationExitCode;
363
+ let killTimer;
364
+ const abort = () => {
365
+ const reason = options.signal?.reason?.message;
366
+ const signal = reason === "SIGINT" ? "SIGINT" : reason === "SIGHUP" ? "SIGHUP" : "SIGTERM";
367
+ cancellationExitCode ??= 128 + constants.signals[signal];
368
+ child.kill(signal);
369
+ killTimer ??= setTimeout(() => child.kill("SIGKILL"), 15e3);
370
+ killTimer.unref();
371
+ };
372
+ const cleanup = () => {
373
+ options.signal?.removeEventListener("abort", abort);
374
+ clearTimeout(killTimer);
375
+ };
376
+ child.once("error", (error) => {
377
+ cleanup();
378
+ reject(error);
379
+ });
380
+ child.once("exit", (code, signal) => {
381
+ cleanup();
382
+ resolve2({ exitCode: cancellationExitCode ?? code ?? 128 + (signal ? constants.signals[signal] : 1) });
383
+ });
384
+ options.signal?.addEventListener("abort", abort, { once: true });
385
+ if (options.signal?.aborted) abort();
386
+ });
387
+ }
388
+ async function runAutoUpdate(args, io, options) {
389
+ if (LANE_BUILD2) return void 0;
390
+ if (args[0] === "dev" && args[1] === "stop") return void 0;
391
+ if (args[0] === "help" || args[0] === "update" || args[0] === "--version" || args[0] === "-v" || args.includes("--help") || args.includes("-h")) return void 0;
392
+ try {
393
+ const env = { ...options.env ?? process.env };
394
+ const pin = env.BUDDY_CLI_PIN_VERSION;
395
+ if (pin !== void 0) {
396
+ if (!/^\d+\.\d+\.\d+-dev\.\d+$/.test(pin) || pin !== options.version) {
397
+ throw new Error(`CLI \u7248\u672C\u9501\u5B9A\u4E0D\u5339\u914D\uFF1A\u8981\u6C42 ${pin}\uFF0C\u5F53\u524D ${options.version}\u3002\u8BF7\u5B89\u88C5 @my-life-buddies/cli@${pin} \u6216\u79FB\u9664 BUDDY_CLI_PIN_VERSION \u73AF\u5883\u53D8\u91CF\u3002`);
398
+ }
399
+ return void 0;
400
+ }
401
+ const handoff = env[HANDOFF_ENV];
402
+ if (handoff !== void 0) {
403
+ delete env[HANDOFF_ENV];
404
+ if (!options.env) delete process.env[HANDOFF_ENV];
405
+ const expected = JSON.parse(handoff);
406
+ const entry2 = await realpath2(join4(options.packageRoot, "dist", "bin", "buddy.js"));
407
+ if (expected.parent !== process.ppid || expected.version !== options.version || expected.entry !== entry2 || JSON.stringify(expected.args) !== JSON.stringify(args)) throw new Error("CLI \u81EA\u52A8\u66F4\u65B0\u63A5\u7EED\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u3002");
408
+ return void 0;
409
+ }
410
+ const latest = await latestVersion(options);
411
+ if (compareVersions(latest, options.version) <= 0) return void 0;
412
+ io.writeError(`CLI \u81EA\u52A8\u66F4\u65B0\uFF1A${options.version} \u2192 ${latest}\u3002\u5B8C\u6210\u540E\u7EE7\u7EED\u5F53\u524D\u547D\u4EE4\uFF0C\u8BF7\u52FF\u91CD\u590D\u6267\u884C\u3002`);
413
+ const entry = await realpath2(await prepareLatest(latest, options));
414
+ options.signal?.throwIfAborted();
415
+ env[HANDOFF_ENV] = JSON.stringify({ parent: process.pid, version: latest, entry, args });
416
+ return await (options.runCli ?? runUpdatedCli)(entry, args, { cwd: options.cwd ?? process.cwd(), env, signal: options.signal });
417
+ } catch (cause) {
418
+ const error = options.signal?.aborted ? "CLI \u81EA\u52A8\u66F4\u65B0\u5DF2\u53D6\u6D88\uFF0C\u539F\u547D\u4EE4\u672A\u6267\u884C\u3002" : `CLI \u81EA\u68C0\u6216\u66F4\u65B0\u5931\u8D25\uFF0C\u539F\u547D\u4EE4\u672A\u6267\u884C\uFF1A${cause instanceof Error ? cause.message : String(cause)}`;
419
+ if (args.includes("--json")) io.write(JSON.stringify({ code: "CLI_UPDATE_FAILED", error }));
420
+ else io.writeError(error);
421
+ return { exitCode: 1 };
422
+ }
423
+ }
424
+
425
+ // apps/cli/src/setup.ts
426
+ import { CLI_COMMAND_NAME as CLI_COMMAND_NAME2 } from "./platform.js";
427
+ import { readFile as readFile4 } from "node:fs/promises";
428
+ import { join as join5 } from "node:path";
429
+ import { ensurePrivateDirectory, runNpm, windowsOperation, LANE_BUILD as LANE_BUILD3, CLI_UPDATE_GUIDANCE as CLI_UPDATE_GUIDANCE2 } from "./platform.js";
430
+ var PACKAGE3 = "@my-life-buddies/cli";
431
+ var REGISTRY3 = "https://registry.npmjs.org/";
432
+ async function runSetupCommand(args, io, options) {
433
+ if (args[0] !== "setup") return void 0;
434
+ const json = args.includes("--json");
435
+ const fail = (error, exitCode = 1) => {
436
+ if (json) io.write(JSON.stringify({ error }));
437
+ else io.writeError(error);
438
+ return { exitCode };
439
+ };
440
+ if (args.slice(1).some((arg) => !["--help", "-h", "--json"].includes(arg)) || new Set(args).size !== args.length) return fail("\u53C2\u6570\u65E0\u6548\uFF0C\u8BF7\u8FD0\u884C " + CLI_COMMAND_NAME2 + " setup --help \u67E5\u770B\u7528\u6CD5\u3002", 2);
441
+ if (args.includes("--help") || args.includes("-h")) {
442
+ const help = LANE_BUILD3 ? `${LANE_BUILD3.command} setup\uFF1A${CLI_UPDATE_GUIDANCE2}` : "\u7528\u6CD5\uFF1A" + CLI_COMMAND_NAME2 + " setup [--json]\n\u5728 Windows \u5F53\u524D\u7528\u6237\u76EE\u5F55\u5B89\u88C5\u8FD0\u884C\u4E2D\u7684\u51C6\u786E SIT \u7248\u672C\uFF0C\u5E76\u5C06\u72EC\u7ACB\u5B89\u88C5\u76EE\u5F55\u52A0\u5165\u7528\u6237 PATH\u3002\u65E0\u9700\u7BA1\u7406\u5458\u6743\u9650\uFF1B\u5B89\u88C5\u540E\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u3002\u6B63\u5F0F\u7248\u548C SIT \u4F7F\u7528\u4E0D\u540C\u76EE\u5F55\u3002";
443
+ io.write(json ? JSON.stringify({ help }) : help);
444
+ return { exitCode: 0 };
445
+ }
446
+ try {
447
+ if (LANE_BUILD3) throw new Error(CLI_UPDATE_GUIDANCE2);
448
+ if ((options.platform ?? process.platform) !== "win32") throw new Error("setup \u7528\u4E8E Windows \u5B89\u88C5\uFF1BmacOS \u8BF7\u6309 CLI \u4F7F\u7528\u6307\u5357\u5B89\u88C5\u3002");
449
+ if (!/^\d+\.\d+\.\d+-dev\.\d+$/u.test(options.version)) throw new Error("SIT \u5B89\u88C5\u5668\u7248\u672C\u65E0\u6548\uFF0C\u5DF2\u505C\u6B62\u5B89\u88C5\u3002");
450
+ const local = (options.env ?? process.env).LOCALAPPDATA;
451
+ if (!local || !/^[A-Za-z]:[\\/]/u.test(local) || local.startsWith("\\\\") || /[;\u0000-\u001f]/u.test(local)) throw new Error("\u65E0\u6CD5\u5B9A\u4F4D\u5F53\u524D\u7528\u6237\u7684\u672C\u5730\u5E94\u7528\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5 LOCALAPPDATA\u3002");
452
+ const prefix = join5(local, "buddy-cli", "sit");
453
+ await (options.prepareDirectory ?? ensurePrivateDirectory)(prefix);
454
+ await withUpdateLock(globalUpdateLock(options.cacheDirectory ?? defaultUpdateCache(), prefix), options.signal, async () => {
455
+ if (!json) io.write(`\u6B63\u5728\u5B89\u88C5\u642D\u642D SIT CLI ${options.version}\u2026`);
456
+ await installCliWithNpm(
457
+ [
458
+ "install",
459
+ "--global",
460
+ "--prefix",
461
+ prefix,
462
+ `${PACKAGE3}@${options.version}`,
463
+ `--registry=${REGISTRY3}`,
464
+ `--@my-life-buddies:registry=${REGISTRY3}`,
465
+ "--engine-strict",
466
+ "--no-audit",
467
+ "--no-fund"
468
+ ],
469
+ { cwd: prefix, signal: options.signal, timeout: 18e4 },
470
+ options.runNpm ?? runNpm
471
+ );
472
+ const manifest = JSON.parse(await readFile4(join5(prefix, "node_modules", PACKAGE3, "package.json"), "utf8"));
473
+ if (manifest.name !== PACKAGE3 || manifest.version !== options.version || manifest.bin?.["buddy-cli-sit"] !== "dist/bin/buddy.js") throw new Error("\u5B89\u88C5\u7ED3\u679C\u4E0E\u8981\u6C42\u7684\u7248\u672C\u6216\u6E20\u9053\u4E0D\u4E00\u81F4\uFF0C\u5C1A\u672A\u4FEE\u6539 PATH\u3002");
474
+ await readFile4(join5(prefix, "buddy-cli-sit.cmd"), "utf8");
475
+ await (options.configureUserPath ?? (async (path) => {
476
+ await windowsOperation({ operation: "user-path", path });
477
+ }))(prefix);
478
+ });
479
+ const message = "\u5B89\u88C5\u5B8C\u6210\u3002\u8BF7\u5173\u95ED\u5E76\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\uFF0C\u7136\u540E\u8FD0\u884C " + CLI_COMMAND_NAME2 + ".cmd --version\u3002";
480
+ io.write(json ? JSON.stringify({ version: options.version, prefix, command: "buddy-cli-sit.cmd", message }) : message);
481
+ return { exitCode: 0 };
482
+ } catch (cause) {
483
+ return fail(cause instanceof Error ? cause.message : String(cause));
484
+ }
485
+ }
486
+
487
+ // apps/cli/src/termination.ts
488
+ function bindTerminationSignals(controller) {
489
+ const handlers = /* @__PURE__ */ new Map();
490
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
491
+ const handler = () => {
492
+ if (!controller.signal.aborted) controller.abort(new Error(signal));
493
+ };
494
+ handlers.set(signal, handler);
495
+ process.on(signal, handler);
496
+ }
497
+ return () => {
498
+ for (const [signal, handler] of handlers) process.off(signal, handler);
499
+ };
500
+ }
501
+
502
+ // apps/cli/src/bin/buddy.ts
503
+ import { assertBuildManifest, LANE_BUILD as LANE_BUILD4 } from "./platform.js";
504
+ var terminal = {
505
+ write(message) {
506
+ stdout.write(`${message}
507
+ `);
508
+ },
509
+ writeError(message) {
510
+ stderr.write(`${message}
511
+ `);
512
+ }
513
+ };
514
+ var lifecycle = new AbortController();
515
+ var unbindTermination = bindTerminationSignals(lifecycle);
516
+ var onOutputError = (error) => {
517
+ if (error.code === "EPIPE" || error.code === "EIO") lifecycle.abort(new Error("SIGHUP"));
518
+ else throw error;
519
+ };
520
+ stdout.on("error", onOutputError);
521
+ stderr.on("error", onOutputError);
522
+ try {
523
+ assertSupportedDeveloperPlatform();
524
+ const args = process.argv.slice(2);
525
+ const require2 = createRequire(import.meta.url);
526
+ const manifestPath = require2.resolve(`${CLI_PACKAGE_NAME2}/package.json`);
527
+ const manifest = require2(manifestPath);
528
+ assertBuildManifest(manifest);
529
+ if (LANE_BUILD4 && !args.includes("--json")) {
530
+ terminal.writeError(`[${LANE_BUILD4.distribution === "npm" ? "\u6CF3\u9053 npm \u6D4B\u8BD5" : "\u672C\u5730\u6CF3\u9053\u6D4B\u8BD5"} ${LANE_BUILD4.id}] ${LANE_BUILD4.origin} \xB7 ${LANE_BUILD4.validationScope} \xB7 ${LANE_BUILD4.distribution === "npm" ? "\u624B\u52A8\u66F4\u65B0\u72EC\u7ACB npm \u5305" : "\u7981\u6B62\u516C\u5171 npm \u53D1\u5E03/\u66F4\u65B0"}`);
531
+ }
532
+ const options = { version: manifest.version, packageRoot: dirname3(manifestPath), signal: lifecycle.signal };
533
+ const maintenance = await runSetupCommand(args, terminal, options) ?? await runSelfUpdateCommand(args, terminal, options) ?? await runAutoUpdate(args, terminal, options);
534
+ const result = maintenance ?? await (await import("./command-runner.js")).runCommand(args, lifecycle.signal);
535
+ process.exitCode = result.exitCode;
536
+ } catch (cause) {
537
+ terminal.writeError(`${CLI_COMMAND_NAME3}: ${cause instanceof Error ? cause.message : String(cause)}`);
538
+ process.exitCode = 1;
539
+ } finally {
540
+ unbindTermination();
541
+ }
542
+ //# sourceMappingURL=buddy.js.map