@aspectlylabs/mikka 0.0.156 → 0.0.158
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/cli.js +260 -253
- package/client.js +2 -2
- package/http-link.js +41 -4
- package/login.js +6 -2
- package/main.js +2 -2
- package/mikka-release-manifest.json +17 -17
- package/mikka.js +260 -253
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/paths.js +2 -2
- package/sbom.cdx.json +7 -7
package/cli.js
CHANGED
|
@@ -4480,7 +4480,7 @@ import { join as join4 } from "node:path";
|
|
|
4480
4480
|
// package.json
|
|
4481
4481
|
var package_default = {
|
|
4482
4482
|
name: "@mikka/daemon",
|
|
4483
|
-
version: "0.0.
|
|
4483
|
+
version: "0.0.158",
|
|
4484
4484
|
description: "Local Mikka daemon: runs agents through the user's own CLIs",
|
|
4485
4485
|
type: "module",
|
|
4486
4486
|
private: true,
|
|
@@ -4540,7 +4540,7 @@ var package_default = {
|
|
|
4540
4540
|
engines: {
|
|
4541
4541
|
node: ">=24"
|
|
4542
4542
|
},
|
|
4543
|
-
gitHead: "
|
|
4543
|
+
gitHead: "5011b7faac02b82a863d07eba4d7180d47fccc2b"
|
|
4544
4544
|
};
|
|
4545
4545
|
|
|
4546
4546
|
// src/paths.ts
|
|
@@ -4881,11 +4881,251 @@ function classifyWrite(worktree, target) {
|
|
|
4881
4881
|
return ok2 ? "write_in_worktree" : "write_outside_repo";
|
|
4882
4882
|
}
|
|
4883
4883
|
|
|
4884
|
+
// src/agents/run-sandbox-launcher.ts
|
|
4885
|
+
import { spawn } from "node:child_process";
|
|
4886
|
+
import { isIP } from "node:net";
|
|
4887
|
+
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
4888
|
+
import { fileURLToPath } from "node:url";
|
|
4889
|
+
import { NetworkConfigSchema } from "@anthropic-ai/sandbox-runtime";
|
|
4890
|
+
var MAX_PENDING_NETWORK = 128;
|
|
4891
|
+
var NETWORK_DECISION_MS = 5 * 6e4;
|
|
4892
|
+
function canonicalHost(host) {
|
|
4893
|
+
if (!host || /[\s\x00-\x1f\x7f%\\/@?#*]/u.test(host)) return;
|
|
4894
|
+
const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
4895
|
+
if (isIP(bare) === 6) return new URL(`http://[${bare}]`).hostname.slice(1, -1);
|
|
4896
|
+
if (bare.includes(":")) return;
|
|
4897
|
+
try {
|
|
4898
|
+
return new URL(`http://${bare}`).hostname.toLowerCase();
|
|
4899
|
+
} catch {
|
|
4900
|
+
return void 0;
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
function matchesRunSandboxDomain(request, domains) {
|
|
4904
|
+
const host = canonicalHost(request.host);
|
|
4905
|
+
if (!host || !Number.isInteger(request.port) || request.port < 1 || request.port > 65535) return false;
|
|
4906
|
+
return domains.some((entry) => {
|
|
4907
|
+
if (entry === "*") return true;
|
|
4908
|
+
const parts = /^(\[[^\]]+\]|[^:]+?)(?::([1-9][0-9]{0,4}))?$/u.exec(entry);
|
|
4909
|
+
if (!parts || parts[2] !== void 0 && (Number(parts[2]) > 65535 || Number(parts[2]) !== request.port)) return false;
|
|
4910
|
+
const wildcard = parts[1].startsWith("*.");
|
|
4911
|
+
const pattern = canonicalHost(wildcard ? parts[1].slice(2) : parts[1]);
|
|
4912
|
+
if (!pattern) return false;
|
|
4913
|
+
if (!wildcard) return host === pattern;
|
|
4914
|
+
return !isIP(host) && pattern.includes(".") && host.endsWith(`.${pattern}`);
|
|
4915
|
+
});
|
|
4916
|
+
}
|
|
4917
|
+
function signalGroup(pid, signal) {
|
|
4918
|
+
if (pid === void 0) return;
|
|
4919
|
+
try {
|
|
4920
|
+
process.kill(-pid, signal);
|
|
4921
|
+
} catch (error) {
|
|
4922
|
+
if (error.code !== "ESRCH") throw error;
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
function spawnRunSandbox(options) {
|
|
4926
|
+
if (process.platform !== "linux" && process.platform !== "darwin") {
|
|
4927
|
+
throw new Error("run sandbox launcher requires Linux or macOS process groups");
|
|
4928
|
+
}
|
|
4929
|
+
if (!options.command || options.command.includes("\0") || !isAbsolute2(options.cwd)) {
|
|
4930
|
+
throw new Error("run sandbox requires a command and absolute cwd");
|
|
4931
|
+
}
|
|
4932
|
+
if (options.authToken !== void 0 && (!options.authToken || Buffer.byteLength(options.authToken) > 1048576)) {
|
|
4933
|
+
throw new Error("invalid run sandbox auth pipe payload length");
|
|
4934
|
+
}
|
|
4935
|
+
const maximum = Object.freeze([...options.maxAllowDomains]);
|
|
4936
|
+
const modelApiDomains = Object.freeze([...options.modelApiDomains ?? []]);
|
|
4937
|
+
NetworkConfigSchema.parse({ allowedDomains: maximum.filter((entry) => entry !== "*"), deniedDomains: [] });
|
|
4938
|
+
NetworkConfigSchema.parse({ allowedDomains: [...modelApiDomains], deniedDomains: [] });
|
|
4939
|
+
const onNetwork = options.onNetwork;
|
|
4940
|
+
const abortSignal = options.signal;
|
|
4941
|
+
const launch = {
|
|
4942
|
+
type: "init",
|
|
4943
|
+
command: options.command,
|
|
4944
|
+
args: [...options.args],
|
|
4945
|
+
cwd: options.cwd,
|
|
4946
|
+
env: { ...options.env },
|
|
4947
|
+
policy: {
|
|
4948
|
+
allowWrite: [...options.policy.allowWrite],
|
|
4949
|
+
denyWrite: [...options.policy.denyWrite ?? []],
|
|
4950
|
+
denyRead: [...options.policy.denyRead ?? []]
|
|
4951
|
+
},
|
|
4952
|
+
maxAllowDomains: [...maximum],
|
|
4953
|
+
modelApiDomains: [...modelApiDomains],
|
|
4954
|
+
hasAuthToken: options.authToken !== void 0
|
|
4955
|
+
};
|
|
4956
|
+
const child = spawn(process.execPath, [fileURLToPath(new URL("./run-sandbox-worker.mjs", import.meta.url))], {
|
|
4957
|
+
// Runtime helpers use the trusted daemon's environment. CLI env arrives
|
|
4958
|
+
// over IPC and is passed explicitly by the worker, without inheritance.
|
|
4959
|
+
env: process.env,
|
|
4960
|
+
cwd: options.cwd,
|
|
4961
|
+
stdio: ["pipe", "pipe", "pipe", "pipe", "ipc"],
|
|
4962
|
+
detached: true
|
|
4963
|
+
});
|
|
4964
|
+
const lifetime = new AbortController();
|
|
4965
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4966
|
+
let stderr = "";
|
|
4967
|
+
let stopped = false;
|
|
4968
|
+
let gone = false;
|
|
4969
|
+
let outcome;
|
|
4970
|
+
let hardStop;
|
|
4971
|
+
let settle;
|
|
4972
|
+
const exited = new Promise((resolve13) => {
|
|
4973
|
+
settle = resolve13;
|
|
4974
|
+
});
|
|
4975
|
+
function send(message) {
|
|
4976
|
+
if (!child.connected || gone) return;
|
|
4977
|
+
child.send(message, (error) => {
|
|
4978
|
+
if (error) stop(0);
|
|
4979
|
+
});
|
|
4980
|
+
}
|
|
4981
|
+
function stop(graceMs = 3e3) {
|
|
4982
|
+
if (gone) return;
|
|
4983
|
+
if (!stopped) {
|
|
4984
|
+
stopped = true;
|
|
4985
|
+
lifetime.abort();
|
|
4986
|
+
for (const request of pending.values()) {
|
|
4987
|
+
clearTimeout(request.timer);
|
|
4988
|
+
request.controller.abort();
|
|
4989
|
+
}
|
|
4990
|
+
pending.clear();
|
|
4991
|
+
send({ type: "stop", graceMs });
|
|
4992
|
+
}
|
|
4993
|
+
if (!hardStop) hardStop = setTimeout(() => {
|
|
4994
|
+
stderr += "\nrun sandbox worker did not stop; terminating its process group";
|
|
4995
|
+
try {
|
|
4996
|
+
signalGroup(child.pid, "SIGKILL");
|
|
4997
|
+
} catch (error) {
|
|
4998
|
+
stderr += `
|
|
4999
|
+
run sandbox group termination failed: ${String(error)}`;
|
|
5000
|
+
}
|
|
5001
|
+
}, Math.max(0, Math.min(graceMs, 3e4)) + 5e3);
|
|
5002
|
+
}
|
|
5003
|
+
child.stderr.on("data", (data) => {
|
|
5004
|
+
stderr = (stderr + data.toString()).slice(-16e3);
|
|
5005
|
+
});
|
|
5006
|
+
child.stdin.on("error", () => void 0);
|
|
5007
|
+
child.on("message", (message) => {
|
|
5008
|
+
if (!message || typeof message !== "object") return;
|
|
5009
|
+
const value = message;
|
|
5010
|
+
if (value.type === "finished") {
|
|
5011
|
+
if ((value.code === null || Number.isInteger(value.code)) && (value.signal === null || typeof value.signal === "string")) {
|
|
5012
|
+
outcome = { code: value.code, signal: value.signal };
|
|
5013
|
+
}
|
|
5014
|
+
return;
|
|
5015
|
+
}
|
|
5016
|
+
if (value.type !== "network" || !Number.isSafeInteger(value.id) || value.id < 1 || pending.has(value.id)) return;
|
|
5017
|
+
const id4 = value.id;
|
|
5018
|
+
const request = {
|
|
5019
|
+
host: typeof value.host === "string" ? value.host : "",
|
|
5020
|
+
port: typeof value.port === "number" ? value.port : 0
|
|
5021
|
+
};
|
|
5022
|
+
if (typeof value.port !== "number" || stopped || lifetime.signal.aborted || pending.size >= MAX_PENDING_NETWORK || !matchesRunSandboxDomain(request, maximum)) {
|
|
5023
|
+
send({ type: "decision", id: id4, allow: false });
|
|
5024
|
+
return;
|
|
5025
|
+
}
|
|
5026
|
+
const controller = new AbortController();
|
|
5027
|
+
const timer = setTimeout(() => {
|
|
5028
|
+
if (!pending.delete(id4)) return;
|
|
5029
|
+
controller.abort();
|
|
5030
|
+
send({ type: "decision", id: id4, allow: false });
|
|
5031
|
+
}, NETWORK_DECISION_MS);
|
|
5032
|
+
pending.set(id4, { controller, timer });
|
|
5033
|
+
void Promise.resolve().then(() => {
|
|
5034
|
+
if (stopped || lifetime.signal.aborted) return false;
|
|
5035
|
+
return onNetwork(request, controller.signal);
|
|
5036
|
+
}).then((allow) => {
|
|
5037
|
+
if (!pending.delete(id4)) return;
|
|
5038
|
+
clearTimeout(timer);
|
|
5039
|
+
send({ type: "decision", id: id4, allow: allow === true && !stopped && !lifetime.signal.aborted && !controller.signal.aborted });
|
|
5040
|
+
}, () => {
|
|
5041
|
+
if (pending.delete(id4)) {
|
|
5042
|
+
clearTimeout(timer);
|
|
5043
|
+
send({ type: "decision", id: id4, allow: false });
|
|
5044
|
+
}
|
|
5045
|
+
});
|
|
5046
|
+
});
|
|
5047
|
+
function onParentExit() {
|
|
5048
|
+
try {
|
|
5049
|
+
signalGroup(child.pid, "SIGKILL");
|
|
5050
|
+
} catch {
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
process.once("exit", onParentExit);
|
|
5054
|
+
function finish(code, signal) {
|
|
5055
|
+
if (gone) return;
|
|
5056
|
+
gone = true;
|
|
5057
|
+
stopped = true;
|
|
5058
|
+
lifetime.abort();
|
|
5059
|
+
for (const request of pending.values()) {
|
|
5060
|
+
clearTimeout(request.timer);
|
|
5061
|
+
request.controller.abort();
|
|
5062
|
+
}
|
|
5063
|
+
pending.clear();
|
|
5064
|
+
clearTimeout(hardStop);
|
|
5065
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
5066
|
+
process.removeListener("exit", onParentExit);
|
|
5067
|
+
try {
|
|
5068
|
+
signalGroup(child.pid, "SIGKILL");
|
|
5069
|
+
} catch (error) {
|
|
5070
|
+
stderr += `
|
|
5071
|
+
run sandbox group cleanup failed: ${String(error)}`;
|
|
5072
|
+
}
|
|
5073
|
+
settle(outcome ?? { code, signal });
|
|
5074
|
+
}
|
|
5075
|
+
child.once("error", (error) => {
|
|
5076
|
+
stderr += `
|
|
5077
|
+
${error.message}`;
|
|
5078
|
+
stop(0);
|
|
5079
|
+
if (child.pid === void 0) finish(null, null);
|
|
5080
|
+
});
|
|
5081
|
+
child.once("exit", finish);
|
|
5082
|
+
child.once("disconnect", () => {
|
|
5083
|
+
if (!outcome) stop(0);
|
|
5084
|
+
});
|
|
5085
|
+
const authPipe = child.stdio[3];
|
|
5086
|
+
authPipe.on("error", () => stop(0));
|
|
5087
|
+
authPipe.end(options.authToken);
|
|
5088
|
+
function onAbort() {
|
|
5089
|
+
stop(0);
|
|
5090
|
+
}
|
|
5091
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
5092
|
+
child.kill = (signal = "SIGTERM") => {
|
|
5093
|
+
if (gone) return false;
|
|
5094
|
+
if (signal === 0) {
|
|
5095
|
+
try {
|
|
5096
|
+
process.kill(child.pid, 0);
|
|
5097
|
+
return true;
|
|
5098
|
+
} catch {
|
|
5099
|
+
return false;
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
stop(signal === "SIGKILL" ? 0 : 3e3);
|
|
5103
|
+
return true;
|
|
5104
|
+
};
|
|
5105
|
+
Object.defineProperty(child, "killed", { get: () => stopped });
|
|
5106
|
+
if (abortSignal?.aborted) stop(0);
|
|
5107
|
+
else send(launch);
|
|
5108
|
+
return {
|
|
5109
|
+
process: child,
|
|
5110
|
+
exited,
|
|
5111
|
+
stderrTail: () => stderr.slice(-4e3).trim(),
|
|
5112
|
+
async kill(graceMs = 3e3) {
|
|
5113
|
+
stop(graceMs);
|
|
5114
|
+
await exited;
|
|
5115
|
+
},
|
|
5116
|
+
async revoke() {
|
|
5117
|
+
stop(0);
|
|
5118
|
+
await exited;
|
|
5119
|
+
}
|
|
5120
|
+
};
|
|
5121
|
+
}
|
|
5122
|
+
|
|
4884
5123
|
// src/agents/tool-approval-policy.ts
|
|
4885
|
-
function toolApprovalDecision(request, policy, worktree, apps = []) {
|
|
5124
|
+
function toolApprovalDecision(request, policy, worktree, apps = [], runHosts = []) {
|
|
4886
5125
|
if (request.method === "sandbox/network" && policy) {
|
|
4887
5126
|
const value = request.input;
|
|
4888
5127
|
if (!value || typeof value.host !== "string" || /[*/:]/.test(value.host) || !networkDomainSchema.safeParse(value.host).success || !Number.isInteger(value.port) || Number(value.port) < 1 || Number(value.port) > 65535) return "deny";
|
|
5128
|
+
if (policy.permissions.network === "ask" && matchesRunSandboxDomain({ host: value.host, port: Number(value.port) }, [...policy.networkAllow, ...runHosts])) return "allow";
|
|
4889
5129
|
return policy.permissions.network;
|
|
4890
5130
|
}
|
|
4891
5131
|
if (request.method.startsWith("cli/") && policy && worktree) {
|
|
@@ -5248,7 +5488,13 @@ var HttpServerLink = class {
|
|
|
5248
5488
|
const response = z26.object({ decision: z26.enum(["pending", "allow", "deny"]) });
|
|
5249
5489
|
const liveness = new AbortController();
|
|
5250
5490
|
const gate = new ToolApprovalGate(assignment.runId, {
|
|
5251
|
-
permission: (request) => toolApprovalDecision(
|
|
5491
|
+
permission: (request) => toolApprovalDecision(
|
|
5492
|
+
request,
|
|
5493
|
+
assignment.policy,
|
|
5494
|
+
worktree,
|
|
5495
|
+
runApps(assignment.mcp)?.apps.map((app) => app.name),
|
|
5496
|
+
assignment.github ? [new URL(assignment.github.endpoint).host] : []
|
|
5497
|
+
),
|
|
5252
5498
|
// Keep the callback pending at the tool boundary. The adapter's current
|
|
5253
5499
|
// session and checkpoint remain intact; handback resolves this same call.
|
|
5254
5500
|
isLive: async () => {
|
|
@@ -5754,7 +6000,7 @@ function buildChildEnv(options = {}) {
|
|
|
5754
6000
|
}
|
|
5755
6001
|
|
|
5756
6002
|
// src/agents/child.ts
|
|
5757
|
-
import { spawn } from "node:child_process";
|
|
6003
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
5758
6004
|
import {
|
|
5759
6005
|
mkdirSync as mkdirSync4,
|
|
5760
6006
|
readFileSync as readFileSync3,
|
|
@@ -5919,7 +6165,7 @@ function endedSession(sessionId, exit) {
|
|
|
5919
6165
|
};
|
|
5920
6166
|
}
|
|
5921
6167
|
var STDERR_TAIL = 4e3;
|
|
5922
|
-
function
|
|
6168
|
+
function signalGroup2(pgid, signal) {
|
|
5923
6169
|
if (pgid === void 0) return;
|
|
5924
6170
|
try {
|
|
5925
6171
|
process.kill(-pgid, signal);
|
|
@@ -5941,7 +6187,7 @@ async function reapStale(pidFile2, marker) {
|
|
|
5941
6187
|
const [group, ...command] = line.trim().split(/\s+/);
|
|
5942
6188
|
return Number(group) === pgid && command.join(" ").includes(marker);
|
|
5943
6189
|
});
|
|
5944
|
-
if (member)
|
|
6190
|
+
if (member) signalGroup2(pgid, "SIGKILL");
|
|
5945
6191
|
}
|
|
5946
6192
|
async function claimRunDir(paths) {
|
|
5947
6193
|
ownDir(paths.runDir);
|
|
@@ -5952,7 +6198,7 @@ async function claimRunDir(paths) {
|
|
|
5952
6198
|
async function spawnCli(sandbox, argv, options) {
|
|
5953
6199
|
await reapStale(options.pidFile, options.marker);
|
|
5954
6200
|
const wrapped = await sandbox.wrap(argv, options.policy);
|
|
5955
|
-
const child =
|
|
6201
|
+
const child = spawn2("/bin/sh", ["-c", wrapped.command], {
|
|
5956
6202
|
cwd: options.cwd,
|
|
5957
6203
|
env: options.env,
|
|
5958
6204
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -5979,7 +6225,7 @@ ${error.message}`;
|
|
|
5979
6225
|
child.once("exit", (code, signal) => resolve13({ code, signal }));
|
|
5980
6226
|
}
|
|
5981
6227
|
).finally(() => {
|
|
5982
|
-
|
|
6228
|
+
signalGroup2(pgid, "SIGKILL");
|
|
5983
6229
|
rmSync3(options.pidFile, { force: true });
|
|
5984
6230
|
wrapped.release();
|
|
5985
6231
|
});
|
|
@@ -5989,7 +6235,7 @@ ${error.message}`;
|
|
|
5989
6235
|
exited,
|
|
5990
6236
|
stderrTail: () => wrapped.annotate(stderr.slice(-STDERR_TAIL)).trim(),
|
|
5991
6237
|
async kill(graceMs = 3e3) {
|
|
5992
|
-
|
|
6238
|
+
signalGroup2(pgid, "SIGTERM");
|
|
5993
6239
|
let timer;
|
|
5994
6240
|
await Promise.race([
|
|
5995
6241
|
exited,
|
|
@@ -5998,7 +6244,7 @@ ${error.message}`;
|
|
|
5998
6244
|
})
|
|
5999
6245
|
]);
|
|
6000
6246
|
clearTimeout(timer);
|
|
6001
|
-
|
|
6247
|
+
signalGroup2(pgid, "SIGKILL");
|
|
6002
6248
|
await exited;
|
|
6003
6249
|
}
|
|
6004
6250
|
};
|
|
@@ -6153,7 +6399,7 @@ import {
|
|
|
6153
6399
|
rmSync as rmSync4
|
|
6154
6400
|
} from "node:fs";
|
|
6155
6401
|
import { lstat, readdir } from "node:fs/promises";
|
|
6156
|
-
import { dirname as dirname6, isAbsolute as
|
|
6402
|
+
import { dirname as dirname6, isAbsolute as isAbsolute3, join as join7, relative as relative2, resolve as resolve4 } from "node:path";
|
|
6157
6403
|
import { execa as execa2 } from "execa";
|
|
6158
6404
|
import { simpleGit } from "simple-git";
|
|
6159
6405
|
var DISK_USAGE_TTL_MS = 5 * 6e4;
|
|
@@ -6171,7 +6417,7 @@ function runBranch(runId) {
|
|
|
6171
6417
|
}
|
|
6172
6418
|
function isInside(parent, child) {
|
|
6173
6419
|
const rel = relative2(parent, child);
|
|
6174
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6420
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
6175
6421
|
}
|
|
6176
6422
|
var configWriters = /* @__PURE__ */ new Map();
|
|
6177
6423
|
function withRepoConfig(repoPath, work) {
|
|
@@ -6309,7 +6555,7 @@ var WorktreeManager = class {
|
|
|
6309
6555
|
const worktreeReal = realpathSync4(worktree);
|
|
6310
6556
|
for (const file of recipe.copyFiles) {
|
|
6311
6557
|
const rel = relative2(repoPath, resolve4(repoPath, file));
|
|
6312
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
6558
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute3(rel)) {
|
|
6313
6559
|
throw new Error(`setup recipe copies a file outside the repo: ${file}`);
|
|
6314
6560
|
}
|
|
6315
6561
|
const from = join7(repoPath, rel);
|
|
@@ -7818,245 +8064,6 @@ import {
|
|
|
7818
8064
|
createSdkMcpServer as createSdkMcpServer2
|
|
7819
8065
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
7820
8066
|
|
|
7821
|
-
// src/agents/run-sandbox-launcher.ts
|
|
7822
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
7823
|
-
import { isIP } from "node:net";
|
|
7824
|
-
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
7825
|
-
import { fileURLToPath } from "node:url";
|
|
7826
|
-
import { NetworkConfigSchema } from "@anthropic-ai/sandbox-runtime";
|
|
7827
|
-
var MAX_PENDING_NETWORK = 128;
|
|
7828
|
-
var NETWORK_DECISION_MS = 5 * 6e4;
|
|
7829
|
-
function canonicalHost(host) {
|
|
7830
|
-
if (!host || /[\s\x00-\x1f\x7f%\\/@?#*]/u.test(host)) return;
|
|
7831
|
-
const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
7832
|
-
if (isIP(bare) === 6) return new URL(`http://[${bare}]`).hostname.slice(1, -1);
|
|
7833
|
-
if (bare.includes(":")) return;
|
|
7834
|
-
try {
|
|
7835
|
-
return new URL(`http://${bare}`).hostname.toLowerCase();
|
|
7836
|
-
} catch {
|
|
7837
|
-
return void 0;
|
|
7838
|
-
}
|
|
7839
|
-
}
|
|
7840
|
-
function matchesRunSandboxDomain(request, domains) {
|
|
7841
|
-
const host = canonicalHost(request.host);
|
|
7842
|
-
if (!host || !Number.isInteger(request.port) || request.port < 1 || request.port > 65535) return false;
|
|
7843
|
-
return domains.some((entry) => {
|
|
7844
|
-
if (entry === "*") return true;
|
|
7845
|
-
const parts = /^(\[[^\]]+\]|[^:]+?)(?::([1-9][0-9]{0,4}))?$/u.exec(entry);
|
|
7846
|
-
if (!parts || parts[2] !== void 0 && (Number(parts[2]) > 65535 || Number(parts[2]) !== request.port)) return false;
|
|
7847
|
-
const wildcard = parts[1].startsWith("*.");
|
|
7848
|
-
const pattern = canonicalHost(wildcard ? parts[1].slice(2) : parts[1]);
|
|
7849
|
-
if (!pattern) return false;
|
|
7850
|
-
if (!wildcard) return host === pattern;
|
|
7851
|
-
return !isIP(host) && pattern.includes(".") && host.endsWith(`.${pattern}`);
|
|
7852
|
-
});
|
|
7853
|
-
}
|
|
7854
|
-
function signalGroup2(pid, signal) {
|
|
7855
|
-
if (pid === void 0) return;
|
|
7856
|
-
try {
|
|
7857
|
-
process.kill(-pid, signal);
|
|
7858
|
-
} catch (error) {
|
|
7859
|
-
if (error.code !== "ESRCH") throw error;
|
|
7860
|
-
}
|
|
7861
|
-
}
|
|
7862
|
-
function spawnRunSandbox(options) {
|
|
7863
|
-
if (process.platform !== "linux" && process.platform !== "darwin") {
|
|
7864
|
-
throw new Error("run sandbox launcher requires Linux or macOS process groups");
|
|
7865
|
-
}
|
|
7866
|
-
if (!options.command || options.command.includes("\0") || !isAbsolute3(options.cwd)) {
|
|
7867
|
-
throw new Error("run sandbox requires a command and absolute cwd");
|
|
7868
|
-
}
|
|
7869
|
-
if (options.authToken !== void 0 && (!options.authToken || Buffer.byteLength(options.authToken) > 1048576)) {
|
|
7870
|
-
throw new Error("invalid run sandbox auth pipe payload length");
|
|
7871
|
-
}
|
|
7872
|
-
const maximum = Object.freeze([...options.maxAllowDomains]);
|
|
7873
|
-
const modelApiDomains = Object.freeze([...options.modelApiDomains ?? []]);
|
|
7874
|
-
NetworkConfigSchema.parse({ allowedDomains: maximum.filter((entry) => entry !== "*"), deniedDomains: [] });
|
|
7875
|
-
NetworkConfigSchema.parse({ allowedDomains: [...modelApiDomains], deniedDomains: [] });
|
|
7876
|
-
const onNetwork = options.onNetwork;
|
|
7877
|
-
const abortSignal = options.signal;
|
|
7878
|
-
const launch = {
|
|
7879
|
-
type: "init",
|
|
7880
|
-
command: options.command,
|
|
7881
|
-
args: [...options.args],
|
|
7882
|
-
cwd: options.cwd,
|
|
7883
|
-
env: { ...options.env },
|
|
7884
|
-
policy: {
|
|
7885
|
-
allowWrite: [...options.policy.allowWrite],
|
|
7886
|
-
denyWrite: [...options.policy.denyWrite ?? []],
|
|
7887
|
-
denyRead: [...options.policy.denyRead ?? []]
|
|
7888
|
-
},
|
|
7889
|
-
maxAllowDomains: [...maximum],
|
|
7890
|
-
modelApiDomains: [...modelApiDomains],
|
|
7891
|
-
hasAuthToken: options.authToken !== void 0
|
|
7892
|
-
};
|
|
7893
|
-
const child = spawn2(process.execPath, [fileURLToPath(new URL("./run-sandbox-worker.mjs", import.meta.url))], {
|
|
7894
|
-
// Runtime helpers use the trusted daemon's environment. CLI env arrives
|
|
7895
|
-
// over IPC and is passed explicitly by the worker, without inheritance.
|
|
7896
|
-
env: process.env,
|
|
7897
|
-
cwd: options.cwd,
|
|
7898
|
-
stdio: ["pipe", "pipe", "pipe", "pipe", "ipc"],
|
|
7899
|
-
detached: true
|
|
7900
|
-
});
|
|
7901
|
-
const lifetime = new AbortController();
|
|
7902
|
-
const pending = /* @__PURE__ */ new Map();
|
|
7903
|
-
let stderr = "";
|
|
7904
|
-
let stopped = false;
|
|
7905
|
-
let gone = false;
|
|
7906
|
-
let outcome;
|
|
7907
|
-
let hardStop;
|
|
7908
|
-
let settle;
|
|
7909
|
-
const exited = new Promise((resolve13) => {
|
|
7910
|
-
settle = resolve13;
|
|
7911
|
-
});
|
|
7912
|
-
function send(message) {
|
|
7913
|
-
if (!child.connected || gone) return;
|
|
7914
|
-
child.send(message, (error) => {
|
|
7915
|
-
if (error) stop(0);
|
|
7916
|
-
});
|
|
7917
|
-
}
|
|
7918
|
-
function stop(graceMs = 3e3) {
|
|
7919
|
-
if (gone) return;
|
|
7920
|
-
if (!stopped) {
|
|
7921
|
-
stopped = true;
|
|
7922
|
-
lifetime.abort();
|
|
7923
|
-
for (const request of pending.values()) {
|
|
7924
|
-
clearTimeout(request.timer);
|
|
7925
|
-
request.controller.abort();
|
|
7926
|
-
}
|
|
7927
|
-
pending.clear();
|
|
7928
|
-
send({ type: "stop", graceMs });
|
|
7929
|
-
}
|
|
7930
|
-
if (!hardStop) hardStop = setTimeout(() => {
|
|
7931
|
-
stderr += "\nrun sandbox worker did not stop; terminating its process group";
|
|
7932
|
-
try {
|
|
7933
|
-
signalGroup2(child.pid, "SIGKILL");
|
|
7934
|
-
} catch (error) {
|
|
7935
|
-
stderr += `
|
|
7936
|
-
run sandbox group termination failed: ${String(error)}`;
|
|
7937
|
-
}
|
|
7938
|
-
}, Math.max(0, Math.min(graceMs, 3e4)) + 5e3);
|
|
7939
|
-
}
|
|
7940
|
-
child.stderr.on("data", (data) => {
|
|
7941
|
-
stderr = (stderr + data.toString()).slice(-16e3);
|
|
7942
|
-
});
|
|
7943
|
-
child.stdin.on("error", () => void 0);
|
|
7944
|
-
child.on("message", (message) => {
|
|
7945
|
-
if (!message || typeof message !== "object") return;
|
|
7946
|
-
const value = message;
|
|
7947
|
-
if (value.type === "finished") {
|
|
7948
|
-
if ((value.code === null || Number.isInteger(value.code)) && (value.signal === null || typeof value.signal === "string")) {
|
|
7949
|
-
outcome = { code: value.code, signal: value.signal };
|
|
7950
|
-
}
|
|
7951
|
-
return;
|
|
7952
|
-
}
|
|
7953
|
-
if (value.type !== "network" || !Number.isSafeInteger(value.id) || value.id < 1 || pending.has(value.id)) return;
|
|
7954
|
-
const id4 = value.id;
|
|
7955
|
-
const request = {
|
|
7956
|
-
host: typeof value.host === "string" ? value.host : "",
|
|
7957
|
-
port: typeof value.port === "number" ? value.port : 0
|
|
7958
|
-
};
|
|
7959
|
-
if (typeof value.port !== "number" || stopped || lifetime.signal.aborted || pending.size >= MAX_PENDING_NETWORK || !matchesRunSandboxDomain(request, maximum)) {
|
|
7960
|
-
send({ type: "decision", id: id4, allow: false });
|
|
7961
|
-
return;
|
|
7962
|
-
}
|
|
7963
|
-
const controller = new AbortController();
|
|
7964
|
-
const timer = setTimeout(() => {
|
|
7965
|
-
if (!pending.delete(id4)) return;
|
|
7966
|
-
controller.abort();
|
|
7967
|
-
send({ type: "decision", id: id4, allow: false });
|
|
7968
|
-
}, NETWORK_DECISION_MS);
|
|
7969
|
-
pending.set(id4, { controller, timer });
|
|
7970
|
-
void Promise.resolve().then(() => {
|
|
7971
|
-
if (stopped || lifetime.signal.aborted) return false;
|
|
7972
|
-
return onNetwork(request, controller.signal);
|
|
7973
|
-
}).then((allow) => {
|
|
7974
|
-
if (!pending.delete(id4)) return;
|
|
7975
|
-
clearTimeout(timer);
|
|
7976
|
-
send({ type: "decision", id: id4, allow: allow === true && !stopped && !lifetime.signal.aborted && !controller.signal.aborted });
|
|
7977
|
-
}, () => {
|
|
7978
|
-
if (pending.delete(id4)) {
|
|
7979
|
-
clearTimeout(timer);
|
|
7980
|
-
send({ type: "decision", id: id4, allow: false });
|
|
7981
|
-
}
|
|
7982
|
-
});
|
|
7983
|
-
});
|
|
7984
|
-
function onParentExit() {
|
|
7985
|
-
try {
|
|
7986
|
-
signalGroup2(child.pid, "SIGKILL");
|
|
7987
|
-
} catch {
|
|
7988
|
-
}
|
|
7989
|
-
}
|
|
7990
|
-
process.once("exit", onParentExit);
|
|
7991
|
-
function finish(code, signal) {
|
|
7992
|
-
if (gone) return;
|
|
7993
|
-
gone = true;
|
|
7994
|
-
stopped = true;
|
|
7995
|
-
lifetime.abort();
|
|
7996
|
-
for (const request of pending.values()) {
|
|
7997
|
-
clearTimeout(request.timer);
|
|
7998
|
-
request.controller.abort();
|
|
7999
|
-
}
|
|
8000
|
-
pending.clear();
|
|
8001
|
-
clearTimeout(hardStop);
|
|
8002
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
8003
|
-
process.removeListener("exit", onParentExit);
|
|
8004
|
-
try {
|
|
8005
|
-
signalGroup2(child.pid, "SIGKILL");
|
|
8006
|
-
} catch (error) {
|
|
8007
|
-
stderr += `
|
|
8008
|
-
run sandbox group cleanup failed: ${String(error)}`;
|
|
8009
|
-
}
|
|
8010
|
-
settle(outcome ?? { code, signal });
|
|
8011
|
-
}
|
|
8012
|
-
child.once("error", (error) => {
|
|
8013
|
-
stderr += `
|
|
8014
|
-
${error.message}`;
|
|
8015
|
-
stop(0);
|
|
8016
|
-
if (child.pid === void 0) finish(null, null);
|
|
8017
|
-
});
|
|
8018
|
-
child.once("exit", finish);
|
|
8019
|
-
child.once("disconnect", () => {
|
|
8020
|
-
if (!outcome) stop(0);
|
|
8021
|
-
});
|
|
8022
|
-
const authPipe = child.stdio[3];
|
|
8023
|
-
authPipe.on("error", () => stop(0));
|
|
8024
|
-
authPipe.end(options.authToken);
|
|
8025
|
-
function onAbort() {
|
|
8026
|
-
stop(0);
|
|
8027
|
-
}
|
|
8028
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
8029
|
-
child.kill = (signal = "SIGTERM") => {
|
|
8030
|
-
if (gone) return false;
|
|
8031
|
-
if (signal === 0) {
|
|
8032
|
-
try {
|
|
8033
|
-
process.kill(child.pid, 0);
|
|
8034
|
-
return true;
|
|
8035
|
-
} catch {
|
|
8036
|
-
return false;
|
|
8037
|
-
}
|
|
8038
|
-
}
|
|
8039
|
-
stop(signal === "SIGKILL" ? 0 : 3e3);
|
|
8040
|
-
return true;
|
|
8041
|
-
};
|
|
8042
|
-
Object.defineProperty(child, "killed", { get: () => stopped });
|
|
8043
|
-
if (abortSignal?.aborted) stop(0);
|
|
8044
|
-
else send(launch);
|
|
8045
|
-
return {
|
|
8046
|
-
process: child,
|
|
8047
|
-
exited,
|
|
8048
|
-
stderrTail: () => stderr.slice(-4e3).trim(),
|
|
8049
|
-
async kill(graceMs = 3e3) {
|
|
8050
|
-
stop(graceMs);
|
|
8051
|
-
await exited;
|
|
8052
|
-
},
|
|
8053
|
-
async revoke() {
|
|
8054
|
-
stop(0);
|
|
8055
|
-
await exited;
|
|
8056
|
-
}
|
|
8057
|
-
};
|
|
8058
|
-
}
|
|
8059
|
-
|
|
8060
8067
|
// src/agents/run-network.ts
|
|
8061
8068
|
function runNetworkApproval(runId, harness, gate, maximum, localComputerEndpoint, appHost) {
|
|
8062
8069
|
if (gate.runId !== runId) throw new Error("Run approval scope mismatch");
|
package/client.js
CHANGED
|
@@ -3741,7 +3741,7 @@ import { join } from "node:path";
|
|
|
3741
3741
|
// package.json
|
|
3742
3742
|
var package_default = {
|
|
3743
3743
|
name: "@mikka/daemon",
|
|
3744
|
-
version: "0.0.
|
|
3744
|
+
version: "0.0.158",
|
|
3745
3745
|
description: "Local Mikka daemon: runs agents through the user's own CLIs",
|
|
3746
3746
|
type: "module",
|
|
3747
3747
|
private: true,
|
|
@@ -3801,7 +3801,7 @@ var package_default = {
|
|
|
3801
3801
|
engines: {
|
|
3802
3802
|
node: ">=24"
|
|
3803
3803
|
},
|
|
3804
|
-
gitHead: "
|
|
3804
|
+
gitHead: "5011b7faac02b82a863d07eba4d7180d47fccc2b"
|
|
3805
3805
|
};
|
|
3806
3806
|
|
|
3807
3807
|
// src/paths.ts
|