aiur-cli 0.0.9-nightly.5a0d430 → 0.0.9-nightly.d4c6e4c

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/aiur.js CHANGED
@@ -165,8 +165,8 @@ function ensureTmux() {
165
165
  // True only when the pinned opencode is on PATH; a wrong (e.g. newer,
166
166
  // unverified) version must be replaced with the pin, so presence alone
167
167
  // isn't enough.
168
- function opencodeMatchesPin() {
169
- const result = safeSpawn("opencode", ["--version"], { encoding: "utf8" });
168
+ function opencodeMatchesPin(command = "opencode") {
169
+ const result = safeSpawn(command, ["--version"], { encoding: "utf8" });
170
170
  return !result.error && result.status === 0 && (result.stdout || "").trim() === opencodeVersion();
171
171
  }
172
172
 
@@ -185,40 +185,60 @@ function installOpencode() {
185
185
  return !result.error && result.status === 0;
186
186
  }
187
187
 
188
+ // npm's global bin may be shadowed by another OpenCode on the incoming PATH.
189
+ // Verify the actual installed executable before making it the release's choice.
190
+ function selectGlobalOpencode() {
191
+ const result = safeSpawn("npm", ["prefix", "-g"], { encoding: "utf8" });
192
+ if (result.error || result.status !== 0) return false;
193
+ const prefix = (result.stdout || "").trim();
194
+ if (!path.isAbsolute(prefix)) return false;
195
+ const bin = path.join(prefix, "bin");
196
+ if (!opencodeMatchesPin(path.join(bin, "opencode"))) return false;
197
+ process.env.PATH = `${bin}${path.delimiter}${process.env.PATH || ""}`;
198
+ return true;
199
+ }
200
+
188
201
  // opencode powers the interactive "take the wheel" panes. Non-fatal: everything
189
202
  // else works without it.
190
203
  function ensureOpencode() {
191
- if (opencodeMatchesPin()) return;
204
+ if (opencodeMatchesPin() || selectGlobalOpencode()) return;
192
205
  if (process.env.AIUR_SKIP_OPENCODE_INSTALL !== "1") {
193
206
  process.stderr.write(
194
207
  `aiur: provisioning opencode@${opencodeVersion()} (one-time, for the interactive 'take the wheel' panes)…\n`,
195
208
  );
196
209
  installOpencode();
197
- if (opencodeMatchesPin()) return;
210
+ if (opencodeMatchesPin() || selectGlobalOpencode()) return;
198
211
  }
199
212
  process.stderr.write(
200
- "aiur: opencode was not found on PATH. The interactive 'take the wheel' " +
213
+ `aiur: opencode@${opencodeVersion()} is unavailable. The interactive 'take the wheel' ` +
201
214
  "feature needs it; everything else works without it. " +
202
- "Install from https://opencode.ai\n",
215
+ `Install with: npm install -g opencode-ai@${opencodeVersion()}, ` +
216
+ "and put npm's global bin ahead of other opencode binaries on PATH.\n",
203
217
  );
204
218
  }
205
219
 
206
- // `init` and `--version` run as foreground one-shots that never start the
207
- // tmux-backed UI, so their tmux/opencode provisioning is irrelevant — and tmux
208
- // may legitimately be absent on a machine that only ever runs `aiur init`.
209
- function isForegroundOneShot(argv) {
210
- for (const arg of argv) {
211
- if (arg === "--version") return true;
212
- if (arg.startsWith("-")) continue;
213
- return arg === "init";
214
- }
215
- return false;
220
+ // Match the shared engine's first-argument dispatch: only session launch paths
221
+ // need interactive tools. Unknown commands still reach the engine's usage error.
222
+ function requiresInteractiveTools(argv) {
223
+ const command = argv[0] || "";
224
+ if (["", "run", "restart", "--bg"].includes(command)) return true;
225
+ const nonLaunchCommands = new Set([
226
+ "__identity", "help", "-h", "-help", "--h", "--help", "--version", "--todo", "--only",
227
+ "init", "findings", "ask", "asks", "status", "usage", "agents", "commands", "units",
228
+ "build-orders", "analytics", "github-cost", "github-usage", "alerts", "watch", "set",
229
+ "upgrade", "pause", "resume", "reset-budget", "message", "cleanup-stale", "stop",
230
+ "executor-answer", "executor-escalate", "executor-moot", "executor-listen", "executor-wait",
231
+ "executor-emit", "executor-subscribe", "executor-unsubscribe", "executor-subscriptions",
232
+ "executor-roster", "executor-fast-forward", "executor-claim", "executor-release", "executor-revoke",
233
+ ]);
234
+ if (nonLaunchCommands.has(command)) return false;
235
+ return command.startsWith("-") || fs.existsSync(command);
216
236
  }
217
237
 
218
238
  function main() {
219
239
  const releaseDir = resolveReleaseDir();
220
240
 
221
- if (!isForegroundOneShot(process.argv.slice(2))) {
241
+ if (requiresInteractiveTools(process.argv.slice(2))) {
222
242
  ensureTmux();
223
243
  ensureOpencode();
224
244
  }
@@ -450,6 +450,9 @@ usage() {
450
450
  Usage: aiur [--interactive] [--no-dashboard] [--executor] [--pause] [--max-agents <n>] [--logs-root <path>] [--port <port>] [--host <host>] [config-path]
451
451
  aiur run [--bg] [--no-dashboard] [--executor] [--debug] explicit launch form (foreground unless --bg)
452
452
  aiur init [--force] scaffold .aiur/config (interactive setup wizard)
453
+ aiur login <harness> <name> [--dir <path>] sign in to a supported backend account
454
+ aiur accounts [<harness>] [--json] list registered accounts
455
+ aiur logout <harness> <name> [--purge] remove a backend account
453
456
  aiur --bg [--no-dashboard] [--executor] [--debug] start detached; dashboard on unless suppressed
454
457
  aiur stop stop the running session
455
458
  aiur restart [--no-build] [run flags] stop, refresh the build, start again (detached)
@@ -472,6 +475,7 @@ Usage: aiur [--interactive] [--no-dashboard] [--executor] [--pause] [--max-agent
472
475
  aiur executor-emit <topic> --payload <json> publish an Executor event
473
476
  aiur executor-subscribe|executor-unsubscribe <pattern>
474
477
  aiur executor-subscriptions list persistent Executor bindings
478
+ aiur workspace-recover <ticket-identifier> <generation> release a held workspace after verified provider exit
475
479
  aiur executor-roster [--json] list Executor consumers with their liveness evidence
476
480
  aiur executor-claim [--as <id>] claim the wake stream, or refuse and name the live owner
477
481
  aiur executor-release [--as <id>] give up this consumer's claim
@@ -522,6 +526,47 @@ run_init() {
522
526
  exec "${release_cmd[@]}"
523
527
  }
524
528
 
529
+ run_local_cli() {
530
+ resolve_release
531
+ build_init_cmd
532
+ init_argv_file
533
+ write_argv "$@"
534
+ export AIUR_ARGV_FILE="$argv_file"
535
+ exec "${release_cmd[@]}"
536
+ }
537
+
538
+ run_claude_login() {
539
+ resolve_release
540
+ local profile_dir
541
+ build_init_cmd
542
+ init_argv_file
543
+ write_argv "__login_prepare" "${@:2}"
544
+ export AIUR_ARGV_FILE="$argv_file"
545
+ profile_dir="$("${release_cmd[@]}")" || return $?
546
+ [ -n "$profile_dir" ] || { echo "aiur: account profile directory is unavailable" >&2; return 1; }
547
+ exec env "CLAUDE_CONFIG_DIR=$profile_dir" claude
548
+ }
549
+
550
+ run_account_login() {
551
+ local harness="$2"
552
+ case "$harness" in
553
+ kimi|deepseek|openrouter) run_local_cli "$@"; return $? ;;
554
+ esac
555
+ resolve_release
556
+ local profile_dir
557
+ build_init_cmd
558
+ init_argv_file
559
+ write_argv "__login_prepare" "$harness" "${@:3}"
560
+ export AIUR_ARGV_FILE="$argv_file"
561
+ profile_dir="$("${release_cmd[@]}")" || return $?
562
+ [ -n "$profile_dir" ] || { echo "aiur: account profile directory is unavailable" >&2; return 1; }
563
+ case "$harness" in
564
+ claude) exec env "CLAUDE_CONFIG_DIR=$profile_dir" claude ;;
565
+ codex) exec env "CODEX_HOME=$profile_dir" codex ;;
566
+ *) echo "aiur: unsupported account harness: $harness" >&2; return 64 ;;
567
+ esac
568
+ }
569
+
525
570
  # --- one-shot: --todo (control RPC; requires a running daemon) ----------------
526
571
 
527
572
  parsed_todo_only=0
@@ -723,22 +768,10 @@ scrub_run_only_env() {
723
768
  }
724
769
 
725
770
  run_argv=()
726
- # Default dashboard bind host. Prefer this machine's Tailscale IPv4 so the
727
- # dashboard is reachable across the tailnet by default (when config omits it);
728
- # fall back to loopback when Tailscale is absent, or when dashboard credentials
729
- # are unset (a non-loopback bind requires them, so we stay on loopback rather
730
- # than refuse to start). The BEAM applies this below an explicit `server.host`,
731
- # while an explicit `--host` remains the highest-precedence value.
771
+ # Default dashboard bind host. Remote access is an explicit operator choice.
772
+ # The BEAM applies this below `server.host`; `--host` has highest precedence.
732
773
  default_dashboard_host() {
733
- local ip=""
734
- if command -v tailscale >/dev/null 2>&1; then
735
- ip="$(tailscale ip -4 2>/dev/null | grep -m1 -E '^100\.[0-9]+\.[0-9]+\.[0-9]+$' || true)"
736
- fi
737
- if [ -n "$ip" ] && [ -n "${AIUR_DASHBOARD_USERNAME:-}" ] && [ -n "${AIUR_DASHBOARD_PASSWORD:-}" ]; then
738
- printf '%s' "$ip"
739
- else
740
- printf '127.0.0.1'
741
- fi
774
+ printf '%s' "${AIUR_DEFAULT_DASHBOARD_HOST:-127.0.0.1}"
742
775
  }
743
776
 
744
777
  build_run_argv() {
@@ -2633,6 +2666,44 @@ cmd_usage() {
2633
2666
  run_control_rpc "Aiur.AgentControlCLI.usage()"
2634
2667
  }
2635
2668
 
2669
+ cmd_accounts() {
2670
+ local json_arg=false harness="" arg encoded expression
2671
+ for arg in "$@"; do
2672
+ case "$arg" in
2673
+ --json)
2674
+ [ "$json_arg" = false ] || die "accounts accepts --json only once"
2675
+ json_arg=true
2676
+ ;;
2677
+ --all)
2678
+ ;;
2679
+ -*)
2680
+ die "accounts accepts an optional harness and --json"
2681
+ ;;
2682
+ *)
2683
+ [ -z "$harness" ] || die "accounts accepts only one harness"
2684
+ harness="$arg"
2685
+ ;;
2686
+ esac
2687
+ done
2688
+
2689
+ resolve_release || return $?
2690
+ prepare_distribution || die "distribution setup failed; cannot contact aiur"
2691
+ resolve_control_identity_from_records
2692
+ if [ "$(probe_node_liveness)" = "down" ]; then
2693
+ # The local one-shot CLI renders the identity and marks usage unavailable.
2694
+ # It never makes a provider request.
2695
+ run_local_cli accounts "$@"
2696
+ else
2697
+ if [ -n "$harness" ]; then
2698
+ encoded="$(printf '%s' "$harness" | base64 | tr -d '\n')"
2699
+ expression="Aiur.AgentControlCLI.accounts($json_arg, Base.decode64!(\"$encoded\"))"
2700
+ else
2701
+ expression="Aiur.AgentControlCLI.accounts($json_arg)"
2702
+ fi
2703
+ run_control_rpc "$expression"
2704
+ fi
2705
+ }
2706
+
2636
2707
  cmd_pause_resume() {
2637
2708
  local command="$1"
2638
2709
  shift
@@ -2680,6 +2751,16 @@ cmd_reset_budget() {
2680
2751
  run_control_rpc "$expression"
2681
2752
  }
2682
2753
 
2754
+ # Requires the operator to name the exact ticket and generation shown by
2755
+ # status. The daemon independently verifies the recorded boot proof.
2756
+ cmd_workspace_recover() {
2757
+ [ "$#" -eq 2 ] || { echo "aiur: workspace-recover expects a ticket identifier and generation (e.g. aiur workspace-recover ENG-123 7)" >&2; exit 64; }
2758
+ local ticket="$1" generation="$2" encoded
2759
+ [[ "$generation" =~ ^[1-9][0-9]*$ ]] || { echo "aiur: workspace-recover generation must be a positive integer" >&2; exit 64; }
2760
+ encoded="$(printf '%s' "$ticket" | base64 | tr -d '\n')"
2761
+ run_control_rpc "Aiur.AgentControlCLI.recover_workspace(Base.decode64!(\"$encoded\"), $generation)"
2762
+ }
2763
+
2683
2764
  # `aiur message <issue> <text>` — deliver Executor text to one running agent.
2684
2765
  # The text is base64-encoded for the RPC hop so arbitrary content (quotes,
2685
2766
  # backslashes, `#{}`, newlines) survives without Elixir-string escaping.
@@ -4106,6 +4187,16 @@ aiur_engine_main() {
4106
4187
  init)
4107
4188
  run_init "$@"
4108
4189
  ;;
4190
+ login)
4191
+ run_account_login "$@"
4192
+ ;;
4193
+ accounts)
4194
+ shift
4195
+ cmd_accounts "$@"
4196
+ ;;
4197
+ logout)
4198
+ run_local_cli "$@"
4199
+ ;;
4109
4200
  findings)
