@team-agent/installer 0.3.25 → 0.3.27
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/leader/lease.rs +57 -0
- package/crates/team-agent/src/leader/start.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch.rs +9 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +55 -0
- package/crates/team-agent/src/messaging/delivery.rs +83 -3
- package/crates/team-agent/src/messaging/leader_receiver.rs +61 -24
- package/crates/team-agent/src/messaging/tests/runtime.rs +2 -0
- package/crates/team-agent/src/messaging/tests/spine.rs +1 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +227 -25
- package/crates/team-agent/src/tmux_backend.rs +409 -105
- 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 +77 -0
- package/npm/install.mjs +222 -14
- package/package.json +4 -4
|
@@ -337,6 +337,75 @@ pub struct InjectReport {
|
|
|
337
337
|
/// Gap42:仅 metadata,`not_yet_observed` 也算成功。
|
|
338
338
|
pub turn_verification: TurnVerification,
|
|
339
339
|
pub attempts: u32,
|
|
340
|
+
/// E50 PR-1 (0.3.24 P0, pasted-prompt 假阴诊断): per-attempt observations
|
|
341
|
+
/// emitted by the paste-prompt + Enter loop in `tmux_backend.rs::inject`.
|
|
342
|
+
/// `None` when the inject path did not exercise the diagnostic
|
|
343
|
+
/// instrumentation (peer payloads, empty payloads, non-bracketed Text,
|
|
344
|
+
/// or any path that bypasses `capture_has_pasted_content_prompt`). When
|
|
345
|
+
/// present, downstream `send.unverified` / `send.failed` events surface
|
|
346
|
+
/// it as `submit_attempts_detail[]` so operators see live pane state
|
|
347
|
+
/// per attempt (matched literal, where-in-tail offset, scrubbed pane
|
|
348
|
+
/// excerpt, elapsed ms) — the missing forensic data the user has been
|
|
349
|
+
/// asking for for many rounds.
|
|
350
|
+
///
|
|
351
|
+
/// Tests / mocks should default this to `None` via `Default`. Wire
|
|
352
|
+
/// byte-lock (`transport::tests::wire`) is untouched: this field is NOT
|
|
353
|
+
/// serialised on the typed wire — it lives only in the in-process
|
|
354
|
+
/// `InjectReport` and is rendered into JSON manually by the delivery
|
|
355
|
+
/// layer when emitting forensic events.
|
|
356
|
+
pub submit_diagnostics: Option<SubmitDiagnostics>,
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/// E50 PR-1 diagnostic payload — one entry per submit attempt in the
|
|
360
|
+
/// pasted-prompt branch + (informational) for the appear-gate poll.
|
|
361
|
+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
362
|
+
pub struct SubmitDiagnostics {
|
|
363
|
+
/// Time spent in the appear-gate (poll for the pasted-content placeholder
|
|
364
|
+
/// before Enter). When `saw_pasted_prompt == false` this is the time we
|
|
365
|
+
/// spent polling before falling through to the E46 token path.
|
|
366
|
+
pub appear_gate_elapsed_ms: u64,
|
|
367
|
+
/// Did the appear-gate ever match the `pasted content` / `pasted text`
|
|
368
|
+
/// literal? When `false`, the inject took the E46 token path; the
|
|
369
|
+
/// `attempts_detail` may still capture observations the operator wants.
|
|
370
|
+
pub appear_gate_matched: bool,
|
|
371
|
+
/// Total elapsed across the whole submit gate (appear-gate + Enter
|
|
372
|
+
/// loop). Useful to triage real-machine slow-paste (codex large-paste
|
|
373
|
+
/// collapse can take >100 s while the framework's loop is sub-second).
|
|
374
|
+
pub total_elapsed_ms: u64,
|
|
375
|
+
/// Per-attempt observations of the post-Enter capture in the pasted-
|
|
376
|
+
/// prompt loop. Empty when `saw_pasted_prompt == false` and the inject
|
|
377
|
+
/// went through the E46 token path.
|
|
378
|
+
pub attempts_detail: Vec<SubmitAttemptObservation>,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/// E50 PR-1 single attempt observation (one Enter + one capture). All fields
|
|
382
|
+
/// are forensic — they exist to make `send.unverified` / `send.failed` events
|
|
383
|
+
/// self-describing rather than requiring the operator to guess.
|
|
384
|
+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
385
|
+
pub struct SubmitAttemptObservation {
|
|
386
|
+
/// 1-based attempt index (mirrors the `attempts` counter that already
|
|
387
|
+
/// ships in `InjectReport.attempts`).
|
|
388
|
+
pub attempt_index: u32,
|
|
389
|
+
/// Did this attempt's post-Enter capture STILL match the pasted-prompt
|
|
390
|
+
/// literal? `true` means the inject saw the placeholder; `false` means
|
|
391
|
+
/// the placeholder cleared (= submit succeeded by the legacy criterion).
|
|
392
|
+
pub matched: bool,
|
|
393
|
+
/// The matched literal substring (`pasted content` / `pasted text`)
|
|
394
|
+
/// when `matched == true`. `None` otherwise.
|
|
395
|
+
pub matched_literal: Option<String>,
|
|
396
|
+
/// Distance from the bottom of the tail where the match occurred:
|
|
397
|
+
/// `Some(0)` = bottom-most line (composer), `Some(N)` = N lines above
|
|
398
|
+
/// bottom (likely scrollback), `None` = no match. Critical for the
|
|
399
|
+
/// false-negative root cause: a `pasted content` literal in scrollback
|
|
400
|
+
/// is NOT the live composer placeholder.
|
|
401
|
+
pub where_in_tail: Option<u32>,
|
|
402
|
+
/// Scrubbed last 20 / 80 lines of the captured tail, ANSI-stripped,
|
|
403
|
+
/// capped ~1200 bytes. Secrets scrubbed (sk-/ghp_/AKIA/Bearer/hex32+).
|
|
404
|
+
pub pane_tail_excerpt: String,
|
|
405
|
+
/// Number of non-empty lines in `pane_tail_excerpt`.
|
|
406
|
+
pub pane_tail_lines: u32,
|
|
407
|
+
/// Time elapsed for THIS attempt (Enter + capture + match).
|
|
408
|
+
pub elapsed_ms: u64,
|
|
340
409
|
}
|
|
341
410
|
|
|
342
411
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -761,9 +830,17 @@ pub fn tmux_spawn_argv(
|
|
|
761
830
|
command.to_string(),
|
|
762
831
|
]
|
|
763
832
|
} else {
|
|
833
|
+
// E53 (0.3.26, adaptive layout same-session tabs): `-d` makes the new
|
|
834
|
+
// window start without switching the client's active window to it. The
|
|
835
|
+
// leader stays on its own window; the worker opens as a background tab.
|
|
836
|
+
// Without `-d` every `new-window` call yanks the leader's terminal to
|
|
837
|
+
// the freshly spawned worker window, disrupting whatever the leader is
|
|
838
|
+
// doing. `-d` matches the Python golden: the managed leader and all
|
|
839
|
+
// workers share the same tmux session (= same terminal window tabs).
|
|
764
840
|
vec![
|
|
765
841
|
"tmux".to_string(),
|
|
766
842
|
"new-window".to_string(),
|
|
843
|
+
"-d".to_string(),
|
|
767
844
|
"-t".to_string(),
|
|
768
845
|
session.as_str().to_string(),
|
|
769
846
|
"-n".to_string(),
|
package/npm/install.mjs
CHANGED
|
@@ -13,6 +13,10 @@ const require = createRequire(import.meta.url);
|
|
|
13
13
|
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
14
14
|
const DOCTOR_TIMEOUT_MS = 5000;
|
|
15
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"];
|
|
16
20
|
|
|
17
21
|
if (isCliEntrypoint()) {
|
|
18
22
|
main();
|
|
@@ -115,7 +119,7 @@ Usage:
|
|
|
115
119
|
npx @team-agent/installer@latest uninstall
|
|
116
120
|
|
|
117
121
|
Options:
|
|
118
|
-
--prefix <dir> wrapper
|
|
122
|
+
--prefix <dir> fallback wrapper prefix when no writable PATH dir exists, default ~/.local
|
|
119
123
|
--runtime-dir <dir> stable runtime root, default ~/.team-agent/runtime
|
|
120
124
|
--purge-runtime uninstall also removes the runtime root
|
|
121
125
|
`);
|
|
@@ -123,9 +127,9 @@ Options:
|
|
|
123
127
|
|
|
124
128
|
function install(argv) {
|
|
125
129
|
const opts = parseOptions(argv);
|
|
126
|
-
const prefix = path.resolve(expandHome(opts.prefix || path.join(os.homedir(), ".local")));
|
|
127
|
-
const binDir = path.join(prefix, "bin");
|
|
128
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;
|
|
129
133
|
const version = packageJson.version || "dev";
|
|
130
134
|
const dest = path.join(runtimeRoot, version);
|
|
131
135
|
const tmp = path.join(runtimeRoot, `.${version}.${process.pid}.tmp`);
|
|
@@ -149,13 +153,29 @@ function install(argv) {
|
|
|
149
153
|
writeExecWrapper(path.join(binDir, "team_orchestrator"), runtimeBinary, ["mcp-server"]);
|
|
150
154
|
writeExecWrapper(path.join(binDir, "team-agent-coordinator"), runtimeBinary, ["coordinator"]);
|
|
151
155
|
installSkills(runtimeBinary);
|
|
156
|
+
writeInstallManifest(runtimeRoot, {
|
|
157
|
+
version,
|
|
158
|
+
binDir,
|
|
159
|
+
runtimeRoot,
|
|
160
|
+
runtimeBinary,
|
|
161
|
+
installedAt: new Date().toISOString(),
|
|
162
|
+
installTargetKind: installTarget.kind,
|
|
163
|
+
});
|
|
152
164
|
|
|
153
165
|
const teamAgent = path.join(binDir, "team-agent");
|
|
154
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
|
+
}
|
|
155
176
|
console.log(`runtime: ${dest}`);
|
|
156
177
|
console.log(`binary: ${platformBinary.packageName}`);
|
|
157
178
|
console.log("skill: installed for Codex, Claude and Copilot");
|
|
158
|
-
console.log(`PATH: ensure ${binDir} is on PATH`);
|
|
159
179
|
|
|
160
180
|
// 0.3.6 hotfix · C-5 cr verdict — post-install binary smoke 门(走 `--help`
|
|
161
181
|
// 子命令,因为 0.3.x CLI 现阶段没有 --version)。真跑一次 binary 才能抓住
|
|
@@ -193,8 +213,8 @@ function install(argv) {
|
|
|
193
213
|
|
|
194
214
|
function runDoctor(argv) {
|
|
195
215
|
const opts = parseOptions(argv);
|
|
196
|
-
const
|
|
197
|
-
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");
|
|
198
218
|
if (!fs.existsSync(teamAgent)) {
|
|
199
219
|
console.error(`team-agent wrapper not found: ${teamAgent}`);
|
|
200
220
|
process.exit(1);
|
|
@@ -213,10 +233,11 @@ function runDoctor(argv) {
|
|
|
213
233
|
|
|
214
234
|
function uninstall(argv) {
|
|
215
235
|
const opts = parseOptions(argv);
|
|
216
|
-
const
|
|
236
|
+
const runtimeRoot = path.resolve(expandHome(opts.runtimeDir || path.join(os.homedir(), ".team-agent", "runtime")));
|
|
237
|
+
const binDir = installedBinDir(runtimeRoot, opts);
|
|
217
238
|
// 卸载 skill 走二进制单源(同一 SkillTarget 表 codex/claude/copilot),在删 wrapper 前调
|
|
218
239
|
// (删 wrapper 后 PATH 上的 team-agent 没了,但 runtime 二进制仍在;用 runtime 二进制直调)。
|
|
219
|
-
const teamAgentBin = path.join(
|
|
240
|
+
const teamAgentBin = path.join(binDir, "team-agent");
|
|
220
241
|
if (fs.existsSync(teamAgentBin)) {
|
|
221
242
|
const res = spawnSync(teamAgentBin, ["install-skill", "--target", "all", "--uninstall", "--json"], {
|
|
222
243
|
text: true,
|
|
@@ -227,13 +248,12 @@ function uninstall(argv) {
|
|
|
227
248
|
console.error(`WARN: skill uninstall via binary failed (status=${res.status ?? "signal"}); skill dirs may remain under ~/.codex|.claude|.copilot/skills/team-agent`);
|
|
228
249
|
}
|
|
229
250
|
}
|
|
230
|
-
for (const name of
|
|
231
|
-
fs.rmSync(path.join(
|
|
251
|
+
for (const name of WRAPPER_NAMES) {
|
|
252
|
+
fs.rmSync(path.join(binDir, name), { force: true });
|
|
232
253
|
}
|
|
233
|
-
console.log(`removed wrappers from ${
|
|
254
|
+
console.log(`removed wrappers from ${binDir}`);
|
|
234
255
|
console.log("removed skills from ~/.codex, ~/.claude and ~/.copilot skills/team-agent");
|
|
235
256
|
if (opts.purgeRuntime) {
|
|
236
|
-
const runtimeRoot = path.resolve(expandHome(opts.runtimeDir || path.join(os.homedir(), ".team-agent", "runtime")));
|
|
237
257
|
fs.rmSync(runtimeRoot, { recursive: true, force: true });
|
|
238
258
|
console.log(`removed runtime root ${runtimeRoot}`);
|
|
239
259
|
} else {
|
|
@@ -262,6 +282,182 @@ function parseOptions(argv) {
|
|
|
262
282
|
return opts;
|
|
263
283
|
}
|
|
264
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
|
+
|
|
265
461
|
function resolvePlatformBinary() {
|
|
266
462
|
const packageName = platformPackageName();
|
|
267
463
|
if (!packageName) {
|
|
@@ -304,9 +500,13 @@ function copyExecutable(src, dest) {
|
|
|
304
500
|
}
|
|
305
501
|
|
|
306
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
|
+
}
|
|
307
506
|
const args = fixedArgs.map(shellQuote).join(" ");
|
|
308
507
|
const argPrefix = args ? `${args} ` : "";
|
|
309
508
|
const content = `#!/usr/bin/env sh
|
|
509
|
+
${WRAPPER_MARKER}
|
|
310
510
|
exec ${shellQuote(binary)} ${argPrefix}"$@"
|
|
311
511
|
`;
|
|
312
512
|
fs.writeFileSync(file, content, { mode: 0o755 });
|
|
@@ -340,15 +540,23 @@ function makeDoctorWorkspace() {
|
|
|
340
540
|
}
|
|
341
541
|
|
|
342
542
|
function expandHome(value) {
|
|
543
|
+
return expandHomeFor(value, os.homedir());
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function expandHomeFor(value, home) {
|
|
343
547
|
if (value === "~") {
|
|
344
|
-
return
|
|
548
|
+
return home;
|
|
345
549
|
}
|
|
346
550
|
if (value.startsWith("~/")) {
|
|
347
|
-
return path.join(
|
|
551
|
+
return path.join(home, value.slice(2));
|
|
348
552
|
}
|
|
349
553
|
return value;
|
|
350
554
|
}
|
|
351
555
|
|
|
556
|
+
function escapeDoubleQuoted(value) {
|
|
557
|
+
return String(value).replace(/["\\`$]/g, "\\$&");
|
|
558
|
+
}
|
|
559
|
+
|
|
352
560
|
function shellQuote(value) {
|
|
353
561
|
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
354
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.27",
|
|
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.27",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.3.27",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.3.27"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|