@steinscheng/zhaotongkuan 0.8.8 → 0.8.10

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
@@ -17,6 +17,24 @@ npx @steinscheng/zhaotongkuan@latest install \
17
17
 
18
18
  `--stop-after-check` 必须与 `--start` 同时使用;成功结果不会留下本地服务进程。
19
19
 
20
+ 已有安装可直接使用同一个 npm 引导器升级,无需手动准备 ZIP 或 SHA-256:
21
+
22
+ ```bash
23
+ npx @steinscheng/zhaotongkuan@latest update --start --json
24
+ ```
25
+
26
+ `update` 会读取标准安装目录中的 `config/installation.json`,复用原来的业务数据目录和安装根目录,并把经过内置 SHA-256 校验的发行包交给同一事务安装器。若原安装使用了自定义目录,请显式传入 `--data-dir`,必要时同时传入 `--install-root`:
27
+
28
+ ```powershell
29
+ npx @steinscheng/zhaotongkuan@latest update `
30
+ --data-dir "C:\Users\Alice\AppData\Local\找同款" `
31
+ --install-root "C:\Users\Alice\AppData\Local\找同款\installation" `
32
+ --start `
33
+ --json
34
+ ```
35
+
36
+ 更新前应停止正在运行的任务。更新器不会切换数据目录、不会删除激活或浏览器资料;更新失败会返回 JSON 并回滚。`update` 不支持找不到安装记录的全新机器,此时使用 `install`。
37
+
20
38
  私有 GitHub Release 会优先使用已登录的 `gh` CLI 下载;引导器不读取、保存或输出 GitHub Token。也可以安装发行方通过其他可信渠道交付的本地 ZIP:
21
39
 
