@cabane/companion 0.6.126 → 0.6.127
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 +83 -32
- package/dist/runtime.js +83 -32
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13810,6 +13810,15 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
13810
13810
|
function containerName(attempt) {
|
|
13811
13811
|
return `cabane-runner-${attempt.conversationId}-${attempt.agentId}`;
|
|
13812
13812
|
}
|
|
13813
|
+
var networkName = containerName;
|
|
13814
|
+
function networkCreateError(stderr) {
|
|
13815
|
+
const said = stderr.trim();
|
|
13816
|
+
if (/address pool|fully subnetted/i.test(said))
|
|
13817
|
+
return new Error(
|
|
13818
|
+
`docker: couldn't create this conversation's runner network: the engine has no address pool left for another network. Each conversation and agent with a runner container holds one; remove kept runner containers, or widen \`default-address-pools\` in the daemon.json. Docker said: ${said}`
|
|
13819
|
+
);
|
|
13820
|
+
return new Error(`docker: creating the runner network failed: ${said || "no reason given"}`);
|
|
13821
|
+
}
|
|
13813
13822
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
13814
13823
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
13815
13824
|
return join17(deviceDir, "attempts", attemptId, "session.tar");
|
|
@@ -13886,7 +13895,7 @@ function createDockerProvider(opts) {
|
|
|
13886
13895
|
const killGraceMs = opts.killGraceMs ?? DEFAULT_KILL_GRACE_MS2;
|
|
13887
13896
|
const sessionCap = opts.sessionCapBytes ?? DEFAULT_SESSION_CAP_BYTES;
|
|
13888
13897
|
const collectCap = opts.collectCapBytes ?? COLLECT_CAP_BYTES;
|
|
13889
|
-
const
|
|
13898
|
+
const sharedNetwork = `cabane-runners-${opts.deviceKey}`;
|
|
13890
13899
|
const companionLabel = createHash4("sha256").update(opts.companion.key).digest("hex").slice(0, 16);
|
|
13891
13900
|
const proxy = `cabane-egress-${opts.deviceKey}`;
|
|
13892
13901
|
const held = /* @__PURE__ */ new Map();
|
|
@@ -13919,32 +13928,14 @@ function createDockerProvider(opts) {
|
|
|
13919
13928
|
}
|
|
13920
13929
|
throw new Error("docker: the egress proxy never started listening");
|
|
13921
13930
|
};
|
|
13922
|
-
let
|
|
13931
|
+
let proxyReady = null;
|
|
13923
13932
|
const proxyKey = async () => {
|
|
13924
13933
|
const id = (await docker.run(["image", "inspect", "-f", "{{.Id}}", opts.image])).stdout.trim();
|
|
13925
13934
|
return createHash4("sha256").update(JSON.stringify([id || opts.image, opts.companion.key, [...opts.egressAllow].sort()])).digest("hex").slice(0, 16);
|
|
13926
13935
|
};
|
|
13927
|
-
const
|
|
13928
|
-
|
|
13936
|
+
const ensureProxy = () => {
|
|
13937
|
+
proxyReady ??= (async () => {
|
|
13929
13938
|
const allowKey = await proxyKey();
|
|
13930
|
-
if ((await docker.run(["network", "inspect", network])).code !== 0) {
|
|
13931
|
-
await must(
|
|
13932
|
-
[
|
|
13933
|
-
"network",
|
|
13934
|
-
"create",
|
|
13935
|
-
// No route off the host, and no address of the host's on the
|
|
13936
|
-
// bridge: the host's own ports are out of reach too. The proxy is
|
|
13937
|
-
// the only other thing on the network.
|
|
13938
|
-
"--internal",
|
|
13939
|
-
"-o",
|
|
13940
|
-
"com.docker.network.bridge.inhibit_ipv4=true",
|
|
13941
|
-
"--label",
|
|
13942
|
-
`cabane.device=${opts.deviceKey}`,
|
|
13943
|
-
network
|
|
13944
|
-
],
|
|
13945
|
-
"creating the runners network"
|
|
13946
|
-
);
|
|
13947
|
-
}
|
|
13948
13939
|
const seen = await docker.run([
|
|
13949
13940
|
"inspect",
|
|
13950
13941
|
"-f",
|
|
@@ -13953,6 +13944,7 @@ function createDockerProvider(opts) {
|
|
|
13953
13944
|
]);
|
|
13954
13945
|
const [key, running] = seen.stdout.trim().split(" ");
|
|
13955
13946
|
if (seen.code === 0 && key === allowKey) {
|
|
13947
|
+
await retireSharedNetwork();
|
|
13956
13948
|
if (running !== "true") {
|
|
13957
13949
|
await must(["start", proxy], "starting the egress proxy");
|
|
13958
13950
|
await proxyListening();
|
|
@@ -13987,17 +13979,61 @@ function createDockerProvider(opts) {
|
|
|
13987
13979
|
],
|
|
13988
13980
|
"starting the egress proxy"
|
|
13989
13981
|
);
|
|
13990
|
-
await
|
|
13991
|
-
["network", "connect", "--alias", PROXY_ALIAS, network, proxy],
|
|
13992
|
-
"connecting the proxy"
|
|
13993
|
-
);
|
|
13982
|
+
await retireSharedNetwork();
|
|
13994
13983
|
await proxyListening();
|
|
13995
|
-
log.info({
|
|
13984
|
+
log.info({ proxy, allow: opts.egressAllow }, "runners: egress proxy ready");
|
|
13996
13985
|
})().catch((err) => {
|
|
13997
|
-
|
|
13986
|
+
proxyReady = null;
|
|
13998
13987
|
throw err;
|
|
13999
13988
|
});
|
|
14000
|
-
return
|
|
13989
|
+
return proxyReady;
|
|
13990
|
+
};
|
|
13991
|
+
const retireSharedNetwork = async () => {
|
|
13992
|
+
if ((await docker.run(["network", "inspect", sharedNetwork])).code !== 0) return;
|
|
13993
|
+
await docker.run(["network", "disconnect", "-f", sharedNetwork, proxy]);
|
|
13994
|
+
const on = await docker.run(["ps", "-aq", "--filter", `network=${sharedNetwork}`]);
|
|
13995
|
+
if (on.code === 0 && !on.stdout.trim()) await docker.run(["network", "rm", sharedNetwork]);
|
|
13996
|
+
};
|
|
13997
|
+
const networksOf = async (container) => (await docker.run([
|
|
13998
|
+
"inspect",
|
|
13999
|
+
"-f",
|
|
14000
|
+
"{{range $k, $_ := .NetworkSettings.Networks}}{{$k}} {{end}}",
|
|
14001
|
+
container
|
|
14002
|
+
])).stdout.split(/\s+/).filter(Boolean);
|
|
14003
|
+
const ensurePairNetwork = async (net, attempt) => {
|
|
14004
|
+
if ((await docker.run(["network", "inspect", net])).code !== 0) {
|
|
14005
|
+
const made = await docker.run([
|
|
14006
|
+
"network",
|
|
14007
|
+
"create",
|
|
14008
|
+
// No route off the host, and no address of the host's on the bridge:
|
|
14009
|
+
// the host's own ports are out of reach too. The proxy is the only
|
|
14010
|
+
// other thing on the network.
|
|
14011
|
+
"--internal",
|
|
14012
|
+
"-o",
|
|
14013
|
+
"com.docker.network.bridge.inhibit_ipv4=true",
|
|
14014
|
+
"--label",
|
|
14015
|
+
`cabane.device=${opts.deviceKey}`,
|
|
14016
|
+
"--label",
|
|
14017
|
+
`cabane.conversation=${attempt.conversationId}`,
|
|
14018
|
+
"--label",
|
|
14019
|
+
`cabane.agent=${attempt.agentId}`,
|
|
14020
|
+
net
|
|
14021
|
+
]);
|
|
14022
|
+
if (made.code !== 0) throw networkCreateError(made.stderr);
|
|
14023
|
+
}
|
|
14024
|
+
if (!(await networksOf(proxy)).includes(net))
|
|
14025
|
+
await must(
|
|
14026
|
+
["network", "connect", "--alias", PROXY_ALIAS, net, proxy],
|
|
14027
|
+
"connecting the proxy"
|
|
14028
|
+
);
|
|
14029
|
+
};
|
|
14030
|
+
const settleNetworks = async (name, net) => {
|
|
14031
|
+
const on = await networksOf(name);
|
|
14032
|
+
if (!on.includes(net)) await must(["network", "connect", net, name], "moving the container");
|
|
14033
|
+
const others = on.filter((n) => n !== net);
|
|
14034
|
+
for (const other of others)
|
|
14035
|
+
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
14036
|
+
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
14001
14037
|
};
|
|
14002
14038
|
const socketRoot = opts.socketRoot ?? join17(
|
|
14003
14039
|
process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir2(),
|
|
@@ -14029,7 +14065,7 @@ function createDockerProvider(opts) {
|
|
|
14029
14065
|
"--label",
|
|
14030
14066
|
`cabane.companion=${companionLabel}`,
|
|
14031
14067
|
"--network",
|
|
14032
|
-
|
|
14068
|
+
networkName(attempt),
|
|
14033
14069
|
"--mount",
|
|
14034
14070
|
`type=volume,src=${name}-home,dst=${CONTAINER_HOME}`,
|
|
14035
14071
|
"--mount",
|
|
@@ -14055,6 +14091,17 @@ function createDockerProvider(opts) {
|
|
|
14055
14091
|
await docker.run(["rm", "-f", "-v", name]);
|
|
14056
14092
|
await docker.run(["volume", "rm", "-f", `${name}-home`, `${name}-work`]);
|
|
14057
14093
|
rmSync9(socketDir(name), { recursive: true, force: true });
|
|
14094
|
+
await dropNetwork(name);
|
|
14095
|
+
};
|
|
14096
|
+
const dropNetwork = async (name) => {
|
|
14097
|
+
if ((await docker.run(["network", "inspect", name])).code !== 0) return;
|
|
14098
|
+
await docker.run(["network", "disconnect", "-f", name, proxy]);
|
|
14099
|
+
const gone = await docker.run(["network", "rm", name]);
|
|
14100
|
+
if (gone.code !== 0)
|
|
14101
|
+
log.warn(
|
|
14102
|
+
{ network: name, err: gone.stderr.trim() },
|
|
14103
|
+
"runners: couldn't remove a pair's network"
|
|
14104
|
+
);
|
|
14058
14105
|
};
|
|
14059
14106
|
const containerEnv = () => {
|
|
14060
14107
|
const proxyUrl = `http://${PROXY_ALIAS}:${PROXY_PORT}`;
|
|
@@ -14221,7 +14268,7 @@ function createDockerProvider(opts) {
|
|
|
14221
14268
|
});
|
|
14222
14269
|
await before;
|
|
14223
14270
|
try {
|
|
14224
|
-
await
|
|
14271
|
+
await ensureProxy();
|
|
14225
14272
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
14226
14273
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
14227
14274
|
let seen = await docker.run([
|
|
@@ -14251,9 +14298,12 @@ function createDockerProvider(opts) {
|
|
|
14251
14298
|
);
|
|
14252
14299
|
seen = { code: 1, stdout: "", stderr: "" };
|
|
14253
14300
|
}
|
|
14301
|
+
await ensurePairNetwork(networkName(attempt), attempt);
|
|
14254
14302
|
if (seen.code !== 0) {
|
|
14255
14303
|
await create(name, attempt);
|
|
14256
14304
|
log.debug({ container: name }, "runners: container created");
|
|
14305
|
+
} else {
|
|
14306
|
+
await settleNetworks(name, networkName(attempt));
|
|
14257
14307
|
}
|
|
14258
14308
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
14259
14309
|
await must(["start", name], "starting the container");
|
|
@@ -14267,7 +14317,8 @@ function createDockerProvider(opts) {
|
|
|
14267
14317
|
return { provider: "docker", id: name };
|
|
14268
14318
|
} catch (err) {
|
|
14269
14319
|
const keep = opts.keepContainers || readPending(name).length > 0;
|
|
14270
|
-
|
|
14320
|
+
const kept = keep && (await docker.run(["inspect", "-f", "{{.Id}}", name])).code === 0;
|
|
14321
|
+
await (kept ? docker.run(["stop", "-t", "1", name]) : keep ? dropNetwork(networkName(attempt)) : destroy(name)).catch(() => {
|
|
14271
14322
|
});
|
|
14272
14323
|
release();
|
|
14273
14324
|
throw err;
|
package/dist/runtime.js
CHANGED
|
@@ -11527,6 +11527,15 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
11527
11527
|
function containerName(attempt) {
|
|
11528
11528
|
return `cabane-runner-${attempt.conversationId}-${attempt.agentId}`;
|
|
11529
11529
|
}
|
|
11530
|
+
var networkName = containerName;
|
|
11531
|
+
function networkCreateError(stderr) {
|
|
11532
|
+
const said = stderr.trim();
|
|
11533
|
+
if (/address pool|fully subnetted/i.test(said))
|
|
11534
|
+
return new Error(
|
|
11535
|
+
`docker: couldn't create this conversation's runner network: the engine has no address pool left for another network. Each conversation and agent with a runner container holds one; remove kept runner containers, or widen \`default-address-pools\` in the daemon.json. Docker said: ${said}`
|
|
11536
|
+
);
|
|
11537
|
+
return new Error(`docker: creating the runner network failed: ${said || "no reason given"}`);
|
|
11538
|
+
}
|
|
11530
11539
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
11531
11540
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
11532
11541
|
return join17(deviceDir, "attempts", attemptId, "session.tar");
|
|
@@ -11603,7 +11612,7 @@ function createDockerProvider(opts) {
|
|
|
11603
11612
|
const killGraceMs = opts.killGraceMs ?? DEFAULT_KILL_GRACE_MS2;
|
|
11604
11613
|
const sessionCap = opts.sessionCapBytes ?? DEFAULT_SESSION_CAP_BYTES;
|
|
11605
11614
|
const collectCap = opts.collectCapBytes ?? COLLECT_CAP_BYTES;
|
|
11606
|
-
const
|
|
11615
|
+
const sharedNetwork = `cabane-runners-${opts.deviceKey}`;
|
|
11607
11616
|
const companionLabel = createHash4("sha256").update(opts.companion.key).digest("hex").slice(0, 16);
|
|
11608
11617
|
const proxy = `cabane-egress-${opts.deviceKey}`;
|
|
11609
11618
|
const held = /* @__PURE__ */ new Map();
|
|
@@ -11636,32 +11645,14 @@ function createDockerProvider(opts) {
|
|
|
11636
11645
|
}
|
|
11637
11646
|
throw new Error("docker: the egress proxy never started listening");
|
|
11638
11647
|
};
|
|
11639
|
-
let
|
|
11648
|
+
let proxyReady = null;
|
|
11640
11649
|
const proxyKey = async () => {
|
|
11641
11650
|
const id = (await docker.run(["image", "inspect", "-f", "{{.Id}}", opts.image])).stdout.trim();
|
|
11642
11651
|
return createHash4("sha256").update(JSON.stringify([id || opts.image, opts.companion.key, [...opts.egressAllow].sort()])).digest("hex").slice(0, 16);
|
|
11643
11652
|
};
|
|
11644
|
-
const
|
|
11645
|
-
|
|
11653
|
+
const ensureProxy = () => {
|
|
11654
|
+
proxyReady ??= (async () => {
|
|
11646
11655
|
const allowKey = await proxyKey();
|
|
11647
|
-
if ((await docker.run(["network", "inspect", network])).code !== 0) {
|
|
11648
|
-
await must(
|
|
11649
|
-
[
|
|
11650
|
-
"network",
|
|
11651
|
-
"create",
|
|
11652
|
-
// No route off the host, and no address of the host's on the
|
|
11653
|
-
// bridge: the host's own ports are out of reach too. The proxy is
|
|
11654
|
-
// the only other thing on the network.
|
|
11655
|
-
"--internal",
|
|
11656
|
-
"-o",
|
|
11657
|
-
"com.docker.network.bridge.inhibit_ipv4=true",
|
|
11658
|
-
"--label",
|
|
11659
|
-
`cabane.device=${opts.deviceKey}`,
|
|
11660
|
-
network
|
|
11661
|
-
],
|
|
11662
|
-
"creating the runners network"
|
|
11663
|
-
);
|
|
11664
|
-
}
|
|
11665
11656
|
const seen = await docker.run([
|
|
11666
11657
|
"inspect",
|
|
11667
11658
|
"-f",
|
|
@@ -11670,6 +11661,7 @@ function createDockerProvider(opts) {
|
|
|
11670
11661
|
]);
|
|
11671
11662
|
const [key, running] = seen.stdout.trim().split(" ");
|
|
11672
11663
|
if (seen.code === 0 && key === allowKey) {
|
|
11664
|
+
await retireSharedNetwork();
|
|
11673
11665
|
if (running !== "true") {
|
|
11674
11666
|
await must(["start", proxy], "starting the egress proxy");
|
|
11675
11667
|
await proxyListening();
|
|
@@ -11704,17 +11696,61 @@ function createDockerProvider(opts) {
|
|
|
11704
11696
|
],
|
|
11705
11697
|
"starting the egress proxy"
|
|
11706
11698
|
);
|
|
11707
|
-
await
|
|
11708
|
-
["network", "connect", "--alias", PROXY_ALIAS, network, proxy],
|
|
11709
|
-
"connecting the proxy"
|
|
11710
|
-
);
|
|
11699
|
+
await retireSharedNetwork();
|
|
11711
11700
|
await proxyListening();
|
|
11712
|
-
log.info({
|
|
11701
|
+
log.info({ proxy, allow: opts.egressAllow }, "runners: egress proxy ready");
|
|
11713
11702
|
})().catch((err) => {
|
|
11714
|
-
|
|
11703
|
+
proxyReady = null;
|
|
11715
11704
|
throw err;
|
|
11716
11705
|
});
|
|
11717
|
-
return
|
|
11706
|
+
return proxyReady;
|
|
11707
|
+
};
|
|
11708
|
+
const retireSharedNetwork = async () => {
|
|
11709
|
+
if ((await docker.run(["network", "inspect", sharedNetwork])).code !== 0) return;
|
|
11710
|
+
await docker.run(["network", "disconnect", "-f", sharedNetwork, proxy]);
|
|
11711
|
+
const on = await docker.run(["ps", "-aq", "--filter", `network=${sharedNetwork}`]);
|
|
11712
|
+
if (on.code === 0 && !on.stdout.trim()) await docker.run(["network", "rm", sharedNetwork]);
|
|
11713
|
+
};
|
|
11714
|
+
const networksOf = async (container) => (await docker.run([
|
|
11715
|
+
"inspect",
|
|
11716
|
+
"-f",
|
|
11717
|
+
"{{range $k, $_ := .NetworkSettings.Networks}}{{$k}} {{end}}",
|
|
11718
|
+
container
|
|
11719
|
+
])).stdout.split(/\s+/).filter(Boolean);
|
|
11720
|
+
const ensurePairNetwork = async (net, attempt) => {
|
|
11721
|
+
if ((await docker.run(["network", "inspect", net])).code !== 0) {
|
|
11722
|
+
const made = await docker.run([
|
|
11723
|
+
"network",
|
|
11724
|
+
"create",
|
|
11725
|
+
// No route off the host, and no address of the host's on the bridge:
|
|
11726
|
+
// the host's own ports are out of reach too. The proxy is the only
|
|
11727
|
+
// other thing on the network.
|
|
11728
|
+
"--internal",
|
|
11729
|
+
"-o",
|
|
11730
|
+
"com.docker.network.bridge.inhibit_ipv4=true",
|
|
11731
|
+
"--label",
|
|
11732
|
+
`cabane.device=${opts.deviceKey}`,
|
|
11733
|
+
"--label",
|
|
11734
|
+
`cabane.conversation=${attempt.conversationId}`,
|
|
11735
|
+
"--label",
|
|
11736
|
+
`cabane.agent=${attempt.agentId}`,
|
|
11737
|
+
net
|
|
11738
|
+
]);
|
|
11739
|
+
if (made.code !== 0) throw networkCreateError(made.stderr);
|
|
11740
|
+
}
|
|
11741
|
+
if (!(await networksOf(proxy)).includes(net))
|
|
11742
|
+
await must(
|
|
11743
|
+
["network", "connect", "--alias", PROXY_ALIAS, net, proxy],
|
|
11744
|
+
"connecting the proxy"
|
|
11745
|
+
);
|
|
11746
|
+
};
|
|
11747
|
+
const settleNetworks = async (name, net) => {
|
|
11748
|
+
const on = await networksOf(name);
|
|
11749
|
+
if (!on.includes(net)) await must(["network", "connect", net, name], "moving the container");
|
|
11750
|
+
const others = on.filter((n) => n !== net);
|
|
11751
|
+
for (const other of others)
|
|
11752
|
+
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
11753
|
+
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
11718
11754
|
};
|
|
11719
11755
|
const socketRoot = opts.socketRoot ?? join17(
|
|
11720
11756
|
process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir2(),
|
|
@@ -11746,7 +11782,7 @@ function createDockerProvider(opts) {
|
|
|
11746
11782
|
"--label",
|
|
11747
11783
|
`cabane.companion=${companionLabel}`,
|
|
11748
11784
|
"--network",
|
|
11749
|
-
|
|
11785
|
+
networkName(attempt),
|
|
11750
11786
|
"--mount",
|
|
11751
11787
|
`type=volume,src=${name}-home,dst=${CONTAINER_HOME}`,
|
|
11752
11788
|
"--mount",
|
|
@@ -11772,6 +11808,17 @@ function createDockerProvider(opts) {
|
|
|
11772
11808
|
await docker.run(["rm", "-f", "-v", name]);
|
|
11773
11809
|
await docker.run(["volume", "rm", "-f", `${name}-home`, `${name}-work`]);
|
|
11774
11810
|
rmSync9(socketDir(name), { recursive: true, force: true });
|
|
11811
|
+
await dropNetwork(name);
|
|
11812
|
+
};
|
|
11813
|
+
const dropNetwork = async (name) => {
|
|
11814
|
+
if ((await docker.run(["network", "inspect", name])).code !== 0) return;
|
|
11815
|
+
await docker.run(["network", "disconnect", "-f", name, proxy]);
|
|
11816
|
+
const gone = await docker.run(["network", "rm", name]);
|
|
11817
|
+
if (gone.code !== 0)
|
|
11818
|
+
log.warn(
|
|
11819
|
+
{ network: name, err: gone.stderr.trim() },
|
|
11820
|
+
"runners: couldn't remove a pair's network"
|
|
11821
|
+
);
|
|
11775
11822
|
};
|
|
11776
11823
|
const containerEnv = () => {
|
|
11777
11824
|
const proxyUrl = `http://${PROXY_ALIAS}:${PROXY_PORT}`;
|
|
@@ -11938,7 +11985,7 @@ function createDockerProvider(opts) {
|
|
|
11938
11985
|
});
|
|
11939
11986
|
await before;
|
|
11940
11987
|
try {
|
|
11941
|
-
await
|
|
11988
|
+
await ensureProxy();
|
|
11942
11989
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
11943
11990
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
11944
11991
|
let seen = await docker.run([
|
|
@@ -11968,9 +12015,12 @@ function createDockerProvider(opts) {
|
|
|
11968
12015
|
);
|
|
11969
12016
|
seen = { code: 1, stdout: "", stderr: "" };
|
|
11970
12017
|
}
|
|
12018
|
+
await ensurePairNetwork(networkName(attempt), attempt);
|
|
11971
12019
|
if (seen.code !== 0) {
|
|
11972
12020
|
await create(name, attempt);
|
|
11973
12021
|
log.debug({ container: name }, "runners: container created");
|
|
12022
|
+
} else {
|
|
12023
|
+
await settleNetworks(name, networkName(attempt));
|
|
11974
12024
|
}
|
|
11975
12025
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
11976
12026
|
await must(["start", name], "starting the container");
|
|
@@ -11984,7 +12034,8 @@ function createDockerProvider(opts) {
|
|
|
11984
12034
|
return { provider: "docker", id: name };
|
|
11985
12035
|
} catch (err) {
|
|
11986
12036
|
const keep = opts.keepContainers || readPending(name).length > 0;
|
|
11987
|
-
|
|
12037
|
+
const kept = keep && (await docker.run(["inspect", "-f", "{{.Id}}", name])).code === 0;
|
|
12038
|
+
await (kept ? docker.run(["stop", "-t", "1", name]) : keep ? dropNetwork(networkName(attempt)) : destroy(name)).catch(() => {
|
|
11988
12039
|
});
|
|
11989
12040
|
release();
|
|
11990
12041
|
throw err;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.127",
|
|
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",
|