@cabane/companion 0.6.192 → 0.6.193
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 +303 -179
- package/dist/runtime.js +166 -90
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -7296,8 +7296,8 @@ var ConnectorHealthStore = class {
|
|
|
7296
7296
|
|
|
7297
7297
|
// src/dispatcher.ts
|
|
7298
7298
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7299
|
-
import { existsSync as
|
|
7300
|
-
import { join as
|
|
7299
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
7300
|
+
import { join as join21 } from "path";
|
|
7301
7301
|
|
|
7302
7302
|
// src/runner/launch.ts
|
|
7303
7303
|
import { createInterface } from "readline";
|
|
@@ -7487,7 +7487,8 @@ var dispatchSchema = z14.object({
|
|
|
7487
7487
|
});
|
|
7488
7488
|
var credentialsSchema = z14.object({
|
|
7489
7489
|
credential: z14.string().startsWith("cabrn_"),
|
|
7490
|
-
secrets: z14.record(z14.string(), z14.string()).optional()
|
|
7490
|
+
secrets: z14.record(z14.string(), z14.string()).optional(),
|
|
7491
|
+
grants: z14.record(z14.string(), z14.record(z14.string(), z14.string())).optional()
|
|
7491
7492
|
});
|
|
7492
7493
|
function runnerEnv(input) {
|
|
7493
7494
|
return {
|
|
@@ -7501,7 +7502,8 @@ function runnerEnv(input) {
|
|
|
7501
7502
|
function runnerStdin(input) {
|
|
7502
7503
|
return `${JSON.stringify({
|
|
7503
7504
|
credential: input.credential,
|
|
7504
|
-
...input.secrets ? { secrets: input.secrets } : {}
|
|
7505
|
+
...input.secrets ? { secrets: input.secrets } : {},
|
|
7506
|
+
...input.grants ? { grants: input.grants } : {}
|
|
7505
7507
|
})}
|
|
7506
7508
|
`;
|
|
7507
7509
|
}
|
|
@@ -7659,7 +7661,12 @@ async function mintForRunner(opts, payload, handleOpts, signal, log) {
|
|
|
7659
7661
|
// The one turn the credential will operate: this run's.
|
|
7660
7662
|
turnId: handleOpts.turnId
|
|
7661
7663
|
});
|
|
7662
|
-
return {
|
|
7664
|
+
return {
|
|
7665
|
+
ok: true,
|
|
7666
|
+
credential: minted.credential,
|
|
7667
|
+
runnerId: minted.runnerId,
|
|
7668
|
+
deviceGrants: minted.deviceGrants ?? []
|
|
7669
|
+
};
|
|
7663
7670
|
} catch (err) {
|
|
7664
7671
|
if (signal.aborted) return { ok: false, reason: "cancelled" };
|
|
7665
7672
|
const status = err instanceof ApiError ? err.status : 0;
|
|
@@ -7754,10 +7761,12 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
7754
7761
|
try {
|
|
7755
7762
|
const boxed = launch.boxed === true;
|
|
7756
7763
|
const env = runnerExecEnv(launch.env ?? process.env, boxed);
|
|
7764
|
+
const grants = minted.deviceGrants.length > 0 ? launch.grants?.(minted.deviceGrants) ?? {} : {};
|
|
7757
7765
|
const input = {
|
|
7758
7766
|
baseUrl: opts.baseUrl,
|
|
7759
7767
|
credential: minted.credential,
|
|
7760
7768
|
...boxed ? { secrets: launch.secrets?.() ?? {} } : {},
|
|
7769
|
+
...Object.keys(grants).length > 0 ? { grants } : {},
|
|
7761
7770
|
dispatch: payload,
|
|
7762
7771
|
turnId,
|
|
7763
7772
|
resumed: handleOpts.resumed,
|
|
@@ -7894,13 +7903,72 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
7894
7903
|
import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
|
|
7895
7904
|
import { mkdirSync as mkdirSync9 } from "fs";
|
|
7896
7905
|
|
|
7906
|
+
// src/grants.ts
|
|
7907
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
7908
|
+
import { join as join13 } from "path";
|
|
7909
|
+
import { z as z15 } from "zod";
|
|
7910
|
+
var VARIABLE_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
|
|
7911
|
+
function allowedGrantVariable(name) {
|
|
7912
|
+
return name.length <= 100 && VARIABLE_NAME_RE.test(name) && !name.startsWith("CABANE_");
|
|
7913
|
+
}
|
|
7914
|
+
function environmentSafe(value) {
|
|
7915
|
+
return !value.includes("\0");
|
|
7916
|
+
}
|
|
7917
|
+
function grantsPath() {
|
|
7918
|
+
return join13(cabaneDir(), "grants.json");
|
|
7919
|
+
}
|
|
7920
|
+
var grantsFileSchema = z15.record(z15.string(), z15.record(z15.string(), z15.string()));
|
|
7921
|
+
function readGrants(keys, report) {
|
|
7922
|
+
if (keys.length === 0) return {};
|
|
7923
|
+
const file = loadGrantsFile(report);
|
|
7924
|
+
const out = {};
|
|
7925
|
+
for (const key of keys) {
|
|
7926
|
+
if (!allowedGrantVariable(key) || !Object.hasOwn(file, key)) continue;
|
|
7927
|
+
const entry = {};
|
|
7928
|
+
for (const [variable, value] of Object.entries(file[key])) {
|
|
7929
|
+
if (allowedGrantVariable(variable) && environmentSafe(value)) entry[variable] = value;
|
|
7930
|
+
else report.refusedVariable(key, variable);
|
|
7931
|
+
}
|
|
7932
|
+
out[key] = entry;
|
|
7933
|
+
}
|
|
7934
|
+
return out;
|
|
7935
|
+
}
|
|
7936
|
+
function loadGrantsFile(report) {
|
|
7937
|
+
const path = grantsPath();
|
|
7938
|
+
if (!existsSync9(path)) return {};
|
|
7939
|
+
let raw;
|
|
7940
|
+
try {
|
|
7941
|
+
raw = readFileSync7(path, "utf8");
|
|
7942
|
+
} catch (err) {
|
|
7943
|
+
const code = err.code ?? "unknown error";
|
|
7944
|
+
report.unreadable(`couldn't read ${path}: ${code}`);
|
|
7945
|
+
return {};
|
|
7946
|
+
}
|
|
7947
|
+
if (raw.trim().length === 0) return {};
|
|
7948
|
+
let parsed;
|
|
7949
|
+
try {
|
|
7950
|
+
parsed = JSON.parse(raw);
|
|
7951
|
+
} catch {
|
|
7952
|
+
report.unreadable(`${path} is not valid JSON.`);
|
|
7953
|
+
return {};
|
|
7954
|
+
}
|
|
7955
|
+
const result = grantsFileSchema.safeParse(parsed);
|
|
7956
|
+
if (!result.success) {
|
|
7957
|
+
report.unreadable(
|
|
7958
|
+
`${path} must map a secret's variable name to an object of "VARIABLE": "value" pairs (e.g. { "HAS_PROD_ACCESS": { "RENDER_API_KEY": "rnd_\u2026" } }).`
|
|
7959
|
+
);
|
|
7960
|
+
return {};
|
|
7961
|
+
}
|
|
7962
|
+
return result.data;
|
|
7963
|
+
}
|
|
7964
|
+
|
|
7897
7965
|
// src/work-dir.ts
|
|
7898
7966
|
import { statSync as statSync3 } from "fs";
|
|
7899
|
-
import { join as
|
|
7967
|
+
import { join as join14 } from "path";
|
|
7900
7968
|
|
|
7901
7969
|
// src/turn-control-tools.ts
|
|
7902
7970
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
7903
|
-
import { z as
|
|
7971
|
+
import { z as z16 } from "zod";
|
|
7904
7972
|
var COMPANION_LOCAL_MCP_SERVER = "cabane_companion";
|
|
7905
7973
|
var COMPANION_LOCAL_TOOL_GLOB = `mcp__${COMPANION_LOCAL_MCP_SERVER}__*`;
|
|
7906
7974
|
var ASK_TOOL = "ask";
|
|
@@ -7915,19 +7983,19 @@ var CANCEL_WAKE_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${CANCEL_WAKE_T
|
|
|
7915
7983
|
// src/codex-config.ts
|
|
7916
7984
|
import { readFile as readFile2 } from "fs/promises";
|
|
7917
7985
|
import { homedir as homedir6 } from "os";
|
|
7918
|
-
import { join as
|
|
7986
|
+
import { join as join15 } from "path";
|
|
7919
7987
|
import { parse } from "smol-toml";
|
|
7920
7988
|
|
|
7921
7989
|
// src/codex-images.ts
|
|
7922
7990
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
7923
7991
|
import { tmpdir } from "os";
|
|
7924
|
-
import { join as
|
|
7992
|
+
import { join as join16 } from "path";
|
|
7925
7993
|
|
|
7926
7994
|
// src/codex-compactions.ts
|
|
7927
7995
|
import { createReadStream } from "fs";
|
|
7928
7996
|
import { readdir } from "fs/promises";
|
|
7929
7997
|
import { homedir as homedir7 } from "os";
|
|
7930
|
-
import { join as
|
|
7998
|
+
import { join as join17 } from "path";
|
|
7931
7999
|
import { createInterface as createInterface2 } from "readline";
|
|
7932
8000
|
|
|
7933
8001
|
// src/prepare-failure.ts
|
|
@@ -7935,24 +8003,24 @@ import { spawn as spawn3 } from "child_process";
|
|
|
7935
8003
|
import { hostname } from "os";
|
|
7936
8004
|
|
|
7937
8005
|
// src/prepared.ts
|
|
7938
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
7939
|
-
import { join as
|
|
8006
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "fs";
|
|
8007
|
+
import { join as join18 } from "path";
|
|
7940
8008
|
|
|
7941
8009
|
// src/secrets.ts
|
|
7942
|
-
import { existsSync as
|
|
7943
|
-
import { join as
|
|
7944
|
-
import { z as
|
|
8010
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
8011
|
+
import { join as join19 } from "path";
|
|
8012
|
+
import { z as z17 } from "zod";
|
|
7945
8013
|
var PLACEHOLDER_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
7946
8014
|
function secretsPath() {
|
|
7947
|
-
return
|
|
8015
|
+
return join19(cabaneDir(), "secrets.json");
|
|
7948
8016
|
}
|
|
7949
|
-
var secretStoreSchema =
|
|
8017
|
+
var secretStoreSchema = z17.record(z17.string(), z17.string());
|
|
7950
8018
|
function loadSecretValues() {
|
|
7951
8019
|
const path = secretsPath();
|
|
7952
|
-
if (!
|
|
8020
|
+
if (!existsSync11(path)) return {};
|
|
7953
8021
|
let raw;
|
|
7954
8022
|
try {
|
|
7955
|
-
raw =
|
|
8023
|
+
raw = readFileSync9(path, "utf8");
|
|
7956
8024
|
} catch (err) {
|
|
7957
8025
|
throw new ConfigError(
|
|
7958
8026
|
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8134,9 +8202,9 @@ function runBounded(path, args, env) {
|
|
|
8134
8202
|
|
|
8135
8203
|
// src/transcript-writer.ts
|
|
8136
8204
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
8137
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
8205
|
+
import { basename as basename3, dirname as dirname7, join as join20 } from "path";
|
|
8138
8206
|
function transcriptsDir() {
|
|
8139
|
-
return
|
|
8207
|
+
return join20(cabaneDir(), "transcripts");
|
|
8140
8208
|
}
|
|
8141
8209
|
|
|
8142
8210
|
// src/turn-execution.ts
|
|
@@ -8239,7 +8307,7 @@ var Dispatcher = class {
|
|
|
8239
8307
|
};
|
|
8240
8308
|
|
|
8241
8309
|
// src/host-report.ts
|
|
8242
|
-
import { existsSync as
|
|
8310
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
8243
8311
|
import { userInfo as userInfo2 } from "os";
|
|
8244
8312
|
function inContainer(signals) {
|
|
8245
8313
|
if (signals.dockerenv) return true;
|
|
@@ -8250,11 +8318,11 @@ function inContainer(signals) {
|
|
|
8250
8318
|
function readContainerSignals() {
|
|
8251
8319
|
let cgroup = null;
|
|
8252
8320
|
try {
|
|
8253
|
-
cgroup =
|
|
8321
|
+
cgroup = readFileSync10("/proc/1/cgroup", "utf8");
|
|
8254
8322
|
} catch {
|
|
8255
8323
|
cgroup = null;
|
|
8256
8324
|
}
|
|
8257
|
-
return { dockerenv:
|
|
8325
|
+
return { dockerenv: existsSync13("/.dockerenv"), cgroup, env: process.env };
|
|
8258
8326
|
}
|
|
8259
8327
|
function osUser() {
|
|
8260
8328
|
try {
|
|
@@ -8491,7 +8559,7 @@ function checkReason(check) {
|
|
|
8491
8559
|
|
|
8492
8560
|
// src/harness-copy.ts
|
|
8493
8561
|
import { homedir as homedir8 } from "os";
|
|
8494
|
-
import { join as
|
|
8562
|
+
import { join as join22 } from "path";
|
|
8495
8563
|
var HARNESS_LABELS = {
|
|
8496
8564
|
"claude-code": "Claude Code",
|
|
8497
8565
|
codex: "Codex",
|
|
@@ -8505,7 +8573,7 @@ function failureText(runtime, reason) {
|
|
|
8505
8573
|
case "spawn":
|
|
8506
8574
|
return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
|
|
8507
8575
|
case "config":
|
|
8508
|
-
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(
|
|
8576
|
+
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(join22(homedir8(), ".codex", "config.toml"))})` : ""}. Fix it, then try again.`;
|
|
8509
8577
|
case "unreadable":
|
|
8510
8578
|
return `Couldn't check whether ${label} is signed in: it answered in a way Cabane couldn't read. Try again, and if it keeps happening, update Cabane.`;
|
|
8511
8579
|
}
|
|
@@ -8762,15 +8830,15 @@ var DispatchClaims = class {
|
|
|
8762
8830
|
|
|
8763
8831
|
// src/outbox.ts
|
|
8764
8832
|
import {
|
|
8765
|
-
existsSync as
|
|
8833
|
+
existsSync as existsSync14,
|
|
8766
8834
|
mkdirSync as mkdirSync10,
|
|
8767
8835
|
readdirSync as readdirSync3,
|
|
8768
|
-
readFileSync as
|
|
8836
|
+
readFileSync as readFileSync11,
|
|
8769
8837
|
renameSync as renameSync5,
|
|
8770
8838
|
rmSync as rmSync7,
|
|
8771
8839
|
writeFileSync as writeFileSync6
|
|
8772
8840
|
} from "fs";
|
|
8773
|
-
import { join as
|
|
8841
|
+
import { join as join23 } from "path";
|
|
8774
8842
|
var MAX_ENTRIES = 2e3;
|
|
8775
8843
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
8776
8844
|
var Outbox = class {
|
|
@@ -8783,10 +8851,10 @@ var Outbox = class {
|
|
|
8783
8851
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
8784
8852
|
// cases route writes at the right tmpdir.
|
|
8785
8853
|
dir() {
|
|
8786
|
-
return
|
|
8854
|
+
return join23(cabaneDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
8787
8855
|
}
|
|
8788
8856
|
fileFor(turnId, seq) {
|
|
8789
|
-
return
|
|
8857
|
+
return join23(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
8790
8858
|
}
|
|
8791
8859
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
8792
8860
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -8818,7 +8886,7 @@ var Outbox = class {
|
|
|
8818
8886
|
// wedging the drain.
|
|
8819
8887
|
list() {
|
|
8820
8888
|
const dir2 = this.dir();
|
|
8821
|
-
if (!
|
|
8889
|
+
if (!existsSync14(dir2)) return [];
|
|
8822
8890
|
let names;
|
|
8823
8891
|
try {
|
|
8824
8892
|
names = readdirSync3(dir2);
|
|
@@ -8828,9 +8896,9 @@ var Outbox = class {
|
|
|
8828
8896
|
const entries = [];
|
|
8829
8897
|
for (const name of names) {
|
|
8830
8898
|
if (!name.endsWith(".json")) continue;
|
|
8831
|
-
const full =
|
|
8899
|
+
const full = join23(dir2, name);
|
|
8832
8900
|
try {
|
|
8833
|
-
const parsed = JSON.parse(
|
|
8901
|
+
const parsed = JSON.parse(readFileSync11(full, "utf8"));
|
|
8834
8902
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
8835
8903
|
entries.push(parsed);
|
|
8836
8904
|
} else {
|
|
@@ -8864,7 +8932,7 @@ var Outbox = class {
|
|
|
8864
8932
|
// different: it is positive evidence that nothing was ever queued here.
|
|
8865
8933
|
maxSeqForTurn(turnId) {
|
|
8866
8934
|
const dir2 = this.dir();
|
|
8867
|
-
if (!
|
|
8935
|
+
if (!existsSync14(dir2)) return 0;
|
|
8868
8936
|
const names = readdirSync3(dir2);
|
|
8869
8937
|
const prefix = `${encodeURIComponent(turnId)}__`;
|
|
8870
8938
|
let max = 0;
|
|
@@ -8884,7 +8952,7 @@ var Outbox = class {
|
|
|
8884
8952
|
}
|
|
8885
8953
|
size() {
|
|
8886
8954
|
const dir2 = this.dir();
|
|
8887
|
-
if (!
|
|
8955
|
+
if (!existsSync14(dir2)) return 0;
|
|
8888
8956
|
try {
|
|
8889
8957
|
return readdirSync3(dir2).filter((n) => n.endsWith(".json")).length;
|
|
8890
8958
|
} catch {
|
|
@@ -9051,20 +9119,20 @@ var SseSubscriber = class {
|
|
|
9051
9119
|
|
|
9052
9120
|
// src/updater.ts
|
|
9053
9121
|
import { spawn as spawn6 } from "child_process";
|
|
9054
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
9055
|
-
import { dirname as dirname11, join as
|
|
9122
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync14, renameSync as renameSync9, rmSync as rmSync10 } from "fs";
|
|
9123
|
+
import { dirname as dirname11, join as join27 } from "path";
|
|
9056
9124
|
|
|
9057
9125
|
// src/boot-rule.ts
|
|
9058
9126
|
import {
|
|
9059
9127
|
mkdirSync as mkdirSync11,
|
|
9060
|
-
readFileSync as
|
|
9128
|
+
readFileSync as readFileSync12,
|
|
9061
9129
|
realpathSync as realpathSync4,
|
|
9062
9130
|
renameSync as renameSync6,
|
|
9063
9131
|
rmSync as rmSync8,
|
|
9064
9132
|
symlinkSync,
|
|
9065
9133
|
writeFileSync as writeFileSync7
|
|
9066
9134
|
} from "fs";
|
|
9067
|
-
import { dirname as dirname8, join as
|
|
9135
|
+
import { dirname as dirname8, join as join24 } from "path";
|
|
9068
9136
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
9069
9137
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
9070
9138
|
function superseded(pending, ...running) {
|
|
@@ -9087,36 +9155,36 @@ function rolledBackState(state, pending, reason, now) {
|
|
|
9087
9155
|
|
|
9088
9156
|
// src/install-layout.ts
|
|
9089
9157
|
import {
|
|
9090
|
-
existsSync as
|
|
9158
|
+
existsSync as existsSync15,
|
|
9091
9159
|
lstatSync,
|
|
9092
9160
|
mkdirSync as mkdirSync12,
|
|
9093
|
-
readFileSync as
|
|
9161
|
+
readFileSync as readFileSync13,
|
|
9094
9162
|
readdirSync as readdirSync4,
|
|
9095
9163
|
realpathSync as realpathSync5,
|
|
9096
9164
|
renameSync as renameSync7,
|
|
9097
9165
|
rmSync as rmSync9,
|
|
9098
9166
|
symlinkSync as symlinkSync2
|
|
9099
9167
|
} from "fs";
|
|
9100
|
-
import { dirname as dirname9, join as
|
|
9168
|
+
import { dirname as dirname9, join as join25 } from "path";
|
|
9101
9169
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
9102
9170
|
function versionsDir() {
|
|
9103
|
-
return
|
|
9171
|
+
return join25(cabaneDir(), "versions");
|
|
9104
9172
|
}
|
|
9105
9173
|
function currentLink() {
|
|
9106
|
-
return
|
|
9174
|
+
return join25(cabaneDir(), "current");
|
|
9107
9175
|
}
|
|
9108
9176
|
function bootGuardPath() {
|
|
9109
|
-
return
|
|
9177
|
+
return join25(cabaneDir(), "boot-guard.mjs");
|
|
9110
9178
|
}
|
|
9111
9179
|
function versionPackageRoot(version) {
|
|
9112
|
-
return
|
|
9180
|
+
return join25(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
9113
9181
|
}
|
|
9114
9182
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
9115
9183
|
return dirname9(dirname9(fileURLToPath5(moduleUrl)));
|
|
9116
9184
|
}
|
|
9117
9185
|
function packageVersion(root) {
|
|
9118
9186
|
try {
|
|
9119
|
-
const pkg2 = JSON.parse(
|
|
9187
|
+
const pkg2 = JSON.parse(readFileSync13(join25(root, "package.json"), "utf8"));
|
|
9120
9188
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
9121
9189
|
} catch {
|
|
9122
9190
|
return null;
|
|
@@ -9125,7 +9193,7 @@ function packageVersion(root) {
|
|
|
9125
9193
|
function currentRoot() {
|
|
9126
9194
|
try {
|
|
9127
9195
|
const root = realpathSync5(currentLink());
|
|
9128
|
-
return
|
|
9196
|
+
return existsSync15(join25(root, "dist", "cli.js")) ? root : null;
|
|
9129
9197
|
} catch {
|
|
9130
9198
|
return null;
|
|
9131
9199
|
}
|
|
@@ -9144,11 +9212,11 @@ function pointCurrentAt(root) {
|
|
|
9144
9212
|
}
|
|
9145
9213
|
function pruneVersions(keep) {
|
|
9146
9214
|
const dir2 = versionsDir();
|
|
9147
|
-
if (!
|
|
9215
|
+
if (!existsSync15(dir2)) return [];
|
|
9148
9216
|
const removed = [];
|
|
9149
9217
|
for (const name of readdirSync4(dir2)) {
|
|
9150
9218
|
if (keep.includes(name)) continue;
|
|
9151
|
-
rmSync9(
|
|
9219
|
+
rmSync9(join25(dir2, name), { recursive: true, force: true });
|
|
9152
9220
|
removed.push(name);
|
|
9153
9221
|
}
|
|
9154
9222
|
return removed;
|
|
@@ -9166,36 +9234,36 @@ function compareVersions(a, b) {
|
|
|
9166
9234
|
}
|
|
9167
9235
|
|
|
9168
9236
|
// src/update-state.ts
|
|
9169
|
-
import { mkdirSync as mkdirSync13, readFileSync as
|
|
9170
|
-
import { dirname as dirname10, join as
|
|
9171
|
-
import { z as
|
|
9172
|
-
var resultSchema =
|
|
9173
|
-
kind:
|
|
9174
|
-
from:
|
|
9175
|
-
to:
|
|
9176
|
-
at:
|
|
9177
|
-
reason:
|
|
9237
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, renameSync as renameSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
9238
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
9239
|
+
import { z as z18 } from "zod";
|
|
9240
|
+
var resultSchema = z18.object({
|
|
9241
|
+
kind: z18.enum(["updated", "rolled_back", "failed"]),
|
|
9242
|
+
from: z18.string(),
|
|
9243
|
+
to: z18.string(),
|
|
9244
|
+
at: z18.string(),
|
|
9245
|
+
reason: z18.string().optional()
|
|
9178
9246
|
});
|
|
9179
|
-
var pendingSchema =
|
|
9180
|
-
from:
|
|
9247
|
+
var pendingSchema = z18.object({
|
|
9248
|
+
from: z18.string(),
|
|
9181
9249
|
// The package root `current` pointed at before the swap, so a rollback can point it back.
|
|
9182
|
-
fromRoot:
|
|
9183
|
-
to:
|
|
9184
|
-
at:
|
|
9185
|
-
boots:
|
|
9250
|
+
fromRoot: z18.string(),
|
|
9251
|
+
to: z18.string(),
|
|
9252
|
+
at: z18.string(),
|
|
9253
|
+
boots: z18.number().int().nonnegative()
|
|
9186
9254
|
});
|
|
9187
|
-
var stateSchema =
|
|
9188
|
-
server:
|
|
9255
|
+
var stateSchema = z18.object({
|
|
9256
|
+
server: z18.object({ latest: z18.string(), minimum: z18.string(), at: z18.string() }).optional(),
|
|
9189
9257
|
pending: pendingSchema.optional(),
|
|
9190
9258
|
lastResult: resultSchema.optional(),
|
|
9191
|
-
failed:
|
|
9259
|
+
failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
|
|
9192
9260
|
});
|
|
9193
9261
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
9194
|
-
return
|
|
9262
|
+
return join26(dir2, "update.json");
|
|
9195
9263
|
}
|
|
9196
9264
|
function readUpdateState(path = updateStatePath()) {
|
|
9197
9265
|
try {
|
|
9198
|
-
const parsed = stateSchema.safeParse(JSON.parse(
|
|
9266
|
+
const parsed = stateSchema.safeParse(JSON.parse(readFileSync14(path, "utf8")));
|
|
9199
9267
|
return parsed.success ? parsed.data : {};
|
|
9200
9268
|
} catch {
|
|
9201
9269
|
return {};
|
|
@@ -9236,12 +9304,12 @@ function versionToStage(input) {
|
|
|
9236
9304
|
return latest;
|
|
9237
9305
|
}
|
|
9238
9306
|
function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
9239
|
-
if (
|
|
9307
|
+
if (existsSync16(pending.fromRoot)) pointCurrentAt(pending.fromRoot);
|
|
9240
9308
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
9241
9309
|
}
|
|
9242
9310
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
9243
|
-
const built =
|
|
9244
|
-
if (!
|
|
9311
|
+
const built = join27(root, "dist", "boot-guard.js");
|
|
9312
|
+
if (!existsSync16(built)) return false;
|
|
9245
9313
|
const target = bootGuardPath();
|
|
9246
9314
|
mkdirSync14(dirname11(target), { recursive: true });
|
|
9247
9315
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
@@ -9280,17 +9348,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve) => {
|
|
|
9280
9348
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
9281
9349
|
const run = deps.run ?? runProcess;
|
|
9282
9350
|
const node = deps.execPath ?? process.execPath;
|
|
9283
|
-
const target =
|
|
9351
|
+
const target = join27(versionsDir(), version);
|
|
9284
9352
|
const placed = versionPackageRoot(version);
|
|
9285
|
-
if (
|
|
9353
|
+
if (existsSync16(placed)) {
|
|
9286
9354
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
9287
9355
|
return { ok: true, root: placed };
|
|
9288
9356
|
}
|
|
9289
|
-
const npm =
|
|
9290
|
-
if (!
|
|
9357
|
+
const npm = join27(dirname11(node), "npm");
|
|
9358
|
+
if (!existsSync16(npm))
|
|
9291
9359
|
return { ok: false, reason: `there is no npm beside Node at ${dirname11(node)}` };
|
|
9292
9360
|
mkdirSync14(versionsDir(), { recursive: true });
|
|
9293
|
-
const staging =
|
|
9361
|
+
const staging = join27(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
9294
9362
|
rmSync10(staging, { recursive: true, force: true });
|
|
9295
9363
|
const installed = await run(
|
|
9296
9364
|
npm,
|
|
@@ -9319,7 +9387,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
9319
9387
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
9320
9388
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
9321
9389
|
}
|
|
9322
|
-
const root =
|
|
9390
|
+
const root = join27(staging, "node_modules", "@cabane", "companion");
|
|
9323
9391
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
9324
9392
|
if (verdict) {
|
|
9325
9393
|
rmSync10(staging, { recursive: true, force: true });
|
|
@@ -9331,7 +9399,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
9331
9399
|
}
|
|
9332
9400
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
9333
9401
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
9334
|
-
const answered = await deps.run(deps.execPath, [
|
|
9402
|
+
const answered = await deps.run(deps.execPath, [join27(root, "dist", "bin.js"), "--version"], {
|
|
9335
9403
|
timeoutMs: 6e4,
|
|
9336
9404
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
9337
9405
|
});
|
|
@@ -9339,7 +9407,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
9339
9407
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
9340
9408
|
}
|
|
9341
9409
|
if (harnesses.length === 0) return null;
|
|
9342
|
-
const probed = await deps.run(deps.execPath, [
|
|
9410
|
+
const probed = await deps.run(deps.execPath, [join27(root, "dist", "postinstall.js"), "--json"], {
|
|
9343
9411
|
timeoutMs: 6e4
|
|
9344
9412
|
});
|
|
9345
9413
|
let resolutions = {};
|
|
@@ -10335,6 +10403,14 @@ var CompanionSupervisor = class {
|
|
|
10335
10403
|
(m) => this.log.warn({ err: m }, "Could not read the secrets file for a boxed turn.")
|
|
10336
10404
|
)
|
|
10337
10405
|
} : {},
|
|
10406
|
+
// Read per dispatch, for the secret variables its mint named and no others.
|
|
10407
|
+
grants: (keys) => readGrants(keys, {
|
|
10408
|
+
refusedVariable: (key, variable) => this.log.warn(
|
|
10409
|
+
{ key, variable },
|
|
10410
|
+
"Skipped a variable in ~/.cabane/grants.json: its name must use capital letters, digits and underscores, not starting with CABANE_, and its value cannot contain a NUL character."
|
|
10411
|
+
),
|
|
10412
|
+
unreadable: (message) => this.log.warn({ err: message }, "Could not read the grants file.")
|
|
10413
|
+
}),
|
|
10338
10414
|
...this.runnerEnv ? { env: this.runnerEnv } : {},
|
|
10339
10415
|
reportRunnerGone: (report) => this.reportRunnerGone(report),
|
|
10340
10416
|
connectorHealth: this.connectorHealth,
|
|
@@ -11026,10 +11102,10 @@ function handleUncaught(log, err, origin) {
|
|
|
11026
11102
|
}
|
|
11027
11103
|
|
|
11028
11104
|
// src/crash-marker.ts
|
|
11029
|
-
import { existsSync as
|
|
11030
|
-
import { join as
|
|
11105
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
11106
|
+
import { join as join28 } from "path";
|
|
11031
11107
|
function crashMarkerPath() {
|
|
11032
|
-
return
|
|
11108
|
+
return join28(cabaneDir(), "last-error.json");
|
|
11033
11109
|
}
|
|
11034
11110
|
function recordCrash(rec) {
|
|
11035
11111
|
try {
|
|
@@ -11041,15 +11117,15 @@ function recordCrash(rec) {
|
|
|
11041
11117
|
function clearCrash() {
|
|
11042
11118
|
try {
|
|
11043
11119
|
const path = crashMarkerPath();
|
|
11044
|
-
if (
|
|
11120
|
+
if (existsSync17(path)) rmSync11(path, { force: true });
|
|
11045
11121
|
} catch {
|
|
11046
11122
|
}
|
|
11047
11123
|
}
|
|
11048
11124
|
|
|
11049
11125
|
// src/stray-companion.ts
|
|
11050
11126
|
import { spawnSync } from "child_process";
|
|
11051
|
-
import { readFileSync as
|
|
11052
|
-
import { join as
|
|
11127
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
11128
|
+
import { join as join29 } from "path";
|
|
11053
11129
|
var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
|
|
11054
11130
|
var START = /\sstart(\s|$)/;
|
|
11055
11131
|
function companionStarts(psOutput) {
|
|
@@ -11066,7 +11142,7 @@ function companionStarts(psOutput) {
|
|
|
11066
11142
|
}
|
|
11067
11143
|
function stateDirOf(env) {
|
|
11068
11144
|
const home = env.CABANE_COMPANION_HOME || env.HOME;
|
|
11069
|
-
return home ?
|
|
11145
|
+
return home ? join29(home, ".cabane") : null;
|
|
11070
11146
|
}
|
|
11071
11147
|
function envFromPsE(output, command) {
|
|
11072
11148
|
const line = output.replace(/\n$/, "");
|
|
@@ -11081,7 +11157,7 @@ function envFromPsE(output, command) {
|
|
|
11081
11157
|
function envFromProc(pid) {
|
|
11082
11158
|
try {
|
|
11083
11159
|
const env = {};
|
|
11084
|
-
for (const entry of
|
|
11160
|
+
for (const entry of readFileSync16(`/proc/${pid}/environ`, "utf8").split("\0")) {
|
|
11085
11161
|
const eq = entry.indexOf("=");
|
|
11086
11162
|
if (eq > 0) env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
11087
11163
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.193",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect an 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",
|