22
40
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steinscheng/zhaotongkuan",
3
- "version": "0.8.8",
3
+ "version": "0.8.10",
4
4
  "description": "Small, verifiable installer bootstrap for the Zhaotongkuan Codex plugin",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { createReadStream, createWriteStream, existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
3
3
  import { copyFile, lstat, mkdir, realpath } from "node:fs/promises";
4
4
  import { get as httpsGet } from "node:https";
5
- import { tmpdir } from "node:os";
5
+ import { homedir, tmpdir } from "node:os";
6
6
  import { basename, dirname, isAbsolute, join, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { spawnSync } from "node:child_process";
@@ -126,6 +126,43 @@ export function parseArgs(argv) {
126
126
  return result;
127
127
  }
128
128
 
129
+ function defaultDataDir(platform = process.platform, env = process.env) {
130
+ const configured = String(env.ZTK_DATA_DIR || "").trim();
131
+ if (configured) return resolve(configured);
132
+ if (platform === "win32") {
133
+ return resolve(env.LOCALAPPDATA || join(homedir(), "AppData", "Local"), "找同款");
134
+ }
135
+ if (platform === "darwin") return resolve(join(homedir(), "Library", "Application Support", "找同款"));
136
+ return resolve(env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "zhaotongkuan");
137
+ }
138
+
139
+ export function discoverExistingInstall(options, platform = process.platform, env = process.env) {
140
+ const requestedDataDir = options.dataDir ? resolve(options.dataDir) : defaultDataDir(platform, env);
141
+ const installRoot = resolve(options.installRoot || join(requestedDataDir, "installation"));
142
+ const pluginRoot = join(installRoot, "plugins", "zhaotongkuan");
143
+ const configPath = join(pluginRoot, "config", "installation.json");
144
+ if (!existsSync(configPath)) {
145
+ throw new BootstrapError(
146
+ "could not find an existing managed installation; pass --data-dir and --install-root if it uses custom paths",
147
+ "existing_installation_missing",
148
+ );
149
+ }
150
+ let config;
151
+ try {
152
+ config = JSON.parse(readFileSync(configPath, "utf8"));
153
+ } catch {
154
+ throw new BootstrapError("existing installation metadata is invalid", "existing_installation_invalid");
155
+ }
156
+ const recordedDataDir = config && typeof config.data_dir === "string" ? resolve(config.data_dir) : null;
157
+ if (!recordedDataDir) {
158
+ throw new BootstrapError("existing installation metadata has no valid data directory", "existing_installation_invalid");
159
+ }
160
+ if (options.dataDir && requestedDataDir !== recordedDataDir) {
161
+ throw new BootstrapError("--data-dir does not match the existing installation", "data_dir_mismatch");
162
+ }
163
+ return { dataDir: recordedDataDir, installRoot };
164
+ }
165
+
129
166
  export function loadChannel() {
130
167
  const channel = JSON.parse(readFileSync(CHANNEL_PATH, "utf8"));
131
168
  if (channel.schema_version !== 1 || !channel.repository || !channel.version) {
@@ -371,7 +408,7 @@ export function sanitizeForJson(value, key = "", depth = 0) {
371
408
  }
372
409
 
373
410
  function help() {
374
- return `Usage:\n npx @steinscheng/zhaotongkuan@latest install [options]\n\nSupported hosts:\n Windows, macOS, or Linux on x64/arm64\n\nOptions:\n --archive <path-or-https-url> Install an explicit release archive\n --expected-sha256 <digest> Trusted SHA-256 (required with --archive)\n --dry-run Validate without writing\n --start Start and health-check the service\n --stop-after-check Stop after a successful startup health check\n --data-dir <path> Preserve business data outside the program\n --install-root <path> Override managed program directory\n --codex-bin <path> Use an explicit Codex CLI\n --python <path> Use an explicit Python 3.10+ executable\n --json Emit one JSON object\n`;
411
+ return `Usage:\n npx @steinscheng/zhaotongkuan@latest install [options]\n npx @steinscheng/zhaotongkuan@latest update [options]\n\nSupported hosts:\n Windows, macOS, or Linux on x64/arm64\n\nOptions:\n --archive <path-or-https-url> Install an explicit release archive\n --expected-sha256 <digest> Trusted SHA-256 (required with --archive)\n --dry-run Validate without writing\n --start Start and health-check the service\n --stop-after-check Stop after a successful startup health check\n --data-dir <path> Preserve business data outside the program\n --install-root <path> Override managed program directory\n --codex-bin <path> Use an explicit Codex CLI\n --python <path> Use an explicit Python 3.10+ executable\n --json Emit one JSON object\n\nThe update command discovers the existing managed installation and reuses its data directory and install root.\n`;
375
412
  }
376
413
 
377
414
  function emitError(error, jsonMode) {
@@ -403,7 +440,13 @@ export async function main(argv) {
403
440
  process.stdout.write(`${PACKAGE_JSON.version}\n`);
404
441
  return 0;
405
442
  }
406
- if (options.command !== "install") throw new BootstrapError(`unknown command: ${options.command}`, "invalid_argument");
443
+ if (!["install", "update"].includes(options.command)) {
444
+ throw new BootstrapError(`unknown command: ${options.command}`, "invalid_argument");
445
+ }
446
+ if (options.command === "update") {
447
+ const existing = discoverExistingInstall(options);
448
+ options = { ...options, ...existing };
449
+ }
407
450
 
408
451
  const release = resolveRelease(options);
409
452
  const python = findPython(options.python);
@@ -446,6 +489,7 @@ export async function main(argv) {
446
489
  release_version: release.version,
447
490
  target: `${release.target.platform}-${release.target.arch}`,
448
491
  checksum_verified: true,
492
+ operation: options.command,
449
493
  },
450
494
  };
451
495
  process.stdout.write(`${JSON.stringify(output)}\n`);
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "repository": "SchengSteins/zhaotongkuan",
4
- "version": "0.8.8",
4
+ "version": "0.8.10",
5
5
  "artifacts": {
6
6
  "any": {
7
- "url": "https://github.com/SchengSteins/zhaotongkuan/releases/download/v0.8.8/zhaotongkuan-0.8.8.zip",
8
- "sha256": "09b1c9322a4e52c1f6dbc8ab000a1e57828886fe41b052afc2920a166164cf2f"
7
+ "url": "https://github.com/SchengSteins/zhaotongkuan/releases/download/v0.8.10/zhaotongkuan-0.8.10.zip",
8
+ "sha256": "22dc69e85afd4cc475663cc9b45b8bdcb7615d28d15796a0c789205f9856452f"
9
9
  }
10
10
  }
11
11
  }
index ea1cda9..c109526 100644
Binary file