@xdxer/dingtalk-agent 0.1.5-beta.5 → 0.1.5-beta.6

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.
@@ -14,8 +14,39 @@ export function upgradeAgent(packageRoot, options = {}) {
14
14
  const prefix = options.prefix || join(home, '.local');
15
15
  const binDir = process.platform === 'win32' ? prefix : join(prefix, 'bin');
16
16
  const executable = join(binDir, process.platform === 'win32' ? 'dta.cmd' : 'dta');
17
+ // 阶段追踪:只发事件,不改任何返回值。不传 onEvent 时整段是纯开销为零的空转。
18
+ let step = 0;
19
+ const total = options.dryRun ? 3 : 7;
20
+ // 观察者抛错不得影响升级:begin 抛错会挡住业务,end 抛错会把已经成功的
21
+ // 安装变成失败抛出,可能诱导用户重试一次真实安装。一律隔离并就地禁用。
22
+ let observer = options.onEvent;
23
+ const track = (id, label, run, meta,
24
+ /** 不给时"没抛错就算成功";verify-env 这种要看内容才知道成没成的必须自定 */
25
+ okOf) => {
26
+ step += 1;
27
+ const emit = (phase, ok, note) => {
28
+ if (!observer)
29
+ return;
30
+ try {
31
+ observer({ step, total, id, label, phase, ok, meta: note });
32
+ }
33
+ catch {
34
+ observer = undefined;
35
+ }
36
+ };
37
+ emit('begin');
38
+ try {
39
+ const value = run();
40
+ emit('end', okOf ? okOf(value) : true, meta?.(value));
41
+ return value;
42
+ }
43
+ catch (error) {
44
+ emit('end', false);
45
+ throw error;
46
+ }
47
+ };
17
48
  // 升级前先冻结本机 Skill 版本,否则重装之后无从核对到底动了什么。
18
- const skillsBefore = skillSuiteStatus(packageRoot, { home }).skills;
49
+ const skillsBefore = track('freeze', '冻结当前 Skill 版本', () => skillSuiteStatus(packageRoot, { home }).skills, (skills) => `${skills.length} 个 Skill`);
19
50
  if (options.channel && options.version) {
20
51
  throw new Error('upgrade 的 --channel 与 --version 不能同时使用');
21
52
  }
@@ -25,11 +56,13 @@ export function upgradeAgent(packageRoot, options = {}) {
25
56
  }
26
57
  const requested = cleanVersion(options.version) || channel;
27
58
  const packageSource = env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${requested}`;
28
- const targetVersion = env.DTA_UPGRADE_TARGET_VERSION || resolveTargetVersion(packageSource, env);
29
- if (compareVersions(targetVersion, currentVersion) < 0 && !(options.version && options.force)) {
30
- throw new Error(`升级目标 ${targetVersion} 低于当前版本 ${currentVersion},拒绝自动降级;` +
31
- '如确需降级,请同时使用 --version --force');
32
- }
59
+ const targetVersion = track('resolve', '解析升级目标', () => env.DTA_UPGRADE_TARGET_VERSION || resolveTargetVersion(packageSource, env), (version) => version);
60
+ track('guard', '检查版本关系', () => {
61
+ if (compareVersions(targetVersion, currentVersion) < 0 && !(options.version && options.force)) {
62
+ throw new Error(`升级目标 ${targetVersion} 低于当前版本 ${currentVersion},拒绝自动降级;` +
63
+ '如确需降级,请同时使用 --version 与 --force');
64
+ }
65
+ }, () => `${currentVersion} → ${targetVersion}`);
33
66
  const installArgs = [
34
67
  'install', '--global', '--prefix', prefix, '--registry', REGISTRY, packageSource,
35
68
  ];
@@ -52,47 +85,63 @@ export function upgradeAgent(packageRoot, options = {}) {
52
85
  commands: { install: installCommand, verify: verifyCommand },
53
86
  };
54
87
  }
55
- if (changed) {
88
+ track('install', `安装 ${packageSource}`, () => {
89
+ if (!changed)
90
+ return;
56
91
  const install = spawnSync('npm', installArgs, { encoding: 'utf8', env, timeout: 120_000 });
57
92
  const output = [install.stdout, install.stderr].filter(Boolean).join('\n').trim();
58
93
  if (install.error || install.status !== 0) {
59
94
  throw new Error(`CLI 升级失败: ${install.error?.message || output || `退出码 ${install.status}`}\n` +
60
95
  `可手动执行: ${installCommand}`);
61
96
  }
62
- }
63
- if (!existsSync(executable)) {
64
- throw new Error(`npm 升级完成但找不到新 CLI: ${executable}\n可手动执行: ${installCommand}`);
65
- }
97
+ }, () => changed ? targetVersion : '未变更,跳过');
98
+ track('locate', '定位新 CLI', () => {
99
+ if (!existsSync(executable)) {
100
+ throw new Error(`npm 升级完成但找不到新 CLI: ${executable}\n可手动执行: ${installCommand}`);
101
+ }
102
+ }, () => executable);
66
103
  const effectiveEnv = {
67
104
  ...env,
68
105
  HOME: home,
69
106
  PATH: [binDir, env.PATH || ''].filter(Boolean).join(delimiter),
70
107
  };
71
- const versionRun = spawnSync(executable, ['--version'], {
72
- encoding: 'utf8', env: effectiveEnv, timeout: 8_000,
73
- });
74
- const installedVersion = String(versionRun.stdout || '').trim();
75
- if (versionRun.status !== 0 || installedVersion !== targetVersion) {
76
- throw new Error(`新 CLI 版本校验失败: expected=${targetVersion}, actual=${installedVersion || 'unknown'}\n` +
77
- `可重试: ${installCommand}`);
78
- }
79
- const setupRun = spawnSync(executable, ['setup', '--skip-cli-install', '--json'], {
80
- encoding: 'utf8', env: effectiveEnv, timeout: 180_000,
81
- });
82
- let setup = null;
83
- try {
84
- setup = setupRun.stdout ? JSON.parse(setupRun.stdout) : null;
85
- }
86
- catch { /* reported below */ }
87
- if (!setup) {
88
- throw new Error(`CLI 已升级到 ${installedVersion},但 setup 输出无法解析: ` +
89
- `${String(setupRun.stderr || setupRun.stdout).trim() || `退出码 ${setupRun.status}`}\n` +
90
- `可手动执行: ${verifyCommand}`);
91
- }
108
+ const installedVersion = track('verify-cli', '校验新 CLI 版本', () => {
109
+ const versionRun = spawnSync(executable, ['--version'], {
110
+ encoding: 'utf8', env: effectiveEnv, timeout: 8_000,
111
+ });
112
+ const actual = String(versionRun.stdout || '').trim();
113
+ if (versionRun.status !== 0 || actual !== targetVersion) {
114
+ throw new Error(`新 CLI 版本校验失败: expected=${targetVersion}, actual=${actual || 'unknown'}\n` +
115
+ `可重试: ${installCommand}`);
116
+ }
117
+ return actual;
118
+ }, (version) => version);
119
+ // spawn 必须在 track 内部:这是整条链路最长的一步(180 秒预算),
120
+ // 放到外面等于最该有进度的地方反而静默。
121
+ let setupStatus = 0;
122
+ const setup = track('verify-env', '复核环境', () => {
123
+ const setupRun = spawnSync(executable, ['setup', '--skip-cli-install', '--json'], {
124
+ encoding: 'utf8', env: effectiveEnv, timeout: 180_000,
125
+ });
126
+ setupStatus = setupRun.status ?? 1;
127
+ let parsed = null;
128
+ try {
129
+ parsed = setupRun.stdout ? JSON.parse(setupRun.stdout) : null;
130
+ }
131
+ catch { /* reported below */ }
132
+ if (!parsed) {
133
+ throw new Error(`CLI 已升级到 ${installedVersion},但 setup 输出无法解析: ` +
134
+ `${String(setupRun.stderr || setupRun.stdout).trim() || `退出码 ${setupRun.status}`}\n` +
135
+ `可手动执行: ${verifyCommand}`);
136
+ }
137
+ return parsed;
138
+ }, (parsed) => parsed?.doctor?.ready === true ? '全部 ready' : '部分未 ready',
139
+ // 不能只看"JSON 解析出来了"就打 ✔:那会渲染成自相矛盾的 "✔ 部分未 ready"
140
+ (parsed) => setupStatus === 0 && parsed?.doctor?.ready === true);
92
141
  return {
93
142
  $schema: 'dingtalk-agent/upgrade@1', currentVersion, targetVersion,
94
143
  target: packageSource, prefix, executable, changed, dryRun: false,
95
- ready: setupRun.status === 0 && setup.doctor?.ready === true,
144
+ ready: setupStatus === 0 && setup.doctor?.ready === true,
96
145
  setup,
97
146
  skills: skillsBefore.map((item) => {
98
147
  const after = (setup?.skills || []).find((entry) => entry?.name === item.name);
@@ -108,6 +157,37 @@ export function upgradeAgent(packageRoot, options = {}) {
108
157
  commands: { install: installCommand, verify: verifyCommand },
109
158
  };
110
159
  }
160
+ /**
161
+ * 只解析升级目标,不碰磁盘、不装任何东西。
162
+ *
163
+ * 独立于 upgradeAgent 的 Schema:`upgrade@1` 是 release-readiness 和契约
164
+ * eval 的解析对象,不能为了一个只读命令给它加字段。
165
+ */
166
+ export function checkUpgrade(packageRoot, options = {}) {
167
+ const env = { ...process.env, ...(options.env || {}) };
168
+ const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'));
169
+ const currentVersion = String(pkg.version || '');
170
+ if (options.channel && options.version) {
171
+ throw new Error('upgrade 的 --channel 与 --version 不能同时使用');
172
+ }
173
+ const channel = options.channel || (currentVersion.includes('-') ? 'beta' : 'latest');
174
+ if (!['beta', 'latest'].includes(channel)) {
175
+ throw new Error(`不支持的升级通道: ${channel};只能是 beta 或 latest`);
176
+ }
177
+ const requested = cleanVersion(options.version) || channel;
178
+ const packageSource = env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${requested}`;
179
+ const targetVersion = env.DTA_UPGRADE_TARGET_VERSION || resolveTargetVersion(packageSource, env);
180
+ const order = compareVersions(targetVersion, currentVersion);
181
+ return {
182
+ $schema: 'dingtalk-agent/upgrade-check@1',
183
+ currentVersion, targetVersion, target: packageSource,
184
+ // 用户显式给过 source(本地路径、tarball 等)就原样保留,只有默认的
185
+ // `pkg@tag` 这种可变来源才需要钉死。
186
+ pinnedSource: env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${targetVersion}`,
187
+ channel: channel,
188
+ relation: order > 0 ? 'upgrade' : order < 0 ? 'downgrade' : 'same',
189
+ };
190
+ }
111
191
  function resolveTargetVersion(packageSource, env) {
112
192
  const run = spawnSync('npm', ['view', packageSource, 'version', '--json', '--registry', REGISTRY], {
113
193
  encoding: 'utf8', env, timeout: 30_000,
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/upgrade.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, join } from 'node:path'\nimport { spawnSync } from 'node:child_process'\nimport { cleanVersion, compareVersions } from './version.js'\nimport { skillSuiteStatus } from './skill-manager.js'\n\nconst PACKAGE_NAME = '@xdxer/dingtalk-agent'\nconst REGISTRY = 'https://registry.npmjs.org'\n\nexport interface UpgradeOptions {\n home?: string\n env?: NodeJS.ProcessEnv\n prefix?: string\n channel?: 'beta' | 'latest'\n version?: string\n dryRun?: boolean\n force?: boolean\n}\n\nexport interface UpgradeResult {\n $schema: 'dingtalk-agent/upgrade@1'\n currentVersion: string\n targetVersion: string\n target: string\n prefix: string\n executable: string\n changed: boolean\n dryRun: boolean\n ready: boolean\n setup: Record<string, any> | null\n skills: UpgradeSkillReport[]\n commands: { install: string; verify: string }\n}\n\n/** 升级前后各 Skill 的版本核对;`after` 为 null 表示 dry-run 尚未执行。 */\nexport interface UpgradeSkillReport {\n name: string\n before: string\n after: string | null\n bundled: string\n state: string\n changed: boolean\n}\n\nexport function upgradeAgent(packageRoot: string, options: UpgradeOptions = {}): UpgradeResult {\n const home = options.home || homedir()\n const env = { ...process.env, ...(options.env || {}) }\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))\n const currentVersion = String(pkg.version || '')\n const prefix = options.prefix || join(home, '.local')\n const binDir = process.platform === 'win32' ? prefix : join(prefix, 'bin')\n const executable = join(binDir, process.platform === 'win32' ? 'dta.cmd' : 'dta')\n // 升级前先冻结本机 Skill 版本,否则重装之后无从核对到底动了什么。\n const skillsBefore = skillSuiteStatus(packageRoot, { home }).skills\n\n if (options.channel && options.version) {\n throw new Error('upgrade 的 --channel 与 --version 不能同时使用')\n }\n const channel = options.channel || (currentVersion.includes('-') ? 'beta' : 'latest')\n if (!['beta', 'latest'].includes(channel)) {\n throw new Error(`不支持的升级通道: ${channel};只能是 beta 或 latest`)\n }\n const requested = cleanVersion(options.version) || channel\n const packageSource = env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${requested}`\n const targetVersion = env.DTA_UPGRADE_TARGET_VERSION || resolveTargetVersion(packageSource, env)\n if (compareVersions(targetVersion, currentVersion) < 0 && !(options.version && options.force)) {\n throw new Error(\n `升级目标 ${targetVersion} 低于当前版本 ${currentVersion},拒绝自动降级;` +\n '如确需降级,请同时使用 --version 与 --force',\n )\n }\n const installArgs = [\n 'install', '--global', '--prefix', prefix, '--registry', REGISTRY, packageSource,\n ]\n const installCommand = ['npm', ...installArgs].map(shellQuote).join(' ')\n const verifyCommand = `${shellQuote(executable)} setup --skip-cli-install`\n const changed = options.force || targetVersion !== currentVersion\n\n if (options.dryRun) {\n return {\n $schema: 'dingtalk-agent/upgrade@1', currentVersion, targetVersion,\n target: packageSource, prefix, executable, changed, dryRun: true,\n ready: false, setup: null,\n skills: skillsBefore.map((item) => ({\n name: item.name,\n before: item.canonical.installedVersion,\n after: null,\n bundled: item.bundled.skillVersion,\n state: item.canonical.state,\n changed: item.canonical.state !== 'current',\n })),\n commands: { install: installCommand, verify: verifyCommand },\n }\n }\n\n if (changed) {\n const install = spawnSync('npm', installArgs, { encoding: 'utf8', env, timeout: 120_000 })\n const output = [install.stdout, install.stderr].filter(Boolean).join('\\n').trim()\n if (install.error || install.status !== 0) {\n throw new Error(\n `CLI 升级失败: ${install.error?.message || output || `退出码 ${install.status}`}\\n` +\n `可手动执行: ${installCommand}`,\n )\n }\n }\n if (!existsSync(executable)) {\n throw new Error(`npm 升级完成但找不到新 CLI: ${executable}\\n可手动执行: ${installCommand}`)\n }\n const effectiveEnv = {\n ...env,\n HOME: home,\n PATH: [binDir, env.PATH || ''].filter(Boolean).join(delimiter),\n }\n const versionRun = spawnSync(executable, ['--version'], {\n encoding: 'utf8', env: effectiveEnv, timeout: 8_000,\n })\n const installedVersion = String(versionRun.stdout || '').trim()\n if (versionRun.status !== 0 || installedVersion !== targetVersion) {\n throw new Error(\n `新 CLI 版本校验失败: expected=${targetVersion}, actual=${installedVersion || 'unknown'}\\n` +\n `可重试: ${installCommand}`,\n )\n }\n const setupRun = spawnSync(executable, ['setup', '--skip-cli-install', '--json'], {\n encoding: 'utf8', env: effectiveEnv, timeout: 180_000,\n })\n let setup: Record<string, any> | null = null\n try { setup = setupRun.stdout ? JSON.parse(setupRun.stdout) : null } catch { /* reported below */ }\n if (!setup) {\n throw new Error(\n `CLI 已升级到 ${installedVersion},但 setup 输出无法解析: ` +\n `${String(setupRun.stderr || setupRun.stdout).trim() || `退出码 ${setupRun.status}`}\\n` +\n `可手动执行: ${verifyCommand}`,\n )\n }\n return {\n $schema: 'dingtalk-agent/upgrade@1', currentVersion, targetVersion,\n target: packageSource, prefix, executable, changed, dryRun: false,\n ready: setupRun.status === 0 && setup.doctor?.ready === true,\n setup,\n skills: skillsBefore.map((item) => {\n const after = (setup?.skills || []).find((entry: any) => entry?.name === item.name)\n return {\n name: item.name,\n before: item.canonical.installedVersion,\n after: after?.canonical?.installedVersion ?? null,\n bundled: after?.bundled?.skillVersion ?? item.bundled.skillVersion,\n state: after?.canonical?.state ?? 'unknown',\n changed: (after?.canonical?.installedVersion ?? '') !== item.canonical.installedVersion,\n }\n }),\n commands: { install: installCommand, verify: verifyCommand },\n }\n}\n\nfunction resolveTargetVersion(packageSource: string, env: NodeJS.ProcessEnv): string {\n const run = spawnSync('npm', ['view', packageSource, 'version', '--json', '--registry', REGISTRY], {\n encoding: 'utf8', env, timeout: 30_000,\n })\n const output = String(run.stdout || '').trim()\n if (run.error || run.status !== 0) {\n throw new Error(\n `无法解析升级目标 ${packageSource}: ` +\n `${run.error?.message || String(run.stderr || '').trim() || `退出码 ${run.status}`}`,\n )\n }\n try {\n const value = JSON.parse(output)\n if (typeof value === 'string' && value) return value\n } catch { /* fall through */ }\n if (output) return output.replace(/^['\"]|['\"]$/g, '')\n throw new Error(`npm view 没有返回 ${packageSource} 的版本`)\n}\n\nfunction shellQuote(value: string): string {\n return /^[A-Za-z0-9_./:=@\"$-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\"'\"'`)}'`\n}\n"]}
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,MAAM,YAAY,GAAG,uBAAuB,CAAA;AAC5C,MAAM,QAAQ,GAAG,4BAA4B,CAAA;AA2E7C,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,UAA0B,EAAE;IAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE,CAAA;IACtC,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAA;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/E,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAEjF,6CAA6C;IAC7C,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACpC,0CAA0C;IAC1C,qCAAqC;IACrC,IAAI,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAA;IAC9B,MAAM,KAAK,GAAG,CACZ,EAAsB,EAAE,KAAa,EAAE,GAAY,EACnD,IAA2B;IAC3B,gDAAgD;IAChD,IAA4B,EACzB,EAAE;QACL,IAAI,IAAI,CAAC,CAAA;QACT,MAAM,IAAI,GAAG,CAAC,KAAsB,EAAE,EAAY,EAAE,IAAa,EAAE,EAAE;YACnE,IAAI,CAAC,QAAQ;gBAAE,OAAM;YACrB,IAAI,CAAC;gBAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;YAAC,CAAC;YACnE,MAAM,CAAC;gBAAC,QAAQ,GAAG,SAAS,CAAA;YAAC,CAAC;QAChC,CAAC,CAAA;QACD,IAAI,CAAC,OAAO,CAAC,CAAA;QACb,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,EAAE,CAAA;YACnB,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;YACrD,OAAO,KAAK,CAAA;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YAClB,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC,CAAA;IAED,sCAAsC;IACtC,MAAM,YAAY,GAAG,KAAK,CACxB,QAAQ,EAAE,eAAe,EACzB,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EACpD,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,UAAU,CACvC,CAAA;IAED,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IACrF,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,aAAa,OAAO,oBAAoB,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAA;IAC1D,MAAM,aAAa,GAAG,GAAG,CAAC,0BAA0B,IAAI,GAAG,YAAY,IAAI,SAAS,EAAE,CAAA;IACtF,MAAM,aAAa,GAAG,KAAK,CACzB,SAAS,EAAE,QAAQ,EACnB,GAAG,EAAE,CAAC,GAAG,CAAC,0BAA0B,IAAI,oBAAoB,CAAC,aAAa,EAAE,GAAG,CAAC,EAChF,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CACrB,CAAA;IACD,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE;QAC5B,IAAI,eAAe,CAAC,aAAa,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9F,MAAM,IAAI,KAAK,CACb,QAAQ,aAAa,WAAW,cAAc,UAAU;gBACxD,iCAAiC,CAClC,CAAA;QACH,CAAC;IACH,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,cAAc,MAAM,aAAa,EAAE,CAAC,CAAA;IAChD,MAAM,WAAW,GAAG;QAClB,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa;KACjF,CAAA;IACD,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACxE,MAAM,aAAa,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,2BAA2B,CAAA;IAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,KAAK,cAAc,CAAA;IAEjE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO;YACL,OAAO,EAAE,0BAA0B,EAAE,cAAc,EAAE,aAAa;YAClE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI;YAChE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI;YACzB,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAClC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,gBAAgB;gBACvC,KAAK,EAAE,IAAI;gBACX,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;gBAClC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;gBAC3B,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,SAAS;aAC5C,CAAC,CAAC;YACH,QAAQ,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE;SAC7D,CAAA;IACH,CAAC;IAED,KAAK,CAAC,SAAS,EAAE,MAAM,aAAa,EAAE,EAAE,GAAG,EAAE;QAC3C,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;QAC1F,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAA;QACjF,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,aAAa,OAAO,CAAC,KAAK,EAAE,OAAO,IAAI,MAAM,IAAI,OAAO,OAAO,CAAC,MAAM,EAAE,IAAI;gBAC5E,UAAU,cAAc,EAAE,CAC3B,CAAA;QACH,CAAC;IACH,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC5C,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE;QAC9B,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,YAAY,cAAc,EAAE,CAAC,CAAA;QAC/E,CAAC;IACH,CAAC,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAA;IACpB,MAAM,YAAY,GAAG;QACnB,GAAG,GAAG;QACN,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;KAC/D,CAAA;IACD,MAAM,gBAAgB,GAAG,KAAK,CAAC,YAAY,EAAE,YAAY,EAAE,GAAG,EAAE;QAC9D,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE;YACtD,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK;SACpD,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACrD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CACb,0BAA0B,aAAa,YAAY,MAAM,IAAI,SAAS,IAAI;gBAC1E,QAAQ,cAAc,EAAE,CACzB,CAAA;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IACxB,2CAA2C;IAC3C,sBAAsB;IACtB,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE;QAC7C,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE;YAChF,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO;SACtD,CAAC,CAAA;QACF,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAA;QAClC,IAAI,MAAM,GAA+B,IAAI,CAAA;QAC7C,IAAI,CAAC;YAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;QACpG,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,YAAY,gBAAgB,mBAAmB;gBAC/C,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,OAAO,QAAQ,CAAC,MAAM,EAAE,IAAI;gBACpF,UAAU,aAAa,EAAE,CAC1B,CAAA;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,EACD,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW;IACrE,gDAAgD;IAChD,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,KAAK,CAAC,IAAI,MAAM,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC,CAAA;IAChE,OAAO;QACL,OAAO,EAAE,0BAA0B,EAAE,cAAc,EAAE,aAAa;QAClE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK;QACjE,KAAK,EAAE,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI;QACxD,KAAK;QACL,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YAChC,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAA;YACnF,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,gBAAgB;gBACvC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,gBAAgB,IAAI,IAAI;gBACjD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY;gBAClE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,SAAS;gBAC3C,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,gBAAgB,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,gBAAgB;aACxF,CAAA;QACH,CAAC,CAAC;QACF,QAAQ,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE;KAC7D,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,UAA0B,EAAE;IAC5E,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAA;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/E,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAChD,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IACrF,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,aAAa,OAAO,oBAAoB,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAA;IAC1D,MAAM,aAAa,GAAG,GAAG,CAAC,0BAA0B,IAAI,GAAG,YAAY,IAAI,SAAS,EAAE,CAAA;IACtF,MAAM,aAAa,GAAG,GAAG,CAAC,0BAA0B,IAAI,oBAAoB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAA;IAChG,MAAM,KAAK,GAAG,eAAe,CAAC,aAAa,EAAE,cAAc,CAAC,CAAA;IAC5D,OAAO;QACL,OAAO,EAAE,gCAAgC;QACzC,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa;QACpD,2CAA2C;QAC3C,yBAAyB;QACzB,YAAY,EAAE,GAAG,CAAC,0BAA0B,IAAI,GAAG,YAAY,IAAI,aAAa,EAAE;QAClF,OAAO,EAAE,OAA4B;QACrC,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;KACnE,CAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,aAAqB,EAAE,GAAsB;IACzE,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE;QACjG,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM;KACvC,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9C,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,YAAY,aAAa,IAAI;YAC7B,GAAG,GAAG,CAAC,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,OAAO,GAAG,CAAC,MAAM,EAAE,EAAE,CAClF,CAAA;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAChC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK;YAAE,OAAO,KAAK,CAAA;IACtD,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;IACrD,MAAM,IAAI,KAAK,CAAC,iBAAiB,aAAa,MAAM,CAAC,CAAA;AACvD,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAA;AAC5F,CAAC","sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, join } from 'node:path'\nimport { spawnSync } from 'node:child_process'\nimport { cleanVersion, compareVersions } from './version.js'\nimport { skillSuiteStatus } from './skill-manager.js'\n\nconst PACKAGE_NAME = '@xdxer/dingtalk-agent'\nconst REGISTRY = 'https://registry.npmjs.org'\n\nexport interface UpgradeOptions {\n home?: string\n env?: NodeJS.ProcessEnv\n prefix?: string\n channel?: 'beta' | 'latest'\n version?: string\n dryRun?: boolean\n force?: boolean\n /** 人类通道的进度回调;不传时行为与不带该参数时逐字节相同。--json 分支不得传。 */\n onEvent?: (event: UpgradeEvent) => void\n}\n\n/** 升级过程中的阶段事件。业务逻辑不依赖它,渲染层也不回写它。 */\nexport interface UpgradeEvent {\n /** 第几步,从 1 起 */\n step: number\n /** 总步数:dry-run 3 步,实跑 7 步 */\n total: number\n id: 'freeze' | 'resolve' | 'guard' | 'install' | 'locate' | 'verify-cli' | 'verify-env'\n label: string\n phase: 'begin' | 'end'\n ok?: boolean\n /** 终态附注,例如 '未变更,跳过' */\n meta?: string\n}\n\n/** `dta upgrade --check` 的结果;与 upgrade@1 分开,避免动既有 Schema。 */\nexport interface UpgradeCheckResult {\n $schema: 'dingtalk-agent/upgrade-check@1'\n currentVersion: string\n targetVersion: string\n /** 解析用的来源,可能是可变 tag(`pkg@latest`) */\n target: string\n /**\n * 钉死到精确版本的安装源。**安装必须用它而不是 target**:\n * 从解析到用户按下 y 之间,registry 的 tag 可能已经指向别的版本,\n * 那样会装进 Y 却按 X 校验,机器已经被改了才发现不一致。\n */\n pinnedSource: string\n channel: 'beta' | 'latest'\n /**\n * 目标相对当前版本的关系。**不能只给 outdated 布尔量**:\n * `--version <旧版> --force` 时 outdated=false,界面会显示\"已是最新\",\n * 用户确认之后却真的降级了——在不可逆动作前给了反的信息。\n */\n relation: 'upgrade' | 'same' | 'downgrade'\n}\n\nexport interface UpgradeResult {\n $schema: 'dingtalk-agent/upgrade@1'\n currentVersion: string\n targetVersion: string\n target: string\n prefix: string\n executable: string\n changed: boolean\n dryRun: boolean\n ready: boolean\n setup: Record<string, any> | null\n skills: UpgradeSkillReport[]\n commands: { install: string; verify: string }\n}\n\n/** 升级前后各 Skill 的版本核对;`after` 为 null 表示 dry-run 尚未执行。 */\nexport interface UpgradeSkillReport {\n name: string\n before: string\n after: string | null\n bundled: string\n state: string\n changed: boolean\n}\n\nexport function upgradeAgent(packageRoot: string, options: UpgradeOptions = {}): UpgradeResult {\n const home = options.home || homedir()\n const env = { ...process.env, ...(options.env || {}) }\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))\n const currentVersion = String(pkg.version || '')\n const prefix = options.prefix || join(home, '.local')\n const binDir = process.platform === 'win32' ? prefix : join(prefix, 'bin')\n const executable = join(binDir, process.platform === 'win32' ? 'dta.cmd' : 'dta')\n\n // 阶段追踪:只发事件,不改任何返回值。不传 onEvent 时整段是纯开销为零的空转。\n let step = 0\n const total = options.dryRun ? 3 : 7\n // 观察者抛错不得影响升级:begin 抛错会挡住业务,end 抛错会把已经成功的\n // 安装变成失败抛出,可能诱导用户重试一次真实安装。一律隔离并就地禁用。\n let observer = options.onEvent\n const track = <T>(\n id: UpgradeEvent['id'], label: string, run: () => T,\n meta?: (value: T) => string,\n /** 不给时\"没抛错就算成功\";verify-env 这种要看内容才知道成没成的必须自定 */\n okOf?: (value: T) => boolean,\n ): T => {\n step += 1\n const emit = (phase: 'begin' | 'end', ok?: boolean, note?: string) => {\n if (!observer) return\n try { observer({ step, total, id, label, phase, ok, meta: note }) }\n catch { observer = undefined }\n }\n emit('begin')\n try {\n const value = run()\n emit('end', okOf ? okOf(value) : true, meta?.(value))\n return value\n } catch (error) {\n emit('end', false)\n throw error\n }\n }\n\n // 升级前先冻结本机 Skill 版本,否则重装之后无从核对到底动了什么。\n const skillsBefore = track(\n 'freeze', '冻结当前 Skill 版本',\n () => skillSuiteStatus(packageRoot, { home }).skills,\n (skills) => `${skills.length} 个 Skill`,\n )\n\n if (options.channel && options.version) {\n throw new Error('upgrade 的 --channel 与 --version 不能同时使用')\n }\n const channel = options.channel || (currentVersion.includes('-') ? 'beta' : 'latest')\n if (!['beta', 'latest'].includes(channel)) {\n throw new Error(`不支持的升级通道: ${channel};只能是 beta 或 latest`)\n }\n const requested = cleanVersion(options.version) || channel\n const packageSource = env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${requested}`\n const targetVersion = track(\n 'resolve', '解析升级目标',\n () => env.DTA_UPGRADE_TARGET_VERSION || resolveTargetVersion(packageSource, env),\n (version) => version,\n )\n track('guard', '检查版本关系', () => {\n if (compareVersions(targetVersion, currentVersion) < 0 && !(options.version && options.force)) {\n throw new Error(\n `升级目标 ${targetVersion} 低于当前版本 ${currentVersion},拒绝自动降级;` +\n '如确需降级,请同时使用 --version 与 --force',\n )\n }\n }, () => `${currentVersion} → ${targetVersion}`)\n const installArgs = [\n 'install', '--global', '--prefix', prefix, '--registry', REGISTRY, packageSource,\n ]\n const installCommand = ['npm', ...installArgs].map(shellQuote).join(' ')\n const verifyCommand = `${shellQuote(executable)} setup --skip-cli-install`\n const changed = options.force || targetVersion !== currentVersion\n\n if (options.dryRun) {\n return {\n $schema: 'dingtalk-agent/upgrade@1', currentVersion, targetVersion,\n target: packageSource, prefix, executable, changed, dryRun: true,\n ready: false, setup: null,\n skills: skillsBefore.map((item) => ({\n name: item.name,\n before: item.canonical.installedVersion,\n after: null,\n bundled: item.bundled.skillVersion,\n state: item.canonical.state,\n changed: item.canonical.state !== 'current',\n })),\n commands: { install: installCommand, verify: verifyCommand },\n }\n }\n\n track('install', `安装 ${packageSource}`, () => {\n if (!changed) return\n const install = spawnSync('npm', installArgs, { encoding: 'utf8', env, timeout: 120_000 })\n const output = [install.stdout, install.stderr].filter(Boolean).join('\\n').trim()\n if (install.error || install.status !== 0) {\n throw new Error(\n `CLI 升级失败: ${install.error?.message || output || `退出码 ${install.status}`}\\n` +\n `可手动执行: ${installCommand}`,\n )\n }\n }, () => changed ? targetVersion : '未变更,跳过')\n track('locate', '定位新 CLI', () => {\n if (!existsSync(executable)) {\n throw new Error(`npm 升级完成但找不到新 CLI: ${executable}\\n可手动执行: ${installCommand}`)\n }\n }, () => executable)\n const effectiveEnv = {\n ...env,\n HOME: home,\n PATH: [binDir, env.PATH || ''].filter(Boolean).join(delimiter),\n }\n const installedVersion = track('verify-cli', '校验新 CLI 版本', () => {\n const versionRun = spawnSync(executable, ['--version'], {\n encoding: 'utf8', env: effectiveEnv, timeout: 8_000,\n })\n const actual = String(versionRun.stdout || '').trim()\n if (versionRun.status !== 0 || actual !== targetVersion) {\n throw new Error(\n `新 CLI 版本校验失败: expected=${targetVersion}, actual=${actual || 'unknown'}\\n` +\n `可重试: ${installCommand}`,\n )\n }\n return actual\n }, (version) => version)\n // spawn 必须在 track 内部:这是整条链路最长的一步(180 秒预算),\n // 放到外面等于最该有进度的地方反而静默。\n let setupStatus = 0\n const setup = track('verify-env', '复核环境', () => {\n const setupRun = spawnSync(executable, ['setup', '--skip-cli-install', '--json'], {\n encoding: 'utf8', env: effectiveEnv, timeout: 180_000,\n })\n setupStatus = setupRun.status ?? 1\n let parsed: Record<string, any> | null = null\n try { parsed = setupRun.stdout ? JSON.parse(setupRun.stdout) : null } catch { /* reported below */ }\n if (!parsed) {\n throw new Error(\n `CLI 已升级到 ${installedVersion},但 setup 输出无法解析: ` +\n `${String(setupRun.stderr || setupRun.stdout).trim() || `退出码 ${setupRun.status}`}\\n` +\n `可手动执行: ${verifyCommand}`,\n )\n }\n return parsed\n },\n (parsed) => parsed?.doctor?.ready === true ? '全部 ready' : '部分未 ready',\n // 不能只看\"JSON 解析出来了\"就打 ✔:那会渲染成自相矛盾的 \"✔ 部分未 ready\"\n (parsed) => setupStatus === 0 && parsed?.doctor?.ready === true)\n return {\n $schema: 'dingtalk-agent/upgrade@1', currentVersion, targetVersion,\n target: packageSource, prefix, executable, changed, dryRun: false,\n ready: setupStatus === 0 && setup.doctor?.ready === true,\n setup,\n skills: skillsBefore.map((item) => {\n const after = (setup?.skills || []).find((entry: any) => entry?.name === item.name)\n return {\n name: item.name,\n before: item.canonical.installedVersion,\n after: after?.canonical?.installedVersion ?? null,\n bundled: after?.bundled?.skillVersion ?? item.bundled.skillVersion,\n state: after?.canonical?.state ?? 'unknown',\n changed: (after?.canonical?.installedVersion ?? '') !== item.canonical.installedVersion,\n }\n }),\n commands: { install: installCommand, verify: verifyCommand },\n }\n}\n\n/**\n * 只解析升级目标,不碰磁盘、不装任何东西。\n *\n * 独立于 upgradeAgent 的 Schema:`upgrade@1` 是 release-readiness 和契约\n * eval 的解析对象,不能为了一个只读命令给它加字段。\n */\nexport function checkUpgrade(packageRoot: string, options: UpgradeOptions = {}): UpgradeCheckResult {\n const env = { ...process.env, ...(options.env || {}) }\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))\n const currentVersion = String(pkg.version || '')\n if (options.channel && options.version) {\n throw new Error('upgrade 的 --channel 与 --version 不能同时使用')\n }\n const channel = options.channel || (currentVersion.includes('-') ? 'beta' : 'latest')\n if (!['beta', 'latest'].includes(channel)) {\n throw new Error(`不支持的升级通道: ${channel};只能是 beta 或 latest`)\n }\n const requested = cleanVersion(options.version) || channel\n const packageSource = env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${requested}`\n const targetVersion = env.DTA_UPGRADE_TARGET_VERSION || resolveTargetVersion(packageSource, env)\n const order = compareVersions(targetVersion, currentVersion)\n return {\n $schema: 'dingtalk-agent/upgrade-check@1',\n currentVersion, targetVersion, target: packageSource,\n // 用户显式给过 source(本地路径、tarball 等)就原样保留,只有默认的\n // `pkg@tag` 这种可变来源才需要钉死。\n pinnedSource: env.DTA_UPGRADE_PACKAGE_SOURCE || `${PACKAGE_NAME}@${targetVersion}`,\n channel: channel as 'beta' | 'latest',\n relation: order > 0 ? 'upgrade' : order < 0 ? 'downgrade' : 'same',\n }\n}\n\nfunction resolveTargetVersion(packageSource: string, env: NodeJS.ProcessEnv): string {\n const run = spawnSync('npm', ['view', packageSource, 'version', '--json', '--registry', REGISTRY], {\n encoding: 'utf8', env, timeout: 30_000,\n })\n const output = String(run.stdout || '').trim()\n if (run.error || run.status !== 0) {\n throw new Error(\n `无法解析升级目标 ${packageSource}: ` +\n `${run.error?.message || String(run.stderr || '').trim() || `退出码 ${run.status}`}`,\n )\n }\n try {\n const value = JSON.parse(output)\n if (typeof value === 'string' && value) return value\n } catch { /* fall through */ }\n if (output) return output.replace(/^['\"]|['\"]$/g, '')\n throw new Error(`npm view 没有返回 ${packageSource} 的版本`)\n}\n\nfunction shellQuote(value: string): string {\n return /^[A-Za-z0-9_./:=@\"$-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\"'\"'`)}'`\n}\n"]}
package/dist/src/waits.js CHANGED
@@ -7,6 +7,7 @@ import { dirname, resolve, join } from 'node:path';
7
7
  import { hostname } from 'node:os';
8
8
  import { randomUUID } from 'node:crypto';
9
9
  import { digest, stableStringify } from './events.js';
10
+ import { err } from './tui.js';
10
11
  const OPEN_CHAIN = new Set([
11
12
  'arming', 'active', 'uncertain', 'claimed', 'cancel-pending',
12
13
  ]);
@@ -841,7 +842,7 @@ function mirror(record) {
841
842
  persist(join(dir, `${record.id}.json`), record);
842
843
  }
843
844
  catch (e) {
844
- console.error(` ⚠️ Wait 审计镜像写入失败(waitRoot 权威真值已保留): ${e.message}`);
845
+ console.error(err().theme.warn(` ⚠️ Wait 审计镜像写入失败(waitRoot 权威真值已保留): ${e.message}`));
845
846
  }
846
847
  }
847
848
  function readJson(path) {