@whyshu/argent 0.1.4 → 0.1.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.
- package/bin/argent.mjs +44 -5
- package/lib/index.mjs +64 -4
- package/package.json +3 -2
package/bin/argent.mjs
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
*
|
|
10
10
|
* 公共参数:--dsh-home <dir> --profile <name> --repo <dir> --json --dry-run
|
|
11
11
|
*/
|
|
12
|
-
import { spawn } from 'node:child_process';
|
|
13
|
-
import { mkdirSync, writeFileSync, rmSync, existsSync, copyFileSync, chmodSync } from 'node:fs';
|
|
12
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
13
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync, copyFileSync, chmodSync, readFileSync } from 'node:fs';
|
|
14
14
|
import { join, dirname } from 'node:path';
|
|
15
15
|
import { homedir } from 'node:os';
|
|
16
16
|
import {
|
|
17
17
|
DSH_EXACT_VERSION, PROFILE_NAME, PROD_BUNDLES, buildLaunchdPlist, buildProfile, dshHomeOf, runnerScriptPath,
|
|
18
18
|
depsInstalled, health, installDeps, loginWithPhone, pairRunner, probeDshVersion, readAuthToken, repoRoot, runnerConfigPath, sendLoginCode, versionMatches,
|
|
19
|
+
engineRoot, materializeRuntime,
|
|
19
20
|
} from '../lib/index.mjs';
|
|
20
21
|
|
|
21
22
|
const argv = process.argv.slice(2);
|
|
@@ -26,9 +27,9 @@ const DRY_RUN = has('--dry-run');
|
|
|
26
27
|
|
|
27
28
|
const command = (() => {
|
|
28
29
|
const positional = argv.filter((a) => !a.startsWith('--') && !['-h'].includes(a));
|
|
29
|
-
const i = argv.findIndex((a) => a === 'web' || a === 'health' || a === 'setup' || a === 'pair' || a === 'login' || a === 'service');
|
|
30
|
+
const i = argv.findIndex((a) => a === 'web' || a === 'health' || a === 'setup' || a === 'pair' || a === 'login' || a === 'service' || a === 'runtime');
|
|
30
31
|
if (i < 0) return '';
|
|
31
|
-
if (argv[i] === 'service') return
|
|
32
|
+
if (argv[i] === 'service' || argv[i] === 'runtime') return `${argv[i]} ${argv[i + 1] || ''}`.trim();
|
|
32
33
|
return argv[i];
|
|
33
34
|
})();
|
|
34
35
|
|
|
@@ -37,6 +38,7 @@ const opts = {
|
|
|
37
38
|
profile: val('--profile', PROFILE_NAME),
|
|
38
39
|
repo: repoRoot({ repo: val('--repo') }),
|
|
39
40
|
port: Number(val('--port', '3080')) || 3080,
|
|
41
|
+
target: val('--target', join(homedir(), '.argent-dsh')),
|
|
40
42
|
extraBundles: (() => { const raw = val('--bundles', '').split(',').map((x) => x.trim()).filter(Boolean); return raw.flatMap((x) => (x === 'prod' ? PROD_BUNDLES : [x])); })(),
|
|
41
43
|
};
|
|
42
44
|
|
|
@@ -45,6 +47,24 @@ function print(obj) {
|
|
|
45
47
|
else for (const [k, v] of Object.entries(obj)) console.log(`${k}: ${typeof v === 'object' ? JSON.stringify(v) : v}`);
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
function ensureBundleDeps() {
|
|
51
|
+
const dir = join(opts.dshHome, 'profiles', opts.profile);
|
|
52
|
+
let pkg;
|
|
53
|
+
try { pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')); } catch { return; }
|
|
54
|
+
for (const [name, version] of Object.entries(pkg.dependencies || {})) {
|
|
55
|
+
const depDir = join(dir, 'node_modules', ...name.split('/'));
|
|
56
|
+
if (existsSync(depDir)) continue;
|
|
57
|
+
console.error(`→ 补装缺失 bundle ${name}@${version} …`);
|
|
58
|
+
const r = spawnSync('dsh', ['plugin', '--profile', opts.profile, 'add', `${name}@${version}`], {
|
|
59
|
+
cwd: dir, stdio: 'inherit', env: { ...process.env, DSH_HOME: opts.dshHome },
|
|
60
|
+
});
|
|
61
|
+
if (r.status !== 0) {
|
|
62
|
+
console.error(`❌ 补装失败:${name}(exit ${r.status})`);
|
|
63
|
+
process.exit(2);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
function ensureProfile({ install = true } = {}) {
|
|
49
69
|
const built = buildProfile({ dshHome: opts.dshHome, profile: opts.profile, repo: opts.repo, extraBundles: opts.extraBundles });
|
|
50
70
|
if (!built.ok) {
|
|
@@ -61,6 +81,7 @@ function ensureProfile({ install = true } = {}) {
|
|
|
61
81
|
}
|
|
62
82
|
console.error('✅ profile 依赖装配完成');
|
|
63
83
|
}
|
|
84
|
+
if (install && !has('--no-install')) ensureBundleDeps();
|
|
64
85
|
return built;
|
|
65
86
|
}
|
|
66
87
|
|
|
@@ -160,6 +181,23 @@ function setupCommand() {
|
|
|
160
181
|
print({ ok: true, profile: built.dir, bundles: built.bundles.join(', '), plugins: built.plugins.join(', '), preset: built.preset, depsInstalled: depsInstalled(opts.dshHome, opts.profile) });
|
|
161
182
|
}
|
|
162
183
|
|
|
184
|
+
function runtimeCommand(sub) {
|
|
185
|
+
const engine = engineRoot();
|
|
186
|
+
if (!engine) { console.error('❌ 未找到 @whyshu/argent-engine;请用新版启动器(npm 依赖应随包安装)'); process.exit(2); }
|
|
187
|
+
const version = readFileSync(join(engine, 'VERSION'), 'utf8').trim();
|
|
188
|
+
if (sub === 'status' || sub === '') {
|
|
189
|
+
let current = null;
|
|
190
|
+
try { current = JSON.parse(readFileSync(join(opts.target, 'RUNTIME_VERSION.json'), 'utf8')); } catch { current = null; }
|
|
191
|
+
print({ ok: true, engine, engineVersion: version, target: opts.target, current: current ? current.version : null, installed: Boolean(current) });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (sub !== 'install') { console.error('用法:argent runtime install|status [--target <dir>] [--dry-run]'); process.exit(2); }
|
|
195
|
+
if (DRY_RUN) { print({ ok: true, dryRun: true, engine, version, target: opts.target, action: 'materialize runtime files' }); return; }
|
|
196
|
+
const r = materializeRuntime({ target: opts.target, engine });
|
|
197
|
+
if (!r.ok) { console.error(`❌ runtime 安装失败:${r.message}`); process.exit(2); }
|
|
198
|
+
print({ ok: true, package: '@whyshu/argent-engine', version: r.version, files: r.files, target: r.target, backupDir: r.backupDir });
|
|
199
|
+
}
|
|
200
|
+
|
|
163
201
|
function serviceCommand(sub) {
|
|
164
202
|
const bundled = runnerScriptPath({ repo: opts.repo });
|
|
165
203
|
// 固定 runner 路径:复制进 DSH_HOME,避免 launchd 指向 npx 临时缓存(缓存清理后失效)
|
|
@@ -199,13 +237,14 @@ function serviceCommand(sub) {
|
|
|
199
237
|
|
|
200
238
|
const main = async () => {
|
|
201
239
|
if (!command || has('-h') || has('--help')) {
|
|
202
|
-
console.log('用法:argent <web|health|setup|login|pair|service install|service uninstall> [--dsh-home <dir>] [--json] [--dry-run]');
|
|
240
|
+
console.log('用法:argent <web|health|setup|login|pair|runtime install|runtime status|service install|service uninstall> [--dsh-home <dir>] [--json] [--dry-run]');
|
|
203
241
|
process.exit(command ? 0 : 2);
|
|
204
242
|
}
|
|
205
243
|
if (command === 'health') return healthCommand();
|
|
206
244
|
if (command === 'setup') return setupCommand();
|
|
207
245
|
if (command === 'pair') return pairCommand();
|
|
208
246
|
if (command === 'login') return loginCommand();
|
|
247
|
+
if (command === 'runtime install' || command === 'runtime status') return runtimeCommand(command.split(' ')[1]);
|
|
209
248
|
if (command === 'service install' || command === 'service uninstall') return serviceCommand(command.split(' ')[1]);
|
|
210
249
|
return webCommand();
|
|
211
250
|
};
|
package/lib/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* · 状态自检(版本/profile/runner 配置/端口)。
|
|
9
9
|
* ⛔ 不读写任何凭据内容(runner.json 由首跑引导写,权限 600)。
|
|
10
10
|
*/
|
|
11
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from 'node:fs';
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, cpSync, readdirSync } from 'node:fs';
|
|
12
12
|
import { spawnSync } from 'node:child_process';
|
|
13
13
|
import { homedir, hostname as osHostname } from 'node:os';
|
|
14
14
|
import { dirname, join, resolve } from 'node:path';
|
|
@@ -83,6 +83,63 @@ export const PUBLISHED_BUNDLE_VERSIONS = {
|
|
|
83
83
|
'@linxin666/dsh-client-ui-task-board': '0.4.7',
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
|
|
87
|
+
/** 发布模式随启动器安装的 runtime(@whyshu/argent-engine)根目录;本地联调回退 dist。 */
|
|
88
|
+
export function engineRoot({ env = process.env, launcherLib = dirname(fileURLToPath(import.meta.url)) } = {}) {
|
|
89
|
+
const candidates = [];
|
|
90
|
+
if (env.ARGENT_ENGINE_ROOT) candidates.push(resolve(env.ARGENT_ENGINE_ROOT));
|
|
91
|
+
// npx/global 安装:node_modules/@whyshu/argent-engine/engine
|
|
92
|
+
candidates.push(resolve(launcherLib, '..', '..', '..', '@whyshu', 'argent-engine', 'engine'));
|
|
93
|
+
candidates.push(resolve(launcherLib, '..', '..', 'node_modules', '@whyshu', 'argent-engine', 'engine'));
|
|
94
|
+
// 本地仓库联调:dist/argent-engine/engine
|
|
95
|
+
candidates.push(resolve(launcherLib, '..', '..', 'dist', 'argent-engine', 'engine'));
|
|
96
|
+
for (const c of candidates) {
|
|
97
|
+
if (existsSync(join(c, 'scripts')) && existsSync(join(c, 'VERSION'))) return c;
|
|
98
|
+
}
|
|
99
|
+
return '';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function walkFiles(dir, base = dir) {
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const e of readdirSync(dir)) {
|
|
105
|
+
const p = join(dir, e);
|
|
106
|
+
const rel = p.slice(base.length + 1).split('\\').join('/');
|
|
107
|
+
const st = statSync(p);
|
|
108
|
+
if (st.isDirectory()) out.push(...walkFiles(p, base));
|
|
109
|
+
else out.push(rel);
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 把 npm runtime 包物化到客户机既有资产目录(默认 ~/.argent-dsh)。
|
|
116
|
+
* 只覆盖 engine 包内的文件;客户配置/凭据/.git 不动;被覆盖文件先备份。
|
|
117
|
+
*/
|
|
118
|
+
export function materializeRuntime({ target = join(homedir(), '.argent-dsh'), engine = engineRoot(), now = new Date() } = {}) {
|
|
119
|
+
if (!engine) return { ok: false, code: 'engine_missing', message: '未找到 @whyshu/argent-engine(启动器依赖未安装)' };
|
|
120
|
+
if (!existsSync(target)) mkdirSync(target, { recursive: true });
|
|
121
|
+
const version = readFileSync(join(engine, 'VERSION'), 'utf8').trim();
|
|
122
|
+
const stamp = now.toISOString().replace(/[:.]/g, '-');
|
|
123
|
+
const backupDir = join(target, '.runtime-backup', stamp);
|
|
124
|
+
const files = walkFiles(engine);
|
|
125
|
+
let copied = 0;
|
|
126
|
+
for (const rel of files) {
|
|
127
|
+
const src = join(engine, rel);
|
|
128
|
+
const dst = join(target, rel);
|
|
129
|
+
if (existsSync(dst)) {
|
|
130
|
+
const b = join(backupDir, rel);
|
|
131
|
+
mkdirSync(dirname(b), { recursive: true });
|
|
132
|
+
cpSync(dst, b);
|
|
133
|
+
}
|
|
134
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
135
|
+
cpSync(src, dst);
|
|
136
|
+
copied += 1;
|
|
137
|
+
}
|
|
138
|
+
mkdirSync(join(target, '.runtime-backup'), { recursive: true });
|
|
139
|
+
writeFileSync(join(target, 'RUNTIME_VERSION.json'), JSON.stringify({ package: '@whyshu/argent-engine', version, installedAt: now.toISOString(), files: copied, backupDir }, null, 2) + '\n');
|
|
140
|
+
return { ok: true, version, files: copied, target, backupDir };
|
|
141
|
+
}
|
|
142
|
+
|
|
86
143
|
export function profileDir(dshHome, profile = PROFILE_NAME) {
|
|
87
144
|
return join(dshHome, 'profiles', profile);
|
|
88
145
|
}
|
|
@@ -112,13 +169,16 @@ export function buildProfile({ dshHome = dshHomeOf(), profile = PROFILE_NAME, re
|
|
|
112
169
|
dsh: { profile: { bundles } },
|
|
113
170
|
};
|
|
114
171
|
writeFileSync(join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
|
|
115
|
-
const
|
|
172
|
+
const patchLines = [
|
|
116
173
|
'# @whyshu/argent 生成(幂等;手工改动会在此文件重生成时被覆盖)',
|
|
117
174
|
'- id: agent-preset-registry',
|
|
118
175
|
' config:',
|
|
119
176
|
` default: ${DEFAULT_PRESET}`,
|
|
120
|
-
|
|
121
|
-
|
|
177
|
+
];
|
|
178
|
+
if (bundles.includes('@linxin666/dsh-client-ui-task-board')) {
|
|
179
|
+
patchLines.push('- insert:', ' - id: ui-task-board', " name: '@linxin666/dsh-client-ui-task-board'");
|
|
180
|
+
}
|
|
181
|
+
const patch = patchLines.join('\n') + '\n';
|
|
122
182
|
writeFileSync(join(dir, 'cordis.patch.yml'), patch);
|
|
123
183
|
// pnpm 配置必须与现有 web profile 同口径:否则 pnpm 会尝试从 registry 自动装 peer(含未发布版本)⇒ 安装失败
|
|
124
184
|
writeFileSync(join(dir, 'pnpm-workspace.yaml'), 'packages:\n - .\nnodeLinker: hoisted\nautoInstallPeers: false\nallowBuilds:\n protobufjs: true\n');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whyshu/argent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Argent 启动器:一条命令启动「打包好的 dsh + Argent 插件/角色预设」,并管理本机 runner 常驻。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"node": ">=20.0.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@deepseek-ai/dsh": "0.2.1-rc.2"
|
|
17
|
+
"@deepseek-ai/dsh": "0.2.1-rc.2",
|
|
18
|
+
"@whyshu/argent-engine": "0.7.4"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"bin/",
|