@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.
@@ -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
- if Instant::now() >= deadline {
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(Duration::from_millis(25));
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.108",
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.108",
24
- "@team-agent/cli-darwin-x64": "0.5.108",
25
- "@team-agent/cli-linux-x64": "0.5.108"
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.