@cabane/companion 0.6.154 → 0.6.155

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/dist/cli.js CHANGED
@@ -585,7 +585,8 @@ var init_config = __esm({
585
585
  // src/bundled-binaries.ts
586
586
  import { realpathSync, statSync } from "fs";
587
587
  import { createRequire } from "module";
588
- import { dirname as dirname2, join as join3 } from "path";
588
+ import { homedir as homedir2 } from "os";
589
+ import { dirname as dirname2, join as join3, sep } from "path";
589
590
  import { fileURLToPath } from "url";
590
591
  function nodeResolveSelf(specifier) {
591
592
  const here = fileURLToPath(import.meta.url);
@@ -741,25 +742,36 @@ function resolveBundledBinary(runtime, env = defaultResolveEnv()) {
741
742
  function missingNoun(status2) {
742
743
  return status2 === "executable_missing" ? "executable" : "platform package";
743
744
  }
744
- function repairCommands() {
745
- return ["npm i -g @cabane/companion --include=optional"];
745
+ function companionInstall(execPath = process.execPath, home = homedir2()) {
746
+ const real = (path) => {
747
+ try {
748
+ return realpathSync(path);
749
+ } catch {
750
+ return path;
751
+ }
752
+ };
753
+ const scriptNode = join3(real(home), ".cabane", "node") + sep;
754
+ return real(execPath).startsWith(scriptNode) ? "script" : "npm";
755
+ }
756
+ function repairCommands(install = companionInstall()) {
757
+ return install === "script" ? [INSTALL_SCRIPT_COMMAND] : ["npm i -g @cabane/companion --include=optional"];
746
758
  }
747
- function repairAction() {
748
- return `Reinstall with \`${repairCommands()[0]}\`.`;
759
+ function repairAction(install = companionInstall()) {
760
+ return `Reinstall with \`${repairCommands(install)[0]}\`.`;
749
761
  }
750
762
  function startupWarning(r) {
751
763
  const label = HARNESS_LABEL[r.runtime];
752
764
  return `${label} is ${HARNESS_INTENT_WORD[r.runtime]}, but its installation is incomplete (${missingNoun(r.status)} is missing). It cannot run turns until repaired. ${repairAction()} Then restart the companion.`;
753
765
  }
754
- function harnessIssueNote(runtime, status2) {
766
+ function harnessIssueNote(runtime, status2, install = companionInstall()) {
755
767
  const intent = HARNESS_INTENT_WORD[runtime];
756
768
  const capitalized = `${intent[0].toUpperCase()}${intent.slice(1)}`;
757
- return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status2)} is missing. ${repairAction()} Then restart the companion.`;
769
+ return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status2)} is missing. ${repairAction(install)} Then restart the companion.`;
758
770
  }
759
771
  function turnIncompleteCopy(runtime) {
760
772
  return `**This device's ${HARNESS_LABEL[runtime]} runtime is incomplete.** ${repairAction()} Restart it, then try again.`;
761
773
  }
762
- var CLAUDE_SDK, CODEX_SDK, CODEX_CLI, CODEX_PACKAGE_BY_TRIPLE, HARNESS_LABEL, HARNESS_INTENT_WORD;
774
+ var CLAUDE_SDK, CODEX_SDK, CODEX_CLI, CODEX_PACKAGE_BY_TRIPLE, HARNESS_LABEL, HARNESS_INTENT_WORD, INSTALL_SCRIPT_COMMAND;
763
775
  var init_bundled_binaries = __esm({
764
776
  "src/bundled-binaries.ts"() {
765
777
  "use strict";
@@ -782,12 +794,13 @@ var init_bundled_binaries = __esm({
782
794
  "claude-code": "connected",
783
795
  codex: "enabled"
784
796
  };
797
+ INSTALL_SCRIPT_COMMAND = "curl -fsSL https://cabane.ai/install.sh | bash";
785
798
  }
786
799
  });
787
800
 
788
801
  // src/term.ts
789
802
  import { createInterface } from "readline";
790
- import { homedir as homedir2 } from "os";
803
+ import { homedir as homedir3 } from "os";
791
804
  import pc from "picocolors";
792
805
  function isInteractive() {
793
806
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
@@ -800,7 +813,7 @@ function blank() {
800
813
  process.stdout.write("\n");
801
814
  }
802
815
  function tildePath(path) {
803
- const home = homedir2();
816
+ const home = homedir3();
804
817
  if (home && path.startsWith(home + "/")) return `~${path.slice(home.length)}`;
805
818
  return path;
806
819
  }
@@ -2543,8 +2556,8 @@ var init_policy = __esm({
2543
2556
  // packages/agent-runtime/src/claude-code/model.ts
2544
2557
  function parseClaudeCodeModel(model) {
2545
2558
  if (!model) return null;
2546
- const sep3 = model.indexOf("/");
2547
- const bare = sep3 === -1 || model.slice(0, sep3) !== "anthropic" ? model : model.slice(sep3 + 1);
2559
+ const sep4 = model.indexOf("/");
2560
+ const bare = sep4 === -1 || model.slice(0, sep4) !== "anthropic" ? model : model.slice(sep4 + 1);
2548
2561
  return bare === CLAUDE_CODE_DEFAULT_MODEL_BARE ? null : model;
2549
2562
  }
2550
2563
  var CLAUDE_CODE_DEFAULT_MODEL_BARE;
@@ -4097,9 +4110,9 @@ var init_policy2 = __esm({
4097
4110
 
4098
4111
  // packages/agent-runtime/src/opencode/model.ts
4099
4112
  function parseOpencodeModel(model) {
4100
- const sep3 = model.indexOf("/");
4101
- if (sep3 === -1) return { providerID: "", modelID: model };
4102
- return { providerID: model.slice(0, sep3), modelID: model.slice(sep3 + 1) };
4113
+ const sep4 = model.indexOf("/");
4114
+ if (sep4 === -1) return { providerID: "", modelID: model };
4115
+ return { providerID: model.slice(0, sep4), modelID: model.slice(sep4 + 1) };
4103
4116
  }
4104
4117
  var init_model2 = __esm({
4105
4118
  "packages/agent-runtime/src/opencode/model.ts"() {
@@ -4400,10 +4413,10 @@ async function* parseSseStream(body, sessionId, signal) {
4400
4413
  const { value, done } = await reader.read();
4401
4414
  if (done) return;
4402
4415
  buf += decoder.decode(value, { stream: true });
4403
- let sep3;
4404
- while ((sep3 = buf.indexOf("\n\n")) !== -1) {
4405
- const frame = buf.slice(0, sep3);
4406
- buf = buf.slice(sep3 + 2);
4416
+ let sep4;
4417
+ while ((sep4 = buf.indexOf("\n\n")) !== -1) {
4418
+ const frame = buf.slice(0, sep4);
4419
+ buf = buf.slice(sep4 + 2);
4407
4420
  const data = frame.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("");
4408
4421
  if (!data) continue;
4409
4422
  let ev;
@@ -5414,8 +5427,8 @@ var init_policy3 = __esm({
5414
5427
 
5415
5428
  // packages/agent-runtime/src/codex/model.ts
5416
5429
  function parseCodexModel(model) {
5417
- const sep3 = model.indexOf("/");
5418
- const bare = sep3 === -1 || model.slice(0, sep3) !== "openai" ? model : model.slice(sep3 + 1);
5430
+ const sep4 = model.indexOf("/");
5431
+ const bare = sep4 === -1 || model.slice(0, sep4) !== "openai" ? model : model.slice(sep4 + 1);
5419
5432
  return CODEX_DEFAULT_MODEL_BARES.has(bare) ? null : bare;
5420
5433
  }
5421
5434
  var CODEX_DEFAULT_MODEL_BARES;
@@ -10510,8 +10523,8 @@ import {
10510
10523
  } from "fs";
10511
10524
  import { createRequire as createRequire2 } from "module";
10512
10525
  import { isIP } from "net";
10513
- import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
10514
- import { dirname as dirname8, join as join19, sep as sep2 } from "path";
10526
+ import { homedir as homedir4, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
10527
+ import { dirname as dirname8, join as join19, sep as sep3 } from "path";
10515
10528
  import { fileURLToPath as fileURLToPath4 } from "url";
10516
10529
  import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
10517
10530
  function accountHome() {
@@ -10637,13 +10650,13 @@ function companionStoreWatch(log) {
10637
10650
  return new CompanionStoreWatch({
10638
10651
  log,
10639
10652
  roots: () => {
10640
- const home = homedir3();
10653
+ const home = homedir4();
10641
10654
  return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
10642
10655
  }
10643
10656
  });
10644
10657
  }
10645
10658
  function isAtOrUnder(path, dir2) {
10646
- return path === dir2 || path.startsWith(dir2 + sep2);
10659
+ return path === dir2 || path.startsWith(dir2 + sep3);
10647
10660
  }
10648
10661
  function expandHome(path, home) {
10649
10662
  if (path === "~") return home;
@@ -10782,7 +10795,7 @@ async function prepareHarnessSandbox(input) {
10782
10795
  baseUrl: input.baseUrl,
10783
10796
  resolved: await resolvedAddresses(input.baseUrl),
10784
10797
  ownAddresses: ownAddresses(),
10785
- home: homedir3(),
10798
+ home: homedir4(),
10786
10799
  accountHome: accountHome(),
10787
10800
  tmp: tmpdir4(),
10788
10801
  cabaneDir: cabaneDir(),
@@ -11814,7 +11827,7 @@ function runtimeIssuesFor(gates) {
11814
11827
  for (const name of ["claude-code", "codex"]) {
11815
11828
  const gate = name === "codex" ? gates.codex : gates.claudeCode;
11816
11829
  if (!gate.intended || gate.bundled === "present") continue;
11817
- issues.push({ name, reason: gate.bundled });
11830
+ issues.push({ name, reason: gate.bundled, install: gates.install });
11818
11831
  }
11819
11832
  return issues;
11820
11833
  }
@@ -14011,7 +14024,7 @@ function publishStaged(from, to, move = atomicMove) {
14011
14024
  // src/providers/runner-base.ts
14012
14025
  import { createHash as createHash3 } from "crypto";
14013
14026
  import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
14014
- import { dirname as dirname7, join as join17, sep } from "path";
14027
+ import { dirname as dirname7, join as join17, sep as sep2 } from "path";
14015
14028
  import { fileURLToPath as fileURLToPath3 } from "url";
14016
14029
  var COMPANION_MOUNT = "/opt/cabane/companion";
14017
14030
  var BASE_DOCKERFILE = `FROM node:22-slim
@@ -14066,7 +14079,7 @@ function ownInstall(moduleFile = fileURLToPath3(import.meta.url)) {
14066
14079
  const sdk = join17(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
14067
14080
  if (!existsSync12(sdk)) return null;
14068
14081
  const root = realpathSync2(dir2);
14069
- return realpathSync2(sdk).startsWith(root + sep) ? root : null;
14082
+ return realpathSync2(sdk).startsWith(root + sep2) ? root : null;
14070
14083
  }
14071
14084
  function companionSource(input) {
14072
14085
  if (input.configured) {
@@ -15801,7 +15814,8 @@ var CompanionSupervisor = class {
15801
15814
  intended: isClaudeCodeConnected(this.config),
15802
15815
  bundled: this.bundledStatus("claude-code")
15803
15816
  },
15804
- codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") }
15817
+ codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") },
15818
+ install: companionInstall()
15805
15819
  });
15806
15820
  }
15807
15821
  // A missing binary is reported ONCE, and again only when the answer CHANGES,
@@ -1,7 +1,8 @@
1
1
  // src/bundled-binaries.ts
2
2
  import { realpathSync, statSync } from "fs";
3
3
  import { createRequire } from "module";
4
- import { dirname, join } from "path";
4
+ import { homedir } from "os";
5
+ import { dirname, join, sep } from "path";
5
6
  import { fileURLToPath } from "url";
6
7
  function nodeResolveSelf(specifier) {
7
8
  const here = fileURLToPath(import.meta.url);
@@ -178,11 +179,23 @@ var HARNESS_LABEL = {
178
179
  function missingNoun(status) {
179
180
  return status === "executable_missing" ? "executable" : "platform package";
180
181
  }
181
- function repairCommands() {
182
- return ["npm i -g @cabane/companion --include=optional"];
182
+ var INSTALL_SCRIPT_COMMAND = "curl -fsSL https://cabane.ai/install.sh | bash";
183
+ function companionInstall(execPath = process.execPath, home = homedir()) {
184
+ const real = (path) => {
185
+ try {
186
+ return realpathSync(path);
187
+ } catch {
188
+ return path;
189
+ }
190
+ };
191
+ const scriptNode = join(real(home), ".cabane", "node") + sep;
192
+ return real(execPath).startsWith(scriptNode) ? "script" : "npm";
193
+ }
194
+ function repairCommands(install = companionInstall()) {
195
+ return install === "script" ? [INSTALL_SCRIPT_COMMAND] : ["npm i -g @cabane/companion --include=optional"];
183
196
  }
184
- function repairAction() {
185
- return `Reinstall with \`${repairCommands()[0]}\`.`;
197
+ function repairAction(install = companionInstall()) {
198
+ return `Reinstall with \`${repairCommands(install)[0]}\`.`;
186
199
  }
187
200
  function installWarning(r) {
188
201
  return `warning: @cabane/companion is incomplete: its bundled ${HARNESS_LABEL[r.runtime]} ${missingNoun(r.status)} for ${r.platform}-${r.arch} is missing. ${repairAction()}`;
package/dist/runtime.js CHANGED
@@ -602,7 +602,8 @@ async function safe(fn) {
602
602
  // src/bundled-binaries.ts
603
603
  import { realpathSync, statSync } from "fs";
604
604
  import { createRequire } from "module";
605
- import { dirname as dirname2, join as join2 } from "path";
605
+ import { homedir as homedir2 } from "os";
606
+ import { dirname as dirname2, join as join2, sep } from "path";
606
607
  import { fileURLToPath } from "url";
607
608
  function nodeResolveSelf(specifier) {
608
609
  const here = fileURLToPath(import.meta.url);
@@ -777,20 +778,32 @@ var HARNESS_INTENT_WORD = {
777
778
  function missingNoun(status) {
778
779
  return status === "executable_missing" ? "executable" : "platform package";
779
780
  }
780
- function repairCommands() {
781
- return ["npm i -g @cabane/companion --include=optional"];
781
+ var INSTALL_SCRIPT_COMMAND = "curl -fsSL https://cabane.ai/install.sh | bash";
782
+ function companionInstall(execPath = process.execPath, home = homedir2()) {
783
+ const real = (path) => {
784
+ try {
785
+ return realpathSync(path);
786
+ } catch {
787
+ return path;
788
+ }
789
+ };
790
+ const scriptNode = join2(real(home), ".cabane", "node") + sep;
791
+ return real(execPath).startsWith(scriptNode) ? "script" : "npm";
792
+ }
793
+ function repairCommands(install = companionInstall()) {
794
+ return install === "script" ? [INSTALL_SCRIPT_COMMAND] : ["npm i -g @cabane/companion --include=optional"];
782
795
  }
783
- function repairAction() {
784
- return `Reinstall with \`${repairCommands()[0]}\`.`;
796
+ function repairAction(install = companionInstall()) {
797
+ return `Reinstall with \`${repairCommands(install)[0]}\`.`;
785
798
  }
786
799
  function startupWarning(r) {
787
800
  const label = HARNESS_LABEL[r.runtime];
788
801
  return `${label} is ${HARNESS_INTENT_WORD[r.runtime]}, but its installation is incomplete (${missingNoun(r.status)} is missing). It cannot run turns until repaired. ${repairAction()} Then restart the companion.`;
789
802
  }
790
- function harnessIssueNote(runtime, status) {
803
+ function harnessIssueNote(runtime, status, install = companionInstall()) {
791
804
  const intent = HARNESS_INTENT_WORD[runtime];
792
805
  const capitalized = `${intent[0].toUpperCase()}${intent.slice(1)}`;
793
- return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status)} is missing. ${repairAction()} Then restart the companion.`;
806
+ return `${capitalized}, but @cabane/companion's bundled ${HARNESS_LABEL[runtime]} ${missingNoun(status)} is missing. ${repairAction(install)} Then restart the companion.`;
794
807
  }
795
808
  function turnIncompleteCopy(runtime) {
796
809
  return `**This device's ${HARNESS_LABEL[runtime]} runtime is incomplete.** ${repairAction()} Restart it, then try again.`;
@@ -819,7 +832,7 @@ function runtimeIssuesFor(gates) {
819
832
  for (const name of ["claude-code", "codex"]) {
820
833
  const gate = name === "codex" ? gates.codex : gates.claudeCode;
821
834
  if (!gate.intended || gate.bundled === "present") continue;
822
- issues.push({ name, reason: gate.bundled });
835
+ issues.push({ name, reason: gate.bundled, install: gates.install });
823
836
  }
824
837
  return issues;
825
838
  }
@@ -3951,8 +3964,8 @@ function readThinking(runtimeOptions) {
3951
3964
  var CLAUDE_CODE_DEFAULT_MODEL_BARE = "default";
3952
3965
  function parseClaudeCodeModel(model) {
3953
3966
  if (!model) return null;
3954
- const sep3 = model.indexOf("/");
3955
- const bare = sep3 === -1 || model.slice(0, sep3) !== "anthropic" ? model : model.slice(sep3 + 1);
3967
+ const sep4 = model.indexOf("/");
3968
+ const bare = sep4 === -1 || model.slice(0, sep4) !== "anthropic" ? model : model.slice(sep4 + 1);
3956
3969
  return bare === CLAUDE_CODE_DEFAULT_MODEL_BARE ? null : model;
3957
3970
  }
3958
3971
 
@@ -5407,9 +5420,9 @@ function readOpencodeDialect(runtimeOptions) {
5407
5420
 
5408
5421
  // packages/agent-runtime/src/opencode/model.ts
5409
5422
  function parseOpencodeModel(model) {
5410
- const sep3 = model.indexOf("/");
5411
- if (sep3 === -1) return { providerID: "", modelID: model };
5412
- return { providerID: model.slice(0, sep3), modelID: model.slice(sep3 + 1) };
5423
+ const sep4 = model.indexOf("/");
5424
+ if (sep4 === -1) return { providerID: "", modelID: model };
5425
+ return { providerID: model.slice(0, sep4), modelID: model.slice(sep4 + 1) };
5413
5426
  }
5414
5427
 
5415
5428
  // packages/agent-runtime/src/opencode/run-spec.ts
@@ -5697,10 +5710,10 @@ async function* parseSseStream(body, sessionId, signal) {
5697
5710
  const { value, done } = await reader.read();
5698
5711
  if (done) return;
5699
5712
  buf += decoder.decode(value, { stream: true });
5700
- let sep3;
5701
- while ((sep3 = buf.indexOf("\n\n")) !== -1) {
5702
- const frame = buf.slice(0, sep3);
5703
- buf = buf.slice(sep3 + 2);
5713
+ let sep4;
5714
+ while ((sep4 = buf.indexOf("\n\n")) !== -1) {
5715
+ const frame = buf.slice(0, sep4);
5716
+ buf = buf.slice(sep4 + 2);
5704
5717
  const data = frame.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("");
5705
5718
  if (!data) continue;
5706
5719
  let ev;
@@ -6640,8 +6653,8 @@ function readCodexDialect(runtimeOptions) {
6640
6653
  // packages/agent-runtime/src/codex/model.ts
6641
6654
  var CODEX_DEFAULT_MODEL_BARES = /* @__PURE__ */ new Set(["default"]);
6642
6655
  function parseCodexModel(model) {
6643
- const sep3 = model.indexOf("/");
6644
- const bare = sep3 === -1 || model.slice(0, sep3) !== "openai" ? model : model.slice(sep3 + 1);
6656
+ const sep4 = model.indexOf("/");
6657
+ const bare = sep4 === -1 || model.slice(0, sep4) !== "openai" ? model : model.slice(sep4 + 1);
6645
6658
  return CODEX_DEFAULT_MODEL_BARES.has(bare) ? null : bare;
6646
6659
  }
6647
6660
 
@@ -9158,10 +9171,10 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
9158
9171
 
9159
9172
  // src/term.ts
9160
9173
  import { createInterface as createInterface2 } from "readline";
9161
- import { homedir as homedir2 } from "os";
9174
+ import { homedir as homedir3 } from "os";
9162
9175
  import pc from "picocolors";
9163
9176
  function tildePath(path) {
9164
- const home = homedir2();
9177
+ const home = homedir3();
9165
9178
  if (home && path.startsWith(home + "/")) return `~${path.slice(home.length)}`;
9166
9179
  return path;
9167
9180
  }
@@ -11587,7 +11600,7 @@ function publishStaged(from, to, move = atomicMove) {
11587
11600
  // src/providers/runner-base.ts
11588
11601
  import { createHash as createHash3 } from "crypto";
11589
11602
  import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
11590
- import { dirname as dirname7, join as join17, sep } from "path";
11603
+ import { dirname as dirname7, join as join17, sep as sep2 } from "path";
11591
11604
  import { fileURLToPath as fileURLToPath3 } from "url";
11592
11605
  var COMPANION_MOUNT = "/opt/cabane/companion";
11593
11606
  var BASE_DOCKERFILE = `FROM node:22-slim
@@ -11642,7 +11655,7 @@ function ownInstall(moduleFile = fileURLToPath3(import.meta.url)) {
11642
11655
  const sdk = join17(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
11643
11656
  if (!existsSync12(sdk)) return null;
11644
11657
  const root = realpathSync2(dir2);
11645
- return realpathSync2(sdk).startsWith(root + sep) ? root : null;
11658
+ return realpathSync2(sdk).startsWith(root + sep2) ? root : null;
11646
11659
  }
11647
11660
  function companionSource(input) {
11648
11661
  if (input.configured) {
@@ -12676,8 +12689,8 @@ import {
12676
12689
  } from "fs";
12677
12690
  import { createRequire as createRequire2 } from "module";
12678
12691
  import { isIP } from "net";
12679
- import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
12680
- import { dirname as dirname8, join as join19, sep as sep2 } from "path";
12692
+ import { homedir as homedir4, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
12693
+ import { dirname as dirname8, join as join19, sep as sep3 } from "path";
12681
12694
  import { fileURLToPath as fileURLToPath4 } from "url";
12682
12695
  import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
12683
12696
  function accountHome() {
@@ -12774,13 +12787,13 @@ function companionStoreWatch(log) {
12774
12787
  return new CompanionStoreWatch({
12775
12788
  log,
12776
12789
  roots: () => {
12777
- const home = homedir3();
12790
+ const home = homedir4();
12778
12791
  return storeWalkRoots([.../* @__PURE__ */ new Set([home, accountHome() ?? home])], cabaneDir());
12779
12792
  }
12780
12793
  });
12781
12794
  }
12782
12795
  function isAtOrUnder(path, dir2) {
12783
- return path === dir2 || path.startsWith(dir2 + sep2);
12796
+ return path === dir2 || path.startsWith(dir2 + sep3);
12784
12797
  }
12785
12798
  function osSandboxUsable(log) {
12786
12799
  if (!SandboxManager.isSupportedPlatform()) {
@@ -13675,7 +13688,8 @@ var CompanionSupervisor = class {
13675
13688
  intended: isClaudeCodeConnected(this.config),
13676
13689
  bundled: this.bundledStatus("claude-code")
13677
13690
  },
13678
- codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") }
13691
+ codex: { intended: isCodexEnabled(this.config), bundled: this.bundledStatus("codex") },
13692
+ install: companionInstall()
13679
13693
  });
13680
13694
  }
13681
13695
  // A missing binary is reported ONCE, and again only when the answer CHANGES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabane/companion",
3
- "version": "0.6.154",
3
+ "version": "0.6.155",
4
4
  "type": "module",
5
5
  "description": "The Cabane Companion (headless): connect a coding agent on your machine to your Cabane workspace as a responder — drive work against your own codebase, files, and MCP servers without putting any of it in Cabane.",
6
6
  "license": "UNLICENSED",