@team-agent/installer 0.5.108 → 0.5.110
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/README.md +33 -0
- package/README.zh.md +30 -0
- package/crates/team-agent/src/cli/emit.rs +8 -1
- package/crates/team-agent/src/cli/mod.rs +2 -1
- package/crates/team-agent/src/cli/route.rs +207 -0
- package/crates/team-agent/src/cli/spec.rs +3 -0
- package/crates/team-agent/src/cli/status_port/brief.rs +224 -33
- package/crates/team-agent/src/leader/start.rs +5 -1
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/cli_argv_routing_red.rs +1457 -0
- package/crates/team-agent/src/lifecycle/tests.rs +1 -0
- package/crates/team-agent/src/provider/argv_route.rs +409 -0
- package/crates/team-agent/src/provider/mod.rs +3 -2
- package/crates/team-agent/src/provider/model_catalog.rs +177 -2
- package/crates/team-agent/src/tmux_backend.rs +99 -2
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +32 -0
|
@@ -173,11 +173,14 @@ impl RealCommandRunner {
|
|
|
173
173
|
let stdout_thread = std::thread::spawn(move || read_pipe(stdout));
|
|
174
174
|
let stderr_thread = std::thread::spawn(move || read_pipe(stderr));
|
|
175
175
|
let deadline = Instant::now() + COMMAND_TIMEOUT;
|
|
176
|
+
// Observe short-lived commands promptly; retain the old cadence for long ones.
|
|
177
|
+
let mut poll_interval = Duration::from_millis(1);
|
|
176
178
|
let status = loop {
|
|
177
179
|
if let Some(status) = child.try_wait()? {
|
|
178
180
|
break status;
|
|
179
181
|
}
|
|
180
|
-
|
|
182
|
+
let now = Instant::now();
|
|
183
|
+
if now >= deadline {
|
|
181
184
|
child.kill()?;
|
|
182
185
|
child.wait()?;
|
|
183
186
|
let _ = join_pipe_reader(stdout_thread)?;
|
|
@@ -187,7 +190,8 @@ impl RealCommandRunner {
|
|
|
187
190
|
format!("{program} exceeded 5s timeout"),
|
|
188
191
|
));
|
|
189
192
|
}
|
|
190
|
-
std::thread::sleep(
|
|
193
|
+
std::thread::sleep(poll_interval.min(deadline.saturating_duration_since(now)));
|
|
194
|
+
poll_interval = (poll_interval * 2).min(Duration::from_millis(25));
|
|
191
195
|
};
|
|
192
196
|
let stdout = join_pipe_reader(stdout_thread)?;
|
|
193
197
|
let stderr = join_pipe_reader(stderr_thread)?;
|
|
@@ -4315,3 +4319,96 @@ fn non_empty(raw: &str) -> Option<&str> {
|
|
|
4315
4319
|
// tmux is a Unix concept), the test module stays Unix-only.
|
|
4316
4320
|
#[cfg(all(test, unix))]
|
|
4317
4321
|
mod tests;
|
|
4322
|
+
|
|
4323
|
+
#[cfg(all(test, unix))]
|
|
4324
|
+
mod command_runner_regressions {
|
|
4325
|
+
use super::{CommandRunner, RealCommandRunner, COMMAND_TIMEOUT};
|
|
4326
|
+
use std::io::ErrorKind;
|
|
4327
|
+
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|
4328
|
+
|
|
4329
|
+
fn shell(command: &str) -> Vec<String> {
|
|
4330
|
+
vec!["/bin/sh".into(), "-c".into(), command.into()]
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4333
|
+
#[test]
|
|
4334
|
+
fn nonzero_exit_preserves_both_outputs_and_lossy_utf8() {
|
|
4335
|
+
let output = RealCommandRunner
|
|
4336
|
+
.run(&shell("printf 'stdout\\377'; printf 'stderr\\376' >&2; exit 17"))
|
|
4337
|
+
.unwrap();
|
|
4338
|
+
assert!(!output.success);
|
|
4339
|
+
assert_eq!(output.code, Some(17));
|
|
4340
|
+
assert_eq!(output.stdout, "stdout\u{fffd}");
|
|
4341
|
+
assert_eq!(output.stderr, "stderr\u{fffd}");
|
|
4342
|
+
}
|
|
4343
|
+
|
|
4344
|
+
#[test]
|
|
4345
|
+
fn stdin_is_delivered_and_closed_before_waiting_for_exit() {
|
|
4346
|
+
let input = "first line\nsecond λ line\n".repeat(32);
|
|
4347
|
+
let output = RealCommandRunner
|
|
4348
|
+
.run_with_stdin(&["/bin/cat".into()], &input)
|
|
4349
|
+
.unwrap();
|
|
4350
|
+
assert!(output.success);
|
|
4351
|
+
assert_eq!(output.code, Some(0));
|
|
4352
|
+
assert_eq!(output.stdout, input);
|
|
4353
|
+
assert_eq!(output.stderr, "");
|
|
4354
|
+
}
|
|
4355
|
+
|
|
4356
|
+
#[test]
|
|
4357
|
+
fn signal_exit_retains_absent_exit_code() {
|
|
4358
|
+
let output = RealCommandRunner.run(&shell("kill -TERM $$")).unwrap();
|
|
4359
|
+
assert!(!output.success);
|
|
4360
|
+
assert_eq!(output.code, None);
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
#[test]
|
|
4364
|
+
fn drains_stdout_and_stderr_beyond_pipe_capacity() {
|
|
4365
|
+
let output = RealCommandRunner
|
|
4366
|
+
.run(&shell(
|
|
4367
|
+
"i=0; while [ \"$i\" -lt 20000 ]; do printf 'out\\n'; printf 'err\\n' >&2; i=$((i+1)); done",
|
|
4368
|
+
))
|
|
4369
|
+
.unwrap();
|
|
4370
|
+
assert!(output.success);
|
|
4371
|
+
assert_eq!(output.code, Some(0));
|
|
4372
|
+
assert_eq!(output.stdout, "out\n".repeat(20000));
|
|
4373
|
+
assert_eq!(output.stderr, "err\n".repeat(20000));
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4376
|
+
#[test]
|
|
4377
|
+
fn timeout_keeps_five_second_budget_and_reaps_the_owned_child() {
|
|
4378
|
+
let nonce = SystemTime::now()
|
|
4379
|
+
.duration_since(UNIX_EPOCH)
|
|
4380
|
+
.unwrap()
|
|
4381
|
+
.as_nanos();
|
|
4382
|
+
let pid_path = std::env::temp_dir().join(format!(
|
|
4383
|
+
"team-agent-runner-timeout-{}-{nonce}.pid",
|
|
4384
|
+
std::process::id()
|
|
4385
|
+
));
|
|
4386
|
+
let file = std::fs::OpenOptions::new()
|
|
4387
|
+
.write(true)
|
|
4388
|
+
.create_new(true)
|
|
4389
|
+
.open(&pid_path)
|
|
4390
|
+
.unwrap();
|
|
4391
|
+
drop(file);
|
|
4392
|
+
// exec keeps one owned PID and closes both pipes when the runner kills it.
|
|
4393
|
+
let mut argv = shell("printf '%s' \"$$\" > \"$1\"; exec /bin/sleep 30");
|
|
4394
|
+
argv.extend([
|
|
4395
|
+
"runner-timeout-test".into(),
|
|
4396
|
+
pid_path.to_string_lossy().into_owned(),
|
|
4397
|
+
]);
|
|
4398
|
+
let started = Instant::now();
|
|
4399
|
+
let error = RealCommandRunner.run(&argv).unwrap_err();
|
|
4400
|
+
let elapsed = started.elapsed();
|
|
4401
|
+
let pid: i32 = std::fs::read_to_string(&pid_path).unwrap().parse().unwrap();
|
|
4402
|
+
std::fs::remove_file(pid_path).unwrap();
|
|
4403
|
+
assert_eq!(error.kind(), ErrorKind::TimedOut);
|
|
4404
|
+
assert!(elapsed >= COMMAND_TIMEOUT);
|
|
4405
|
+
assert!(elapsed < COMMAND_TIMEOUT + Duration::from_secs(2));
|
|
4406
|
+
assert!(pid > 1);
|
|
4407
|
+
// SAFETY: signal 0 only probes the exact child PID recorded by this fixture.
|
|
4408
|
+
assert_eq!(unsafe { libc::kill(pid, 0) }, -1);
|
|
4409
|
+
assert_eq!(
|
|
4410
|
+
std::io::Error::last_os_error().raw_os_error(),
|
|
4411
|
+
Some(libc::ESRCH)
|
|
4412
|
+
);
|
|
4413
|
+
}
|
|
4414
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.110",
|
|
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.5.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.5.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.5.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.5.110",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.110",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.110"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -79,6 +79,38 @@ A different `--provider`, including a manually edited role provider, is rejected
|
|
|
79
79
|
|
|
80
80
|
Profiles keep provider authentication, endpoints and custom-model settings local. Imported roles use this team's/workspace's `profiles`, never the original external directory. Prepare a named profile locally through the public profile commands; do not search for or copy external credentials. Never put secrets in `TEAM.md` or a role file. See [the operator reference](references/team-agent-operator.md) for existing profile and field details. Supported metadata also includes `agent_id` (legacy `name`), `role`, `auth_mode`, and `communication_mode`; old `tools`, `permission_mode`, and `label` keys do not configure a worker.
|
|
81
81
|
|
|
82
|
+
## Global native CLI argv (optional)
|
|
83
|
+
|
|
84
|
+
Default OFF; no workspace or provider login is needed to manage routes:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
team-agent route set pi -- --mode rpc
|
|
88
|
+
team-agent route enable
|
|
89
|
+
team-agent route status --json
|
|
90
|
+
team-agent route show pi --json
|
|
91
|
+
team-agent route add pi -- --verbose
|
|
92
|
+
team-agent route clear pi
|
|
93
|
+
team-agent route disable
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`set` replaces one mapping, `add` appends without deduplication, `clear` removes it;
|
|
97
|
+
none changes the switch. `enable`/`disable` preserve mappings in `~/.team-agent/argv-routing.json`.
|
|
98
|
+
`TEAM_AGENT_CLI_ARGV_ROUTING` overrides the switch: `1/true/on`, `0/false/off` (case-insensitive,
|
|
99
|
+
trimmed); invalid values disable routing. Check `persisted_enabled`, `effective_enabled` and
|
|
100
|
+
`override_status` rather than assuming `enable` defeats an environment OFF.
|
|
101
|
+
|
|
102
|
+
Keys: `claude`, `codex`, `copilot`, `gemini_cli`, `grok`, `cursor_agent`, `pi`.
|
|
103
|
+
Claude/Claude Code share `claude`; aliases `claude_code/claude-code`, `agent/cursor` are accepted.
|
|
104
|
+
The first `--` ends control parsing: subsequent tokens, including `--help`, `--json`, empty strings
|
|
105
|
+
and `{workspace}`, are literal data. Tokens go after the executable and before its original tail;
|
|
106
|
+
there is no shell expansion. Do not route secrets or override framework-managed session/MCP/permission flags.
|
|
107
|
+
|
|
108
|
+
The current mapping affects the next actual Leader/worker spawn (also restart, single-seat start/reset,
|
|
109
|
+
add/clone, Pi new-seat fork and remove rollback), not running/attached processes, dry-run or helper probes.
|
|
110
|
+
Enabled bad configuration prevents native spawn; an emergency environment OFF avoids reading it,
|
|
111
|
+
while `route disable` will not overwrite a broken file. Pi `--mode rpc` does not provide an RPC host
|
|
112
|
+
or change the existing Team Agent transport.
|
|
113
|
+
|
|
82
114
|
## Inspect and stop
|
|
83
115
|
|
|
84
116
|
Use `team-agent status --json` for readiness and the installed command's `--help` for syntax. `ok: true` with `ready: false` means the team is not ready; follow the reported action. Accepted/queued send is not the worker's reply: wait for a natural response or result. Use `<workspace>::<team>/<agent>` when a fully qualified recipient is needed.
|