@team-agent/installer 0.3.24 → 0.3.26
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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/emit.rs +1 -0
- package/crates/team-agent/src/cli/mod.rs +193 -7
- package/crates/team-agent/src/cli/send.rs +106 -0
- package/crates/team-agent/src/cli/tests/leader_watch.rs +1 -0
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +270 -13
- package/crates/team-agent/src/cli/tests/status_send.rs +1 -0
- package/crates/team-agent/src/cli/types.rs +7 -0
- package/crates/team-agent/src/coordinator/tests/energy.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/spine.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/takeover.rs +1 -0
- package/crates/team-agent/src/coordinator/tick.rs +55 -7
- package/crates/team-agent/src/leader/lease.rs +57 -0
- package/crates/team-agent/src/leader/start.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch.rs +217 -19
- package/crates/team-agent/src/lifecycle/restart/agent.rs +116 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +66 -1
- package/crates/team-agent/src/lifecycle/restart.rs +19 -2
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +994 -0
- package/crates/team-agent/src/messaging/delivery.rs +91 -1
- package/crates/team-agent/src/messaging/helpers.rs +64 -24
- package/crates/team-agent/src/messaging/tests/runtime.rs +2 -0
- package/crates/team-agent/src/messaging/tests/spine.rs +158 -4
- package/crates/team-agent/src/tmux_backend/tests.rs +414 -2
- package/crates/team-agent/src/tmux_backend.rs +366 -2
- package/crates/team-agent/src/transport/test_support.rs +1 -0
- package/crates/team-agent/src/transport/tests/mod.rs +1 -0
- package/crates/team-agent/src/transport/tests/wire.rs +2 -1
- package/crates/team-agent/src/transport.rs +103 -1
- package/npm/install.mjs +312 -39
- package/package.json +4 -4
package/npm/install.mjs
CHANGED
|
@@ -6,36 +6,108 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(modulePath);
|
|
10
11
|
const packageRoot = path.resolve(__dirname, "..");
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
13
14
|
const DOCTOR_TIMEOUT_MS = 5000;
|
|
14
15
|
const VERSION_SMOKE_TIMEOUT_MS = 5000;
|
|
16
|
+
const INSTALL_MANIFEST = "install-manifest.json";
|
|
17
|
+
const PATH_MARKER = "# team-agent PATH (E48)";
|
|
18
|
+
const WRAPPER_MARKER = "# team-agent installer wrapper";
|
|
19
|
+
const WRAPPER_NAMES = ["team-agent", "team_orchestrator", "team-agent-coordinator"];
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (["-h", "--help", "help"].includes(command)) {
|
|
20
|
-
printHelp();
|
|
21
|
-
process.exit(0);
|
|
21
|
+
if (isCliEntrypoint()) {
|
|
22
|
+
main();
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} else if (command === "uninstall") {
|
|
30
|
-
uninstall(args);
|
|
31
|
-
} else {
|
|
32
|
-
console.error(`unknown command: ${command}`);
|
|
25
|
+
function main() {
|
|
26
|
+
const command = process.argv[2] || "install";
|
|
27
|
+
const args = process.argv.slice(3);
|
|
28
|
+
|
|
29
|
+
if (["-h", "--help", "help"].includes(command)) {
|
|
33
30
|
printHelp();
|
|
34
|
-
process.exit(
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
if (command === "install" || command === "update") {
|
|
36
|
+
install(args);
|
|
37
|
+
} else if (command === "doctor") {
|
|
38
|
+
runDoctor(args);
|
|
39
|
+
} else if (command === "uninstall") {
|
|
40
|
+
uninstall(args);
|
|
41
|
+
} else {
|
|
42
|
+
console.error(`unknown command: ${command}`);
|
|
43
|
+
printHelp();
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isCliEntrypoint() {
|
|
53
|
+
if (!process.argv[1]) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return fs.realpathSync(process.argv[1]) === fs.realpathSync(modulePath);
|
|
58
|
+
} catch {
|
|
59
|
+
return path.resolve(process.argv[1]) === modulePath;
|
|
35
60
|
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function doctorSelfCheckVerdict(doctorBody, spawnMeta = {}) {
|
|
64
|
+
if (spawnMeta.error || spawnMeta.signal) {
|
|
65
|
+
return { kind: "advisory", blockers: [] };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let body;
|
|
69
|
+
try {
|
|
70
|
+
body = JSON.parse(doctorBody || "");
|
|
71
|
+
} catch {
|
|
72
|
+
return { kind: "advisory", blockers: [] };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const blockers = [];
|
|
76
|
+
// Doctor JSON source: crates/team-agent/src/cli/mod.rs:2739-2764.
|
|
77
|
+
if (body?.tmux?.installed === false) {
|
|
78
|
+
blockers.push("tmux not installed");
|
|
79
|
+
}
|
|
80
|
+
if (!body?.mcp?.server_command) {
|
|
81
|
+
blockers.push("MCP server command missing");
|
|
82
|
+
}
|
|
83
|
+
const profileSmokeStatus = body?.profile_smoke?.status;
|
|
84
|
+
const profileSmokeNonBlocking =
|
|
85
|
+
profileSmokeStatus === "legacy_team_invalid" || profileSmokeStatus === "not_required";
|
|
86
|
+
if (
|
|
87
|
+
body?.error === "profile_smoke_failed" ||
|
|
88
|
+
(body?.profile_smoke?.ok === false && !profileSmokeNonBlocking)
|
|
89
|
+
) {
|
|
90
|
+
blockers.push("profile smoke failed");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (blockers.length > 0) {
|
|
94
|
+
return { kind: "blockers", blockers };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const noContext =
|
|
98
|
+
body?.ok === false && body?.error === "workspace has no Team Agent spec or runtime context";
|
|
99
|
+
if (body?.ok === true || noContext) {
|
|
100
|
+
return { kind: "ok", blockers: [] };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { kind: "advisory", blockers: [] };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function doctorSelfCheckLine(verdict) {
|
|
107
|
+
if (verdict.kind === "blockers") {
|
|
108
|
+
return `doctor: found blockers (${verdict.blockers.join("; ")}); run team-agent doctor in your project for details`;
|
|
109
|
+
}
|
|
110
|
+
return "doctor: ok (run team-agent doctor inside a team workspace for a full report)";
|
|
39
111
|
}
|
|
40
112
|
|
|
41
113
|
function printHelp() {
|
|
@@ -47,7 +119,7 @@ Usage:
|
|
|
47
119
|
npx @team-agent/installer@latest uninstall
|
|
48
120
|
|
|
49
121
|
Options:
|
|
50
|
-
--prefix <dir> wrapper
|
|
122
|
+
--prefix <dir> fallback wrapper prefix when no writable PATH dir exists, default ~/.local
|
|
51
123
|
--runtime-dir <dir> stable runtime root, default ~/.team-agent/runtime
|
|
52
124
|
--purge-runtime uninstall also removes the runtime root
|
|
53
125
|
`);
|
|
@@ -55,9 +127,9 @@ Options:
|
|
|
55
127
|
|
|
56
128
|
function install(argv) {
|
|
57
129
|
const opts = parseOptions(argv);
|
|
58
|
-
const prefix = path.resolve(expandHome(opts.prefix || path.join(os.homedir(), ".local")));
|
|
59
|
-
const binDir = path.join(prefix, "bin");
|
|
60
130
|
const runtimeRoot = path.resolve(expandHome(opts.runtimeDir || path.join(os.homedir(), ".team-agent", "runtime")));
|
|
131
|
+
const installTarget = resolveInstallBinDir({ env: process.env, home: os.homedir(), prefix: opts.prefix });
|
|
132
|
+
const binDir = installTarget.binDir;
|
|
61
133
|
const version = packageJson.version || "dev";
|
|
62
134
|
const dest = path.join(runtimeRoot, version);
|
|
63
135
|
const tmp = path.join(runtimeRoot, `.${version}.${process.pid}.tmp`);
|
|
@@ -81,13 +153,29 @@ function install(argv) {
|
|
|
81
153
|
writeExecWrapper(path.join(binDir, "team_orchestrator"), runtimeBinary, ["mcp-server"]);
|
|
82
154
|
writeExecWrapper(path.join(binDir, "team-agent-coordinator"), runtimeBinary, ["coordinator"]);
|
|
83
155
|
installSkills(runtimeBinary);
|
|
156
|
+
writeInstallManifest(runtimeRoot, {
|
|
157
|
+
version,
|
|
158
|
+
binDir,
|
|
159
|
+
runtimeRoot,
|
|
160
|
+
runtimeBinary,
|
|
161
|
+
installedAt: new Date().toISOString(),
|
|
162
|
+
installTargetKind: installTarget.kind,
|
|
163
|
+
});
|
|
84
164
|
|
|
85
165
|
const teamAgent = path.join(binDir, "team-agent");
|
|
86
166
|
console.log(`installed: ${teamAgent}`);
|
|
167
|
+
if (installTarget.readyNow) {
|
|
168
|
+
console.log(`installed to ${binDir} (on PATH, ready now)`);
|
|
169
|
+
} else if (installTarget.rc?.files?.length > 0) {
|
|
170
|
+
console.log(`installed to ${binDir}; added ${binDir} to ${installTarget.rc.files.join(", ")}; restart terminal or open a new shell to use team-agent`);
|
|
171
|
+
} else if (installTarget.rc?.skipped?.length > 0) {
|
|
172
|
+
console.log(`installed to ${binDir}; PATH entry already present in ${installTarget.rc.skipped.join(", ")}; restart terminal or open a new shell to use team-agent`);
|
|
173
|
+
} else {
|
|
174
|
+
console.log(`installed to ${binDir}; add it to PATH to use team-agent by name`);
|
|
175
|
+
}
|
|
87
176
|
console.log(`runtime: ${dest}`);
|
|
88
177
|
console.log(`binary: ${platformBinary.packageName}`);
|
|
89
178
|
console.log("skill: installed for Codex, Claude and Copilot");
|
|
90
|
-
console.log(`PATH: ensure ${binDir} is on PATH`);
|
|
91
179
|
|
|
92
180
|
// 0.3.6 hotfix · C-5 cr verdict — post-install binary smoke 门(走 `--help`
|
|
93
181
|
// 子命令,因为 0.3.x CLI 现阶段没有 --version)。真跑一次 binary 才能抓住
|
|
@@ -116,11 +204,8 @@ function install(argv) {
|
|
|
116
204
|
encoding: "utf8",
|
|
117
205
|
timeout: DOCTOR_TIMEOUT_MS,
|
|
118
206
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} else {
|
|
122
|
-
console.log("doctor: has blockers; run `team-agent doctor` after updating PATH");
|
|
123
|
-
}
|
|
207
|
+
const verdict = doctorSelfCheckVerdict(doctor.stdout, doctor);
|
|
208
|
+
console.log(doctorSelfCheckLine(verdict));
|
|
124
209
|
} finally {
|
|
125
210
|
fs.rmSync(doctorWorkspace, { recursive: true, force: true });
|
|
126
211
|
}
|
|
@@ -128,8 +213,8 @@ function install(argv) {
|
|
|
128
213
|
|
|
129
214
|
function runDoctor(argv) {
|
|
130
215
|
const opts = parseOptions(argv);
|
|
131
|
-
const
|
|
132
|
-
const teamAgent = path.join(
|
|
216
|
+
const runtimeRoot = path.resolve(expandHome(opts.runtimeDir || path.join(os.homedir(), ".team-agent", "runtime")));
|
|
217
|
+
const teamAgent = path.join(installedBinDir(runtimeRoot, opts), "team-agent");
|
|
133
218
|
if (!fs.existsSync(teamAgent)) {
|
|
134
219
|
console.error(`team-agent wrapper not found: ${teamAgent}`);
|
|
135
220
|
process.exit(1);
|
|
@@ -148,10 +233,11 @@ function runDoctor(argv) {
|
|
|
148
233
|
|
|
149
234
|
function uninstall(argv) {
|
|
150
235
|
const opts = parseOptions(argv);
|
|
151
|
-
const
|
|
236
|
+
const runtimeRoot = path.resolve(expandHome(opts.runtimeDir || path.join(os.homedir(), ".team-agent", "runtime")));
|
|
237
|
+
const binDir = installedBinDir(runtimeRoot, opts);
|
|
152
238
|
// 卸载 skill 走二进制单源(同一 SkillTarget 表 codex/claude/copilot),在删 wrapper 前调
|
|
153
239
|
// (删 wrapper 后 PATH 上的 team-agent 没了,但 runtime 二进制仍在;用 runtime 二进制直调)。
|
|
154
|
-
const teamAgentBin = path.join(
|
|
240
|
+
const teamAgentBin = path.join(binDir, "team-agent");
|
|
155
241
|
if (fs.existsSync(teamAgentBin)) {
|
|
156
242
|
const res = spawnSync(teamAgentBin, ["install-skill", "--target", "all", "--uninstall", "--json"], {
|
|
157
243
|
text: true,
|
|
@@ -162,13 +248,12 @@ function uninstall(argv) {
|
|
|
162
248
|
console.error(`WARN: skill uninstall via binary failed (status=${res.status ?? "signal"}); skill dirs may remain under ~/.codex|.claude|.copilot/skills/team-agent`);
|
|
163
249
|
}
|
|
164
250
|
}
|
|
165
|
-
for (const name of
|
|
166
|
-
fs.rmSync(path.join(
|
|
251
|
+
for (const name of WRAPPER_NAMES) {
|
|
252
|
+
fs.rmSync(path.join(binDir, name), { force: true });
|
|
167
253
|
}
|
|
168
|
-
console.log(`removed wrappers from ${
|
|
254
|
+
console.log(`removed wrappers from ${binDir}`);
|
|
169
255
|
console.log("removed skills from ~/.codex, ~/.claude and ~/.copilot skills/team-agent");
|
|
170
256
|
if (opts.purgeRuntime) {
|
|
171
|
-
const runtimeRoot = path.resolve(expandHome(opts.runtimeDir || path.join(os.homedir(), ".team-agent", "runtime")));
|
|
172
257
|
fs.rmSync(runtimeRoot, { recursive: true, force: true });
|
|
173
258
|
console.log(`removed runtime root ${runtimeRoot}`);
|
|
174
259
|
} else {
|
|
@@ -197,6 +282,182 @@ function parseOptions(argv) {
|
|
|
197
282
|
return opts;
|
|
198
283
|
}
|
|
199
284
|
|
|
285
|
+
export function resolveInstallBinDir(options = {}) {
|
|
286
|
+
const env = options.env || process.env;
|
|
287
|
+
const home = options.home || os.homedir();
|
|
288
|
+
const entries = uniquePathEntries(env.PATH || "", home);
|
|
289
|
+
for (const entry of entries) {
|
|
290
|
+
if (isVersionManagedPath(entry) || !canWriteDir(entry) || hasForeignWrapper(entry)) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
return { binDir: entry, kind: "path", readyNow: true, rc: null };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
for (const entry of entries) {
|
|
297
|
+
if (isVersionManagedPath(entry) || !isReasonableUserBinDir(entry, home) || !canWriteDir(entry) || hasForeignWrapper(entry)) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
return { binDir: entry, kind: "path_user", readyNow: true, rc: null };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const fallbackPrefix = options.prefix
|
|
304
|
+
? path.resolve(expandHomeFor(options.prefix, home))
|
|
305
|
+
: path.join(home, ".local");
|
|
306
|
+
const binDir = path.join(fallbackPrefix, "bin");
|
|
307
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
308
|
+
const rc = ensureBinDirOnShellRc(binDir, { env, home });
|
|
309
|
+
return { binDir, kind: "shell_rc", readyNow: false, rc };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function uniquePathEntries(searchPath, home) {
|
|
313
|
+
const seen = new Set();
|
|
314
|
+
const entries = [];
|
|
315
|
+
for (const raw of searchPath.split(path.delimiter)) {
|
|
316
|
+
if (!raw) {
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const resolved = path.resolve(expandHomeFor(raw, home));
|
|
320
|
+
if (seen.has(resolved)) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
seen.add(resolved);
|
|
324
|
+
entries.push(resolved);
|
|
325
|
+
}
|
|
326
|
+
return entries;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function isVersionManagedPath(dir) {
|
|
330
|
+
const value = dir.replace(/\\/g, "/");
|
|
331
|
+
return [
|
|
332
|
+
"/.nvm/versions/",
|
|
333
|
+
"/Cellar/",
|
|
334
|
+
"/volta/tools/image/",
|
|
335
|
+
"/fnm/node-versions/",
|
|
336
|
+
"/.asdf/installs/",
|
|
337
|
+
"/node_modules/.bin",
|
|
338
|
+
"/.npm/_npx/",
|
|
339
|
+
"/_npx/",
|
|
340
|
+
].some((marker) => value.includes(marker));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function isReasonableUserBinDir(dir, home) {
|
|
344
|
+
const relative = path.relative(home, dir);
|
|
345
|
+
return Boolean(relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function canWriteDir(dir) {
|
|
349
|
+
try {
|
|
350
|
+
const probe = path.join(dir, `.team-agent-write-test-${process.pid}-${Date.now()}`);
|
|
351
|
+
fs.writeFileSync(probe, "");
|
|
352
|
+
fs.rmSync(probe, { force: true });
|
|
353
|
+
return true;
|
|
354
|
+
} catch {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function hasForeignWrapper(binDir) {
|
|
360
|
+
return WRAPPER_NAMES.some((name) => {
|
|
361
|
+
const file = path.join(binDir, name);
|
|
362
|
+
return fs.existsSync(file) && !isInstallerManagedWrapper(file);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function isInstallerManagedWrapper(file) {
|
|
367
|
+
try {
|
|
368
|
+
const text = fs.readFileSync(file, "utf8");
|
|
369
|
+
if (text.includes(WRAPPER_MARKER)) {
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
return /^#!\/usr\/bin\/env sh\nexec '[^']+\/bin\/team-agent'(?: '[^']+')? "\$@"\n$/.test(text);
|
|
373
|
+
} catch {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function ensureBinDirOnShellRc(binDir, options = {}) {
|
|
379
|
+
const env = options.env || process.env;
|
|
380
|
+
const home = options.home || os.homedir();
|
|
381
|
+
const shell = path.basename(env.SHELL || "");
|
|
382
|
+
const rc = shellRcTargets(shell, home);
|
|
383
|
+
if (!rc) {
|
|
384
|
+
return { files: [], skipped: [], unsupported: true };
|
|
385
|
+
}
|
|
386
|
+
const block = rc.style === "fish"
|
|
387
|
+
? `\n${PATH_MARKER}\nset -gx PATH ${shellQuote(binDir)} $PATH\n`
|
|
388
|
+
: `\n${PATH_MARKER}\nexport PATH=\"${escapeDoubleQuoted(binDir)}:$PATH\"\n`;
|
|
389
|
+
const files = [];
|
|
390
|
+
const skipped = [];
|
|
391
|
+
for (const file of rc.files) {
|
|
392
|
+
let existing = "";
|
|
393
|
+
try {
|
|
394
|
+
existing = fs.readFileSync(file, "utf8");
|
|
395
|
+
} catch {
|
|
396
|
+
existing = "";
|
|
397
|
+
}
|
|
398
|
+
if (existing.includes(PATH_MARKER)) {
|
|
399
|
+
skipped.push(file);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
404
|
+
fs.appendFileSync(file, block);
|
|
405
|
+
files.push(file);
|
|
406
|
+
} catch {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return { files, skipped, unsupported: false };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function shellRcTargets(shell, home) {
|
|
414
|
+
if (shell === "zsh") {
|
|
415
|
+
return { files: [path.join(home, ".zshrc")], style: "posix" };
|
|
416
|
+
}
|
|
417
|
+
if (shell === "bash") {
|
|
418
|
+
return { files: [path.join(home, ".bashrc"), path.join(home, ".bash_profile")], style: "posix" };
|
|
419
|
+
}
|
|
420
|
+
if (shell === "fish") {
|
|
421
|
+
return { files: [path.join(home, ".config", "fish", "config.fish")], style: "fish" };
|
|
422
|
+
}
|
|
423
|
+
if (!shell || shell === "sh") {
|
|
424
|
+
return {
|
|
425
|
+
files: [process.platform === "darwin" ? path.join(home, ".zshrc") : path.join(home, ".profile")],
|
|
426
|
+
style: "posix",
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function installedBinDir(runtimeRoot, opts) {
|
|
433
|
+
const manifest = readInstallManifest(runtimeRoot);
|
|
434
|
+
if (typeof manifest?.binDir === "string" && manifest.binDir) {
|
|
435
|
+
return manifest.binDir;
|
|
436
|
+
}
|
|
437
|
+
const prefix = path.resolve(expandHome(opts.prefix || path.join(os.homedir(), ".local")));
|
|
438
|
+
return path.join(prefix, "bin");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function installManifestPath(runtimeRoot) {
|
|
442
|
+
return path.join(runtimeRoot, INSTALL_MANIFEST);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function readInstallManifest(runtimeRoot) {
|
|
446
|
+
try {
|
|
447
|
+
return JSON.parse(fs.readFileSync(installManifestPath(runtimeRoot), "utf8"));
|
|
448
|
+
} catch {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function writeInstallManifest(runtimeRoot, manifest) {
|
|
454
|
+
fs.mkdirSync(runtimeRoot, { recursive: true });
|
|
455
|
+
const file = installManifestPath(runtimeRoot);
|
|
456
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
457
|
+
fs.writeFileSync(tmp, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
458
|
+
fs.renameSync(tmp, file);
|
|
459
|
+
}
|
|
460
|
+
|
|
200
461
|
function resolvePlatformBinary() {
|
|
201
462
|
const packageName = platformPackageName();
|
|
202
463
|
if (!packageName) {
|
|
@@ -239,9 +500,13 @@ function copyExecutable(src, dest) {
|
|
|
239
500
|
}
|
|
240
501
|
|
|
241
502
|
function writeExecWrapper(file, binary, fixedArgs) {
|
|
503
|
+
if (fs.existsSync(file) && !isInstallerManagedWrapper(file)) {
|
|
504
|
+
throw new Error(`refusing to overwrite non-Team Agent installer wrapper: ${file}`);
|
|
505
|
+
}
|
|
242
506
|
const args = fixedArgs.map(shellQuote).join(" ");
|
|
243
507
|
const argPrefix = args ? `${args} ` : "";
|
|
244
508
|
const content = `#!/usr/bin/env sh
|
|
509
|
+
${WRAPPER_MARKER}
|
|
245
510
|
exec ${shellQuote(binary)} ${argPrefix}"$@"
|
|
246
511
|
`;
|
|
247
512
|
fs.writeFileSync(file, content, { mode: 0o755 });
|
|
@@ -275,15 +540,23 @@ function makeDoctorWorkspace() {
|
|
|
275
540
|
}
|
|
276
541
|
|
|
277
542
|
function expandHome(value) {
|
|
543
|
+
return expandHomeFor(value, os.homedir());
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function expandHomeFor(value, home) {
|
|
278
547
|
if (value === "~") {
|
|
279
|
-
return
|
|
548
|
+
return home;
|
|
280
549
|
}
|
|
281
550
|
if (value.startsWith("~/")) {
|
|
282
|
-
return path.join(
|
|
551
|
+
return path.join(home, value.slice(2));
|
|
283
552
|
}
|
|
284
553
|
return value;
|
|
285
554
|
}
|
|
286
555
|
|
|
556
|
+
function escapeDoubleQuoted(value) {
|
|
557
|
+
return String(value).replace(/["\\`$]/g, "\\$&");
|
|
558
|
+
}
|
|
559
|
+
|
|
287
560
|
function shellQuote(value) {
|
|
288
561
|
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
289
562
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.26",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.3.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.3.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.3.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.3.26",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.3.26",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.3.26"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|