4110
4201
  run_findings "$@"
4111
4202
  ;;
@@ -4231,6 +4322,10 @@ aiur_engine_main() {
4231
4322
  shift
4232
4323
  cmd_reset_budget "$@"
4233
4324
  ;;
4325
+ workspace-recover)
4326
+ shift
4327
+ cmd_workspace_recover "$@"
4328
+ ;;
4234
4329
  message)
4235
4330
  shift
4236
4331
  cmd_message "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiur-cli",
3
- "version": "0.0.9-nightly.5a0d430",
3
+ "version": "0.0.9-nightly.d4c6e4c",
4
4
  "description": "AI Unit Runtime for Executors.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/aiur-team/aiur",
@@ -35,8 +35,8 @@
35
35
  ],
36
36
  "opencodeVersion": "1.17.10",
37
37
  "optionalDependencies": {
38
- "aiur-cli-linux-x64": "0.0.9-nightly.5a0d430",
39
- "aiur-cli-linux-arm64": "0.0.9-nightly.5a0d430",
40
- "aiur-cli-darwin-arm64": "0.0.9-nightly.5a0d430"
38
+ "aiur-cli-linux-x64": "0.0.9-nightly.d4c6e4c",
39
+ "aiur-cli-linux-arm64": "0.0.9-nightly.d4c6e4c",
40
+ "aiur-cli-darwin-arm64": "0.0.9-nightly.d4c6e4c"
41
41
  }
42
42
  }