@kitman-sh/cli 0.10.71 → 0.10.73
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/{chunk-g4xsa9ks.js → chunk-0rzh8yfd.js} +159 -34
- package/dist/chunk-3edjgnxf.js +202 -0
- package/dist/chunk-gmnqchsc.js +16 -0
- package/dist/{chunk-y7p921rs.js → chunk-r8rcfjag.js} +4 -4
- package/dist/egress-proxy.ts +51 -4
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-1w81w56v.js +0 -109
- package/dist/chunk-wtpwkjws.js +0 -100
- package/dist/{chunk-vs10kwdr.js → chunk-t1z7atrq.js} +8 -8
|
@@ -285,6 +285,16 @@ import {
|
|
|
285
285
|
skillIdentity2
|
|
286
286
|
} from "./chunk-4wqjxfbv.js";
|
|
287
287
|
import"./chunk-hr9vd6dj.js";
|
|
288
|
+
import {
|
|
289
|
+
egressAllowed,
|
|
290
|
+
writeSecretRegistry,
|
|
291
|
+
secretRefusalLog,
|
|
292
|
+
appendSecretRefusals,
|
|
293
|
+
parseSecretRefusals,
|
|
294
|
+
secretFilter2,
|
|
295
|
+
opaqueTunnelRefusal2,
|
|
296
|
+
isOpaqueConnectReason2
|
|
297
|
+
} from "./chunk-3edjgnxf.js";
|
|
288
298
|
import {
|
|
289
299
|
sliceBytes2,
|
|
290
300
|
maxLineBytes2,
|
|
@@ -293,11 +303,6 @@ import {
|
|
|
293
303
|
decoded2,
|
|
294
304
|
readOpencode2
|
|
295
305
|
} from "./chunk-csbf77zs.js";
|
|
296
|
-
import {
|
|
297
|
-
writeSecretRegistry,
|
|
298
|
-
secretRefusalLog,
|
|
299
|
-
parseSecretRefusals
|
|
300
|
-
} from "./chunk-1w81w56v.js";
|
|
301
306
|
import {
|
|
302
307
|
uploadRunFile,
|
|
303
308
|
programLease,
|
|
@@ -2594,6 +2599,82 @@ var prepareDockerHome = (input) => gen(function* () {
|
|
|
2594
2599
|
}
|
|
2595
2600
|
});
|
|
2596
2601
|
|
|
2602
|
+
// packages/agent-runtime/src/docker-secret-proxy.ts
|
|
2603
|
+
import { createHttpProxyServer } from "@anthropic-ai/sandbox-runtime/dist/sandbox/http-proxy.js";
|
|
2604
|
+
import { isLoopbackName } from "@anthropic-ai/sandbox-runtime/dist/sandbox/address.js";
|
|
2605
|
+
import { createMitmCA, disposeMitmCA } from "@anthropic-ai/sandbox-runtime/dist/sandbox/mitm-ca.js";
|
|
2606
|
+
import { canonicalizeHost } from "@anthropic-ai/sandbox-runtime/dist/sandbox/parent-proxy.js";
|
|
2607
|
+
import {
|
|
2608
|
+
createResolvedAddressGuard
|
|
2609
|
+
} from "@anthropic-ai/sandbox-runtime/dist/sandbox/resolved-address-guard.js";
|
|
2610
|
+
import { chmodSync } from "fs";
|
|
2611
|
+
import { isIP } from "net";
|
|
2612
|
+
import { networkInterfaces } from "os";
|
|
2613
|
+
class SecretProxyUnavailable extends TaggedError("SecretProxyUnavailable") {
|
|
2614
|
+
}
|
|
2615
|
+
var dockerSecretProxySocket = (registryDirectory) => `${registryDirectory}/proxy.sock`;
|
|
2616
|
+
var anyName = "kitman-target.invalid";
|
|
2617
|
+
var dockerBridge = (name) => name === "docker0" || name === "docker_gwbridge" || name.startsWith("br-");
|
|
2618
|
+
var dockerNetworkSubnets = (interfaces = networkInterfaces()) => Object.entries(interfaces).flatMap(([name, addresses]) => dockerBridge(name) ? (addresses ?? []).flatMap(({ cidr }) => cidr === null ? [] : [cidr]) : []);
|
|
2619
|
+
var runnerAddresses = (interfaces) => {
|
|
2620
|
+
let current;
|
|
2621
|
+
try {
|
|
2622
|
+
current = interfaces();
|
|
2623
|
+
} catch {
|
|
2624
|
+
return [];
|
|
2625
|
+
}
|
|
2626
|
+
const own = Object.values(current).flatMap((addresses) => (addresses ?? []).map(({ address }) => address));
|
|
2627
|
+
return [...own, ...dockerNetworkSubnets(current)];
|
|
2628
|
+
};
|
|
2629
|
+
var runnerAddressGuard = (options = {}, interfaces = networkInterfaces) => createResolvedAddressGuard({ ...options, localAddresses: () => runnerAddresses(interfaces) });
|
|
2630
|
+
var namesRunner = (guard, host, port) => {
|
|
2631
|
+
const canonical = canonicalizeHost(host) ?? host;
|
|
2632
|
+
if (isLoopbackName(canonical))
|
|
2633
|
+
return !guard.permits(anyName, "127.0.0.1", port);
|
|
2634
|
+
return isIP(canonical) !== 0 && !guard.permits(anyName, canonical, port);
|
|
2635
|
+
};
|
|
2636
|
+
var startDockerSecretProxy = (input) => gen(function* () {
|
|
2637
|
+
const ca = yield* acquireRelease(try_2({ try: () => createMitmCA({}), catch: (cause) => new SecretProxyUnavailable({ cause }) }), (ca) => ignore(tryPromise(() => disposeMitmCA(ca))));
|
|
2638
|
+
const { network, registryFile } = input;
|
|
2639
|
+
const guard = runnerAddressGuard(input.addressGuard);
|
|
2640
|
+
const server = createHttpProxyServer({
|
|
2641
|
+
filter: (port, host, _socket, _command, explain) => {
|
|
2642
|
+
if (namesRunner(guard, host, port)) {
|
|
2643
|
+
explain?.("the Runner's own address or one of its docker networks");
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
if (network === "open" || egressAllowed(network.hosts, host, port))
|
|
2647
|
+
return true;
|
|
2648
|
+
explain?.("host not allowed");
|
|
2649
|
+
return false;
|
|
2650
|
+
},
|
|
2651
|
+
lookupFor: (port) => guard.lookupFor(port),
|
|
2652
|
+
mitmCA: ca,
|
|
2653
|
+
filterRequest: secretFilter2(registryFile),
|
|
2654
|
+
refuseOpaqueTunnels: true,
|
|
2655
|
+
onFilterRequestDenied: ({ method, url, reason }) => {
|
|
2656
|
+
if (method !== "CONNECT" || !isOpaqueConnectReason2(reason))
|
|
2657
|
+
return;
|
|
2658
|
+
try {
|
|
2659
|
+
appendSecretRefusals(registryFile, [{ host: url, reason: opaqueTunnelRefusal2 }]);
|
|
2660
|
+
} catch {}
|
|
2661
|
+
},
|
|
2662
|
+
...input.upstreamCA === undefined ? undefined : { tlsTerminateUpstreamCA: input.upstreamCA }
|
|
2663
|
+
});
|
|
2664
|
+
yield* acquireRelease(callback3((resume) => {
|
|
2665
|
+
server.once("error", (cause) => resume(fail7(new SecretProxyUnavailable({ cause }))));
|
|
2666
|
+
server.listen(input.socket, () => resume(void_3));
|
|
2667
|
+
}), () => sync2(() => server.close()));
|
|
2668
|
+
yield* try_2({
|
|
2669
|
+
try: () => chmodSync(input.socket, 438),
|
|
2670
|
+
catch: (cause) => new SecretProxyUnavailable({ cause })
|
|
2671
|
+
});
|
|
2672
|
+
return { socket: input.socket, trustBundle: ca.trustBundlePath };
|
|
2673
|
+
});
|
|
2674
|
+
|
|
2675
|
+
// packages/agent-runtime/src/docker-sandbox.ts
|
|
2676
|
+
import { CA_TRUST_VARS } from "@anthropic-ai/sandbox-runtime/dist/sandbox/sandbox-utils.js";
|
|
2677
|
+
|
|
2597
2678
|
// packages/agent-runtime/src/helper-scripts.ts
|
|
2598
2679
|
import { fileURLToPath } from "url";
|
|
2599
2680
|
var helperScriptPath = (name) => gen(function* () {
|
|
@@ -2611,9 +2692,14 @@ var RunnerLabel = "kitman.runner";
|
|
|
2611
2692
|
var StepAttemptLabel = "kitman.step-attempt";
|
|
2612
2693
|
var EgressProxyImage = "oven/bun@sha256:4cf82f5686b196504fe119db3fcd9185b191f82ece82c40680e597228ec6070a";
|
|
2613
2694
|
var egressProxyAddress = "http://kitman-egress:3128";
|
|
2695
|
+
var relayedSocket = "/kitman/proxy.sock";
|
|
2696
|
+
var trustBundle = "/kitman/trust.pem";
|
|
2614
2697
|
|
|
2615
2698
|
class ContainerError extends TaggedError("ContainerError") {
|
|
2616
2699
|
}
|
|
2700
|
+
|
|
2701
|
+
class SecretProxyUnreachable extends TaggedError("SecretProxyUnreachable") {
|
|
2702
|
+
}
|
|
2617
2703
|
var imagePull = { timeout: "30 minutes" };
|
|
2618
2704
|
var dockerContainerRunner = map8(ChildProcessSpawner, (spawner) => {
|
|
2619
2705
|
const docker = (args, options = {}) => runCommand2("docker", args, options).pipe(mapError2((error) => new ContainerError({ message: error.message })), provideService(ChildProcessSpawner, spawner));
|
|
@@ -2629,6 +2715,7 @@ var dockerContainerRunner = map8(ChildProcessSpawner, (spawner) => {
|
|
|
2629
2715
|
input.name,
|
|
2630
2716
|
...labelArgs(input.labels),
|
|
2631
2717
|
...input.network === undefined ? [] : ["--network", input.network],
|
|
2718
|
+
...input.alias === undefined ? [] : ["--network-alias", input.alias],
|
|
2632
2719
|
...input.user === undefined ? [] : ["--user", input.user],
|
|
2633
2720
|
...input.mounts.flatMap((mount) => [
|
|
2634
2721
|
"--volume",
|
|
@@ -2678,26 +2765,49 @@ var startDockerSandbox = (input) => gen(function* () {
|
|
|
2678
2765
|
[StepAttemptLabel, input.attemptId]
|
|
2679
2766
|
];
|
|
2680
2767
|
const agent = `kitman-agent-${id}`;
|
|
2681
|
-
const
|
|
2768
|
+
const { secretProxy } = input;
|
|
2769
|
+
const network = input.policy.network === "open" && secretProxy === undefined ? undefined : `kitman-agent-net-${id}`;
|
|
2682
2770
|
yield* acquireRelease(void_3, () => containers.removeLabelled([StepAttemptLabel, input.attemptId]));
|
|
2683
|
-
if (
|
|
2771
|
+
if (network !== undefined) {
|
|
2684
2772
|
const proxy = `kitman-egress-${id}`;
|
|
2773
|
+
const script = {
|
|
2774
|
+
source: yield* helperScriptPath("egress-proxy.ts"),
|
|
2775
|
+
target: "/kitman/egress-proxy.ts",
|
|
2776
|
+
readOnly: true
|
|
2777
|
+
};
|
|
2685
2778
|
yield* containers.createInternalNetwork({ name: network, labels });
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2779
|
+
const image = input.docker.proxyImage ?? EgressProxyImage;
|
|
2780
|
+
if (secretProxy === undefined) {
|
|
2781
|
+
yield* containers.run({
|
|
2782
|
+
name: proxy,
|
|
2783
|
+
image,
|
|
2784
|
+
labels,
|
|
2785
|
+
mounts: [script],
|
|
2786
|
+
env: {},
|
|
2787
|
+
command: ["bun", script.target, input.policy.network === "open" ? "" : input.policy.network.hosts.join(",")]
|
|
2788
|
+
});
|
|
2789
|
+
yield* containers.connect({ network, container: proxy, alias: "kitman-egress" });
|
|
2790
|
+
} else {
|
|
2791
|
+
yield* containers.run({
|
|
2792
|
+
name: proxy,
|
|
2793
|
+
image,
|
|
2794
|
+
labels,
|
|
2795
|
+
network,
|
|
2796
|
+
alias: "kitman-egress",
|
|
2797
|
+
mounts: [script, { source: secretProxy.socket, target: relayedSocket }],
|
|
2798
|
+
env: {},
|
|
2799
|
+
command: ["bun", script.target, "--relay", relayedSocket]
|
|
2800
|
+
});
|
|
2801
|
+
const probe = yield* containers.exec({
|
|
2802
|
+
container: proxy,
|
|
2803
|
+
workdir: "/",
|
|
2804
|
+
env: {},
|
|
2805
|
+
command: ["bun", script.target, "--probe", relayedSocket],
|
|
2806
|
+
timeout: "1 minute"
|
|
2807
|
+
});
|
|
2808
|
+
if (probe.exitCode !== 0)
|
|
2809
|
+
return yield* new SecretProxyUnreachable({ output: probe.output });
|
|
2810
|
+
}
|
|
2701
2811
|
for (const service of input.services ?? [])
|
|
2702
2812
|
yield* acquireRelease(containers.connect({ network, ...service }), () => containers.disconnect({ network, container: service.container }));
|
|
2703
2813
|
}
|
|
@@ -2709,7 +2819,8 @@ var startDockerSandbox = (input) => gen(function* () {
|
|
|
2709
2819
|
...input.docker.user === undefined ? undefined : { user: input.docker.user },
|
|
2710
2820
|
mounts: [
|
|
2711
2821
|
...input.policy.writablePaths.map((writable) => ({ source: writable, target: writable })),
|
|
2712
|
-
...(input.readOnlyPaths ?? []).map((readable) => ({ source: readable, target: readable, readOnly: true }))
|
|
2822
|
+
...(input.readOnlyPaths ?? []).map((readable) => ({ source: readable, target: readable, readOnly: true })),
|
|
2823
|
+
...secretProxy === undefined ? [] : [{ source: secretProxy.trustBundle, target: trustBundle, readOnly: true }]
|
|
2713
2824
|
],
|
|
2714
2825
|
workdir: input.workspace,
|
|
2715
2826
|
env: {
|
|
@@ -2722,7 +2833,8 @@ var startDockerSandbox = (input) => gen(function* () {
|
|
|
2722
2833
|
http_proxy: egressProxyAddress,
|
|
2723
2834
|
NO_PROXY: "localhost,127.0.0.1",
|
|
2724
2835
|
no_proxy: "localhost,127.0.0.1"
|
|
2725
|
-
}
|
|
2836
|
+
},
|
|
2837
|
+
...secretProxy === undefined ? undefined : Object.fromEntries(CA_TRUST_VARS.map((name) => [name, trustBundle]))
|
|
2726
2838
|
},
|
|
2727
2839
|
command: ["sleep", "infinity"]
|
|
2728
2840
|
});
|
|
@@ -3179,12 +3291,14 @@ var runAttempt = (request, options) => gen(function* () {
|
|
|
3179
3291
|
const { attempt } = request.attempt;
|
|
3180
3292
|
const workspace = request.workspace.path;
|
|
3181
3293
|
const secrets = request.launch.secrets ?? {};
|
|
3182
|
-
if (Object.keys(secrets).length > 0 && request.sandbox.backend === "docker")
|
|
3183
|
-
return failed("secrets_need_srt", "A docker Step cannot take Secrets yet: its egress proxy cannot swap their references for values, and Kitman puts no value in a sandbox. Run this Step in srt.");
|
|
3184
3294
|
const bound = yield* result(requireSecretHosts(request.launch.secrets, request.launch.secretDestinations));
|
|
3185
3295
|
if (isFailure(bound))
|
|
3186
3296
|
return failed(bound.failure.reason, bound.failure.summary);
|
|
3187
|
-
const referenced = request.sandbox.backend === "
|
|
3297
|
+
const referenced = request.sandbox.backend === "host" ? undefined : secretReferences(secrets, request.launch.secretDestinations ?? {});
|
|
3298
|
+
const holdSecrets = (initial) => stepSecretRegistry(stepSecretDirectory(options.home, attempt.id), initial, referenced?.references ?? {}).pipe(mapError2(() => new AgentStepFailure({
|
|
3299
|
+
reason: "secret_registry_unwritable",
|
|
3300
|
+
summary: "The Step's Secret references could not be written."
|
|
3301
|
+
})));
|
|
3188
3302
|
const agent = referenced === undefined || request.launch.secrets === undefined ? request.launch : { ...request.launch, secrets: referenced.references };
|
|
3189
3303
|
const prepared = yield* result(prepareHarness({
|
|
3190
3304
|
attempt: request.attempt,
|
|
@@ -3225,10 +3339,7 @@ var runAttempt = (request, options) => gen(function* () {
|
|
|
3225
3339
|
]))
|
|
3226
3340
|
};
|
|
3227
3341
|
const holdsLogin = holdsLoginByReference(launch);
|
|
3228
|
-
const registry = Object.keys(initial).length === 0 && !holdsLogin ? undefined : yield*
|
|
3229
|
-
reason: "secret_registry_unwritable",
|
|
3230
|
-
summary: "The Step's Secret references could not be written."
|
|
3231
|
-
})));
|
|
3342
|
+
const registry = Object.keys(initial).length === 0 && !holdsLogin ? undefined : yield* holdSecrets(initial);
|
|
3232
3343
|
const withSessions = sessions === undefined ? policy : {
|
|
3233
3344
|
...policy,
|
|
3234
3345
|
writablePaths: [...policy.writablePaths, sessions],
|
|
@@ -3254,14 +3365,27 @@ var runAttempt = (request, options) => gen(function* () {
|
|
|
3254
3365
|
case "docker": {
|
|
3255
3366
|
if (options.docker === undefined)
|
|
3256
3367
|
return yield* new AgentStepFailure({ reason: "sandbox_unavailable", summary: "`docker` is not available." });
|
|
3368
|
+
const registry = referenced === undefined || Object.keys(referenced.registry).length === 0 ? undefined : yield* holdSecrets(referenced.registry);
|
|
3369
|
+
const secretProxy = registry === undefined ? undefined : yield* startDockerSecretProxy({
|
|
3370
|
+
registryFile: registry.file,
|
|
3371
|
+
socket: dockerSecretProxySocket(registry.directory),
|
|
3372
|
+
network: policy.network
|
|
3373
|
+
}).pipe(tapError2((error) => logWarning("secret_proxy_unavailable", error.cause)), mapError2(() => new AgentStepFailure({
|
|
3374
|
+
reason: "secret_proxy_unavailable",
|
|
3375
|
+
summary: "The proxy that swaps the Step's Secret references could not be started."
|
|
3376
|
+
})));
|
|
3257
3377
|
const { container } = yield* startDockerSandbox({
|
|
3258
3378
|
docker: options.docker,
|
|
3259
3379
|
runnerId: request.runnerId,
|
|
3260
3380
|
attemptId: attempt.id,
|
|
3261
3381
|
policy,
|
|
3262
3382
|
workspace,
|
|
3263
|
-
configDir
|
|
3264
|
-
|
|
3383
|
+
configDir,
|
|
3384
|
+
...secretProxy === undefined ? undefined : { secretProxy }
|
|
3385
|
+
}).pipe(tapError2((error) => isTagged(error, "SecretProxyUnreachable") ? logWarning("secret_proxy_unavailable", error.output) : void_3), mapError2((error) => isTagged(error, "SecretProxyUnreachable") ? new AgentStepFailure({
|
|
3386
|
+
reason: "secret_proxy_unavailable",
|
|
3387
|
+
summary: "The egress container could not reach the proxy that swaps the Step's Secret references."
|
|
3388
|
+
}) : new AgentStepFailure({
|
|
3265
3389
|
reason: "sandbox_container_unavailable",
|
|
3266
3390
|
summary: "The attempt's container could not be started."
|
|
3267
3391
|
})));
|
|
@@ -3285,7 +3409,7 @@ var runAttempt = (request, options) => gen(function* () {
|
|
|
3285
3409
|
return {
|
|
3286
3410
|
argv: dockerExecArgv({ container, workspace, env: policy.environment, argv: harnessArgv }),
|
|
3287
3411
|
env: policy.environment,
|
|
3288
|
-
registry
|
|
3412
|
+
registry
|
|
3289
3413
|
};
|
|
3290
3414
|
}
|
|
3291
3415
|
default:
|
|
@@ -6804,6 +6928,7 @@ export {
|
|
|
6804
6928
|
RepoMaterializationError,
|
|
6805
6929
|
RunnerLabel,
|
|
6806
6930
|
RunnerReplicaCancellation,
|
|
6931
|
+
SecretProxyUnreachable,
|
|
6807
6932
|
SkillMaterializationRefused2 as SkillMaterializationRefused,
|
|
6808
6933
|
StepAttemptLabel,
|
|
6809
6934
|
agentStepExecutionLimit,
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
getOrUndefined,
|
|
4
|
+
flatMap,
|
|
5
|
+
toArray,
|
|
6
|
+
decodeUnknownOption,
|
|
7
|
+
optionalKey,
|
|
8
|
+
String4,
|
|
9
|
+
Struct,
|
|
10
|
+
Record,
|
|
11
|
+
ArraySchema,
|
|
12
|
+
decodeJsonStringOption,
|
|
13
|
+
encodeUnknownJsonStringSync
|
|
14
|
+
} from "./chunk-d3ksv8y1.js";
|
|
15
|
+
import {
|
|
16
|
+
replaceSecretReferences,
|
|
17
|
+
secretReferencesIn
|
|
18
|
+
} from "./chunk-rrnee9mk.js";
|
|
19
|
+
|
|
20
|
+
// packages/agent-runtime/src/srt-step.ts
|
|
21
|
+
import { SandboxRuntimeConfigSchema } from "@anthropic-ai/sandbox-runtime";
|
|
22
|
+
import { spawn } from "child_process";
|
|
23
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
24
|
+
|
|
25
|
+
// packages/agent-runtime/src/secret-proxy.ts
|
|
26
|
+
import { appendFileSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
27
|
+
|
|
28
|
+
// packages/agent-runtime/src/egress-proxy.ts
|
|
29
|
+
var egressAllowed = (hosts, host, port) => {
|
|
30
|
+
const target = host.toLowerCase();
|
|
31
|
+
return hosts.some((entry) => {
|
|
32
|
+
const [name = "", entryPort] = entry.toLowerCase().split(":");
|
|
33
|
+
if (entryPort !== undefined && Number(entryPort) !== port)
|
|
34
|
+
return false;
|
|
35
|
+
return name.startsWith("*.") ? target.endsWith(name.slice(1)) : target === name;
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
if (false)
|
|
39
|
+
;
|
|
40
|
+
|
|
41
|
+
// packages/agent-runtime/src/secret-proxy.ts
|
|
42
|
+
var basicPrefix = /^basic\s+/i;
|
|
43
|
+
var basicCredential = (value) => {
|
|
44
|
+
if (!basicPrefix.test(value))
|
|
45
|
+
return;
|
|
46
|
+
const decoded = Buffer.from(value.replace(basicPrefix, ""), "base64").toString("utf8");
|
|
47
|
+
return secretReferencesIn(decoded).length === 0 ? undefined : decoded;
|
|
48
|
+
};
|
|
49
|
+
var referencesInHeader = (value) => [
|
|
50
|
+
...new Set([...secretReferencesIn(value), ...secretReferencesIn(basicCredential(value) ?? "")])
|
|
51
|
+
];
|
|
52
|
+
var swapped = (value, registry) => {
|
|
53
|
+
const swap = (reference) => registry[reference]?.value ?? reference;
|
|
54
|
+
const basic = basicCredential(value);
|
|
55
|
+
return basic === undefined ? replaceSecretReferences(value, swap) : `Basic ${Buffer.from(replaceSecretReferences(basic, swap)).toString("base64")}`;
|
|
56
|
+
};
|
|
57
|
+
var decideSecretRequest = (request, readRegistry) => {
|
|
58
|
+
const carrying = request.headers.flatMap(([name, value]) => {
|
|
59
|
+
const references = referencesInHeader(value);
|
|
60
|
+
return references.length === 0 ? [] : [{ name, value, references }];
|
|
61
|
+
});
|
|
62
|
+
const inTarget = secretReferencesIn(request.target);
|
|
63
|
+
if (carrying.length === 0 && inTarget.length === 0)
|
|
64
|
+
return { action: "allow" };
|
|
65
|
+
const registry = readRegistry();
|
|
66
|
+
const refusals = [
|
|
67
|
+
...inTarget.map((reference) => ({
|
|
68
|
+
reference,
|
|
69
|
+
host: request.host,
|
|
70
|
+
reason: "A reference is swapped only in a header, never in a URL."
|
|
71
|
+
})),
|
|
72
|
+
...[...new Set(carrying.flatMap((header) => header.references))].flatMap((reference) => {
|
|
73
|
+
const entry = registry?.[reference];
|
|
74
|
+
const reason = entry === undefined ? "The reference is unknown or revoked." : request.scheme !== "https" ? "A reference is swapped only over HTTPS." : !egressAllowed(entry.hosts, request.host, request.port) ? `The Secret may be sent only to ${entry.hosts.join(", ")}.` : undefined;
|
|
75
|
+
return reason === undefined ? [] : [{ reference, host: request.host, reason }];
|
|
76
|
+
})
|
|
77
|
+
];
|
|
78
|
+
if (refusals.length > 0)
|
|
79
|
+
return {
|
|
80
|
+
action: "deny",
|
|
81
|
+
reason: `Kitman refused this request: ${refusals.map((refusal) => `${refusal.reference}: ${refusal.reason}`).join(" ")}`,
|
|
82
|
+
refusals
|
|
83
|
+
};
|
|
84
|
+
const names = new Set(carrying.map((header) => header.name.toLowerCase()));
|
|
85
|
+
return {
|
|
86
|
+
action: "allow",
|
|
87
|
+
setHeaders: request.headers.filter(([name]) => names.has(name.toLowerCase())).map(([name, value]) => [name, registry === undefined ? value : swapped(value, registry)])
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
var Registry = Record(String4, Struct({ value: String4, hosts: ArraySchema(String4) }));
|
|
91
|
+
var Refusal = Struct({
|
|
92
|
+
reference: optionalKey(String4),
|
|
93
|
+
host: String4,
|
|
94
|
+
reason: String4
|
|
95
|
+
});
|
|
96
|
+
var readSecretRegistry = (file) => {
|
|
97
|
+
try {
|
|
98
|
+
return getOrUndefined(flatMap(decodeJsonStringOption(readFileSync(file, "utf8")), decodeUnknownOption(Registry)));
|
|
99
|
+
} catch {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var writeSecretRegistry = (file, registry) => {
|
|
104
|
+
const next = `${file}.next`;
|
|
105
|
+
writeFileSync(next, encodeUnknownJsonStringSync(registry), { mode: 384 });
|
|
106
|
+
renameSync(next, file);
|
|
107
|
+
};
|
|
108
|
+
var secretRefusalLog = (registryFile) => `${registryFile}.refused`;
|
|
109
|
+
var appendSecretRefusals = (registryFile, refusals) => appendFileSync(secretRefusalLog(registryFile), refusals.map((refusal) => `${encodeUnknownJsonStringSync(refusal)}
|
|
110
|
+
`).join(""), { mode: 384 });
|
|
111
|
+
var parseSecretRefusals = (text) => text.split(`
|
|
112
|
+
`).flatMap((line) => toArray(flatMap(decodeJsonStringOption(line), decodeUnknownOption(Refusal))));
|
|
113
|
+
|
|
114
|
+
// packages/agent-runtime/src/srt-step.ts
|
|
115
|
+
var secretFilter2 = (registryFile) => (request, info) => {
|
|
116
|
+
const url = new URL(request.url);
|
|
117
|
+
const decision = decideSecretRequest({
|
|
118
|
+
headers: [...request.headers.entries()],
|
|
119
|
+
host: info?.target?.host ?? url.hostname,
|
|
120
|
+
port: info?.target?.port ?? (url.port === "" ? url.protocol === "https:" ? 443 : 80 : Number(url.port)),
|
|
121
|
+
target: info?.requestTarget ?? `${url.pathname}${url.search}`,
|
|
122
|
+
scheme: info?.scheme ?? (url.protocol === "https:" ? "https" : "http")
|
|
123
|
+
}, () => readSecretRegistry(registryFile));
|
|
124
|
+
if (decision.action === "allow")
|
|
125
|
+
return Promise.resolve(decision.setHeaders === undefined ? { action: "allow" } : { action: "allow", setHeaders: decision.setHeaders.map(([name, value]) => [name, value]) });
|
|
126
|
+
try {
|
|
127
|
+
appendSecretRefusals(registryFile, decision.refusals);
|
|
128
|
+
} catch {}
|
|
129
|
+
return Promise.resolve({ action: "deny", status: 403, mark: "secret-refused", reason: decision.reason });
|
|
130
|
+
};
|
|
131
|
+
var stepSandboxConfig = (settings, registryFile) => {
|
|
132
|
+
const parsed = SandboxRuntimeConfigSchema.safeParse({
|
|
133
|
+
...settings,
|
|
134
|
+
network: { ...settings.network, tlsTerminate: {}, refuseOpaqueTunnels: true }
|
|
135
|
+
});
|
|
136
|
+
return parsed.success ? {
|
|
137
|
+
success: true,
|
|
138
|
+
data: { ...parsed.data, network: { ...parsed.data.network, filterRequest: secretFilter2(registryFile) } }
|
|
139
|
+
} : parsed;
|
|
140
|
+
};
|
|
141
|
+
var opaqueTunnelRefusal2 = "A tunnel Kitman cannot see into, such as SOCKS or a CONNECT without TLS, is refused.";
|
|
142
|
+
var opaqueConnectReasons = "tunnel carries no TLS|this proxy does not tunnel CONNECTs";
|
|
143
|
+
var isOpaqueConnectReason2 = (reason) => new RegExp(`^(?:${opaqueConnectReasons})`).test(reason);
|
|
144
|
+
var opaqueTunnel = new RegExp(`^deny (?:socks-tunnel (\\S+) \\(refuseOpaqueTunnels\\)|http-request CONNECT (\\S+) \\((?:${opaqueConnectReasons})[^)]*\\))`);
|
|
145
|
+
var refusedTunnelHost = (line) => {
|
|
146
|
+
const match = opaqueTunnel.exec(line);
|
|
147
|
+
return match === null ? undefined : match[1] ?? match[2];
|
|
148
|
+
};
|
|
149
|
+
var shellQuoted = (arg) => `'${arg.replaceAll("'", "'\\''")}'`;
|
|
150
|
+
var argument = (args, flag) => {
|
|
151
|
+
const at = args.indexOf(flag);
|
|
152
|
+
return at === -1 ? undefined : args[at + 1];
|
|
153
|
+
};
|
|
154
|
+
var runSrtStep2 = async (args) => {
|
|
155
|
+
const separator = args.indexOf("--");
|
|
156
|
+
const settingsFile = argument(args, "--settings");
|
|
157
|
+
const registryFile = argument(args, "--secrets");
|
|
158
|
+
const command = separator === -1 ? [] : args.slice(separator + 1);
|
|
159
|
+
if (settingsFile === undefined || registryFile === undefined || command.length === 0) {
|
|
160
|
+
console.error("Usage: srt-step --settings <file> --secrets <registry> -- <command>");
|
|
161
|
+
process.exit(2);
|
|
162
|
+
}
|
|
163
|
+
delete process.env.SRT_DEBUG;
|
|
164
|
+
const { SandboxManager } = await import("@anthropic-ai/sandbox-runtime");
|
|
165
|
+
const config = stepSandboxConfig(JSON.parse(readFileSync2(settingsFile, "utf8")), registryFile);
|
|
166
|
+
if (!config.success) {
|
|
167
|
+
console.error(`srt-step: ${settingsFile} does not hold a valid srt config: ${config.error.message}`);
|
|
168
|
+
process.exit(2);
|
|
169
|
+
}
|
|
170
|
+
await SandboxManager.initialize(config.data);
|
|
171
|
+
const recorded = new WeakSet;
|
|
172
|
+
SandboxManager.getSandboxViolationStore().subscribe((violations) => {
|
|
173
|
+
const refusals = violations.flatMap((violation) => {
|
|
174
|
+
if (recorded.has(violation))
|
|
175
|
+
return [];
|
|
176
|
+
recorded.add(violation);
|
|
177
|
+
const host = refusedTunnelHost(violation.line);
|
|
178
|
+
return host === undefined ? [] : [{ host, reason: opaqueTunnelRefusal2 }];
|
|
179
|
+
});
|
|
180
|
+
if (refusals.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
try {
|
|
183
|
+
appendSecretRefusals(registryFile, refusals);
|
|
184
|
+
} catch {}
|
|
185
|
+
});
|
|
186
|
+
const child = spawn(await SandboxManager.wrapWithSandbox(command.map(shellQuoted).join(" ")), {
|
|
187
|
+
shell: true,
|
|
188
|
+
stdio: "inherit"
|
|
189
|
+
});
|
|
190
|
+
child.on("exit", (code, signal) => {
|
|
191
|
+
SandboxManager.cleanupAfterCommand();
|
|
192
|
+
process.exit(signal === null ? code ?? 0 : signal === "SIGINT" || signal === "SIGTERM" ? 0 : 1);
|
|
193
|
+
});
|
|
194
|
+
child.on("error", (error) => {
|
|
195
|
+
console.error(`Failed to execute command: ${error.message}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
});
|
|
198
|
+
for (const signal of ["SIGINT", "SIGTERM"])
|
|
199
|
+
process.on(signal, () => child.kill(signal));
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export { egressAllowed, writeSecretRegistry, secretRefusalLog, appendSecretRefusals, parseSecretRefusals, secretFilter2, opaqueTunnelRefusal2, isOpaqueConnectReason2, runSrtStep2 };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
secretFilter2,
|
|
4
|
+
opaqueTunnelRefusal2,
|
|
5
|
+
isOpaqueConnectReason2,
|
|
6
|
+
runSrtStep2
|
|
7
|
+
} from "./chunk-3edjgnxf.js";
|
|
8
|
+
import"./chunk-rrnee9mk.js";
|
|
9
|
+
import"./chunk-v01c0k6j.js";
|
|
10
|
+
import"./chunk-d3ksv8y1.js";
|
|
11
|
+
export {
|
|
12
|
+
isOpaqueConnectReason2 as isOpaqueConnectReason,
|
|
13
|
+
opaqueTunnelRefusal2 as opaqueTunnelRefusal,
|
|
14
|
+
runSrtStep2 as runSrtStep,
|
|
15
|
+
secretFilter2 as secretFilter
|
|
16
|
+
};
|
|
@@ -1651,7 +1651,7 @@ var package_default = {
|
|
|
1651
1651
|
};
|
|
1652
1652
|
|
|
1653
1653
|
// apps/cli/src/scaffold.ts
|
|
1654
|
-
var cliVersion = "0.10.
|
|
1654
|
+
var cliVersion = "0.10.73";
|
|
1655
1655
|
var packageJson = (name, scaffold) => `${JSON.stringify({
|
|
1656
1656
|
name,
|
|
1657
1657
|
private: true,
|
|
@@ -1901,7 +1901,7 @@ var Usage = [
|
|
|
1901
1901
|
"Every run, loop, agent, verify, constraint, secret, claim, deploy, dev and kit command takes --json."
|
|
1902
1902
|
].join(`
|
|
1903
1903
|
`);
|
|
1904
|
-
var agentRuntime = promise(() => import("./chunk-
|
|
1904
|
+
var agentRuntime = promise(() => import("./chunk-0rzh8yfd.js"));
|
|
1905
1905
|
var TuiLauncher = Reference("kitman/cli/TuiLauncher", {
|
|
1906
1906
|
defaultValue: () => gen(function* () {
|
|
1907
1907
|
const tui = yield* tryPromise({
|
|
@@ -4351,9 +4351,9 @@ function claimCommand(args) {
|
|
|
4351
4351
|
}
|
|
4352
4352
|
});
|
|
4353
4353
|
}
|
|
4354
|
-
var KitWriter = map8(promise(() => import("./chunk-
|
|
4354
|
+
var KitWriter = map8(promise(() => import("./chunk-t1z7atrq.js")), (build) => build.writeKit);
|
|
4355
4355
|
var KitBuilder = Reference("kitman/cli/KitBuilder", {
|
|
4356
|
-
defaultValue: () => (directory) => flatMap3(promise(() => import("./chunk-
|
|
4356
|
+
defaultValue: () => (directory) => flatMap3(promise(() => import("./chunk-t1z7atrq.js")), (build) => build.buildKit(directory))
|
|
4357
4357
|
});
|
|
4358
4358
|
var uploadKit = (client, workspaceId, built) => gen(function* () {
|
|
4359
4359
|
yield* client.uploadKitBundle(workspaceId, built.digest, { bundle: encode2(built.bundle) });
|
package/dist/egress-proxy.ts
CHANGED
|
@@ -215,10 +215,57 @@ export const startEgressProxy = (input: {
|
|
|
215
215
|
return once(server, 'listening').then(() => server)
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
/** Passes every connection to the proxy listening on the Unix socket `socket`, which decides what leaves. */
|
|
219
|
+
export const startEgressRelay = (input: {
|
|
220
|
+
readonly socket: string
|
|
221
|
+
readonly port: number
|
|
222
|
+
readonly hostname: string
|
|
223
|
+
}): Promise<Server> => {
|
|
224
|
+
const server = createServer(client => {
|
|
225
|
+
const upstream = connect(input.socket)
|
|
226
|
+
let connected = false
|
|
227
|
+
|
|
228
|
+
client.pipe(upstream)
|
|
229
|
+
upstream.pipe(client)
|
|
230
|
+
upstream.once('connect', () => {
|
|
231
|
+
connected = true
|
|
232
|
+
})
|
|
233
|
+
upstream.on('error', () => {
|
|
234
|
+
if (connected) client.destroy()
|
|
235
|
+
else
|
|
236
|
+
client.end(
|
|
237
|
+
"HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nKitman's egress relay cannot reach the Runner's secret proxy.\n"
|
|
238
|
+
)
|
|
239
|
+
})
|
|
240
|
+
client.on('error', () => upstream.destroy())
|
|
241
|
+
}).listen(input.port, input.hostname)
|
|
242
|
+
|
|
243
|
+
return once(server, 'listening').then(() => server)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Whether a connection to the Unix socket `socket` opens. */
|
|
247
|
+
export const probeSocket = (socket: string): Promise<boolean> => {
|
|
248
|
+
const probe = connect(socket)
|
|
249
|
+
|
|
250
|
+
return once(probe, 'connect').then(
|
|
251
|
+
() => (probe.destroy(), true),
|
|
252
|
+
() => false
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const main = (argv: readonly string[]) => {
|
|
257
|
+
const [mode = '', socket = ''] = argv
|
|
258
|
+
|
|
259
|
+
if (mode === '--probe') return probeSocket(socket).then(opened => process.exit(opened ? 0 : 1))
|
|
260
|
+
|
|
261
|
+
if (mode === '--relay') return startEgressRelay({ socket, port: 3128, hostname: '0.0.0.0' })
|
|
262
|
+
|
|
263
|
+
return startEgressProxy({
|
|
264
|
+
hosts: mode.split(',').filter(host => host.length > 0),
|
|
222
265
|
port: 3128,
|
|
223
266
|
hostname: '0.0.0.0'
|
|
224
267
|
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// `bun egress-proxy.ts <comma-separated allowlist>`, `bun egress-proxy.ts --relay <socket>` or `--probe <socket>`
|
|
271
|
+
if (import.meta.main) void main(Bun.argv.slice(2))
|
package/dist/index.js
CHANGED
|
@@ -8,12 +8,12 @@ if (args[0] === "srt") {
|
|
|
8
8
|
const { runSrtCli } = await import("./chunk-rm83dbpv.js");
|
|
9
9
|
await runSrtCli();
|
|
10
10
|
} else if (args[0] === "srt-step") {
|
|
11
|
-
const { runSrtStep } = await import("./chunk-
|
|
11
|
+
const { runSrtStep } = await import("./chunk-gmnqchsc.js");
|
|
12
12
|
await runSrtStep(args.slice(1));
|
|
13
13
|
} else if (args[0] === "opencode-read") {
|
|
14
14
|
const { runOpencodeReader } = await import("./chunk-60333x3q.js");
|
|
15
15
|
runOpencodeReader();
|
|
16
16
|
} else {
|
|
17
|
-
const { runCli } = await import("./chunk-
|
|
17
|
+
const { runCli } = await import("./chunk-r8rcfjag.js");
|
|
18
18
|
runCli(args);
|
|
19
19
|
}
|
package/package.json
CHANGED
package/dist/chunk-1w81w56v.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
getOrUndefined,
|
|
4
|
-
flatMap,
|
|
5
|
-
toArray,
|
|
6
|
-
decodeUnknownOption,
|
|
7
|
-
optionalKey,
|
|
8
|
-
String4,
|
|
9
|
-
Struct,
|
|
10
|
-
Record,
|
|
11
|
-
ArraySchema,
|
|
12
|
-
decodeJsonStringOption,
|
|
13
|
-
encodeUnknownJsonStringSync
|
|
14
|
-
} from "./chunk-d3ksv8y1.js";
|
|
15
|
-
import {
|
|
16
|
-
replaceSecretReferences,
|
|
17
|
-
secretReferencesIn
|
|
18
|
-
} from "./chunk-rrnee9mk.js";
|
|
19
|
-
|
|
20
|
-
// packages/agent-runtime/src/secret-proxy.ts
|
|
21
|
-
import { appendFileSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
22
|
-
|
|
23
|
-
// packages/agent-runtime/src/egress-proxy.ts
|
|
24
|
-
var egressAllowed = (hosts, host, port) => {
|
|
25
|
-
const target = host.toLowerCase();
|
|
26
|
-
return hosts.some((entry) => {
|
|
27
|
-
const [name = "", entryPort] = entry.toLowerCase().split(":");
|
|
28
|
-
if (entryPort !== undefined && Number(entryPort) !== port)
|
|
29
|
-
return false;
|
|
30
|
-
return name.startsWith("*.") ? target.endsWith(name.slice(1)) : target === name;
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
if (false)
|
|
34
|
-
;
|
|
35
|
-
|
|
36
|
-
// packages/agent-runtime/src/secret-proxy.ts
|
|
37
|
-
var basicPrefix = /^basic\s+/i;
|
|
38
|
-
var basicCredential = (value) => {
|
|
39
|
-
if (!basicPrefix.test(value))
|
|
40
|
-
return;
|
|
41
|
-
const decoded = Buffer.from(value.replace(basicPrefix, ""), "base64").toString("utf8");
|
|
42
|
-
return secretReferencesIn(decoded).length === 0 ? undefined : decoded;
|
|
43
|
-
};
|
|
44
|
-
var referencesInHeader = (value) => [
|
|
45
|
-
...new Set([...secretReferencesIn(value), ...secretReferencesIn(basicCredential(value) ?? "")])
|
|
46
|
-
];
|
|
47
|
-
var swapped = (value, registry) => {
|
|
48
|
-
const swap = (reference) => registry[reference]?.value ?? reference;
|
|
49
|
-
const basic = basicCredential(value);
|
|
50
|
-
return basic === undefined ? replaceSecretReferences(value, swap) : `Basic ${Buffer.from(replaceSecretReferences(basic, swap)).toString("base64")}`;
|
|
51
|
-
};
|
|
52
|
-
var decideSecretRequest = (request, readRegistry) => {
|
|
53
|
-
const carrying = request.headers.flatMap(([name, value]) => {
|
|
54
|
-
const references = referencesInHeader(value);
|
|
55
|
-
return references.length === 0 ? [] : [{ name, value, references }];
|
|
56
|
-
});
|
|
57
|
-
const inTarget = secretReferencesIn(request.target);
|
|
58
|
-
if (carrying.length === 0 && inTarget.length === 0)
|
|
59
|
-
return { action: "allow" };
|
|
60
|
-
const registry = readRegistry();
|
|
61
|
-
const refusals = [
|
|
62
|
-
...inTarget.map((reference) => ({
|
|
63
|
-
reference,
|
|
64
|
-
host: request.host,
|
|
65
|
-
reason: "A reference is swapped only in a header, never in a URL."
|
|
66
|
-
})),
|
|
67
|
-
...[...new Set(carrying.flatMap((header) => header.references))].flatMap((reference) => {
|
|
68
|
-
const entry = registry?.[reference];
|
|
69
|
-
const reason = entry === undefined ? "The reference is unknown or revoked." : request.scheme !== "https" ? "A reference is swapped only over HTTPS." : !egressAllowed(entry.hosts, request.host, request.port) ? `The Secret may be sent only to ${entry.hosts.join(", ")}.` : undefined;
|
|
70
|
-
return reason === undefined ? [] : [{ reference, host: request.host, reason }];
|
|
71
|
-
})
|
|
72
|
-
];
|
|
73
|
-
if (refusals.length > 0)
|
|
74
|
-
return {
|
|
75
|
-
action: "deny",
|
|
76
|
-
reason: `Kitman refused this request: ${refusals.map((refusal) => `${refusal.reference}: ${refusal.reason}`).join(" ")}`,
|
|
77
|
-
refusals
|
|
78
|
-
};
|
|
79
|
-
const names = new Set(carrying.map((header) => header.name.toLowerCase()));
|
|
80
|
-
return {
|
|
81
|
-
action: "allow",
|
|
82
|
-
setHeaders: request.headers.filter(([name]) => names.has(name.toLowerCase())).map(([name, value]) => [name, registry === undefined ? value : swapped(value, registry)])
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
var Registry = Record(String4, Struct({ value: String4, hosts: ArraySchema(String4) }));
|
|
86
|
-
var Refusal = Struct({
|
|
87
|
-
reference: optionalKey(String4),
|
|
88
|
-
host: String4,
|
|
89
|
-
reason: String4
|
|
90
|
-
});
|
|
91
|
-
var readSecretRegistry = (file) => {
|
|
92
|
-
try {
|
|
93
|
-
return getOrUndefined(flatMap(decodeJsonStringOption(readFileSync(file, "utf8")), decodeUnknownOption(Registry)));
|
|
94
|
-
} catch {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
var writeSecretRegistry = (file, registry) => {
|
|
99
|
-
const next = `${file}.next`;
|
|
100
|
-
writeFileSync(next, encodeUnknownJsonStringSync(registry), { mode: 384 });
|
|
101
|
-
renameSync(next, file);
|
|
102
|
-
};
|
|
103
|
-
var secretRefusalLog = (registryFile) => `${registryFile}.refused`;
|
|
104
|
-
var appendSecretRefusals = (registryFile, refusals) => appendFileSync(secretRefusalLog(registryFile), refusals.map((refusal) => `${encodeUnknownJsonStringSync(refusal)}
|
|
105
|
-
`).join(""), { mode: 384 });
|
|
106
|
-
var parseSecretRefusals = (text) => text.split(`
|
|
107
|
-
`).flatMap((line) => toArray(flatMap(decodeJsonStringOption(line), decodeUnknownOption(Refusal))));
|
|
108
|
-
|
|
109
|
-
export { decideSecretRequest, readSecretRegistry, writeSecretRegistry, secretRefusalLog, appendSecretRefusals, parseSecretRefusals };
|
package/dist/chunk-wtpwkjws.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import"./chunk-d3ksv8y1.js";
|
|
3
|
-
import"./chunk-rrnee9mk.js";
|
|
4
|
-
import"./chunk-v01c0k6j.js";
|
|
5
|
-
import {
|
|
6
|
-
decideSecretRequest,
|
|
7
|
-
readSecretRegistry,
|
|
8
|
-
appendSecretRefusals
|
|
9
|
-
} from "./chunk-1w81w56v.js";
|
|
10
|
-
|
|
11
|
-
// packages/agent-runtime/src/srt-step.ts
|
|
12
|
-
import { SandboxRuntimeConfigSchema } from "@anthropic-ai/sandbox-runtime";
|
|
13
|
-
import { spawn } from "child_process";
|
|
14
|
-
import { readFileSync } from "fs";
|
|
15
|
-
var secretFilter = (registryFile) => (request, info) => {
|
|
16
|
-
const url = new URL(request.url);
|
|
17
|
-
const decision = decideSecretRequest({
|
|
18
|
-
headers: [...request.headers.entries()],
|
|
19
|
-
host: info?.target?.host ?? url.hostname,
|
|
20
|
-
port: info?.target?.port ?? (url.port === "" ? url.protocol === "https:" ? 443 : 80 : Number(url.port)),
|
|
21
|
-
target: info?.requestTarget ?? `${url.pathname}${url.search}`,
|
|
22
|
-
scheme: info?.scheme ?? (url.protocol === "https:" ? "https" : "http")
|
|
23
|
-
}, () => readSecretRegistry(registryFile));
|
|
24
|
-
if (decision.action === "allow")
|
|
25
|
-
return Promise.resolve(decision.setHeaders === undefined ? { action: "allow" } : { action: "allow", setHeaders: decision.setHeaders.map(([name, value]) => [name, value]) });
|
|
26
|
-
try {
|
|
27
|
-
appendSecretRefusals(registryFile, decision.refusals);
|
|
28
|
-
} catch {}
|
|
29
|
-
return Promise.resolve({ action: "deny", status: 403, mark: "secret-refused", reason: decision.reason });
|
|
30
|
-
};
|
|
31
|
-
var stepSandboxConfig = (settings, registryFile) => {
|
|
32
|
-
const parsed = SandboxRuntimeConfigSchema.safeParse({
|
|
33
|
-
...settings,
|
|
34
|
-
network: { ...settings.network, tlsTerminate: {}, refuseOpaqueTunnels: true }
|
|
35
|
-
});
|
|
36
|
-
return parsed.success ? {
|
|
37
|
-
success: true,
|
|
38
|
-
data: { ...parsed.data, network: { ...parsed.data.network, filterRequest: secretFilter(registryFile) } }
|
|
39
|
-
} : parsed;
|
|
40
|
-
};
|
|
41
|
-
var opaqueTunnel = /^deny (?:socks-tunnel (\S+) \(refuseOpaqueTunnels\)|http-request CONNECT (\S+) \((?:tunnel carries no TLS|this proxy does not tunnel CONNECTs)[^)]*\))/;
|
|
42
|
-
var refusedTunnelHost = (line) => {
|
|
43
|
-
const match = opaqueTunnel.exec(line);
|
|
44
|
-
return match === null ? undefined : match[1] ?? match[2];
|
|
45
|
-
};
|
|
46
|
-
var shellQuoted = (arg) => `'${arg.replaceAll("'", "'\\''")}'`;
|
|
47
|
-
var argument = (args, flag) => {
|
|
48
|
-
const at = args.indexOf(flag);
|
|
49
|
-
return at === -1 ? undefined : args[at + 1];
|
|
50
|
-
};
|
|
51
|
-
var runSrtStep = async (args) => {
|
|
52
|
-
const separator = args.indexOf("--");
|
|
53
|
-
const settingsFile = argument(args, "--settings");
|
|
54
|
-
const registryFile = argument(args, "--secrets");
|
|
55
|
-
const command = separator === -1 ? [] : args.slice(separator + 1);
|
|
56
|
-
if (settingsFile === undefined || registryFile === undefined || command.length === 0) {
|
|
57
|
-
console.error("Usage: srt-step --settings <file> --secrets <registry> -- <command>");
|
|
58
|
-
process.exit(2);
|
|
59
|
-
}
|
|
60
|
-
delete process.env.SRT_DEBUG;
|
|
61
|
-
const { SandboxManager } = await import("@anthropic-ai/sandbox-runtime");
|
|
62
|
-
const config = stepSandboxConfig(JSON.parse(readFileSync(settingsFile, "utf8")), registryFile);
|
|
63
|
-
if (!config.success) {
|
|
64
|
-
console.error(`srt-step: ${settingsFile} does not hold a valid srt config: ${config.error.message}`);
|
|
65
|
-
process.exit(2);
|
|
66
|
-
}
|
|
67
|
-
await SandboxManager.initialize(config.data);
|
|
68
|
-
const recorded = new WeakSet;
|
|
69
|
-
SandboxManager.getSandboxViolationStore().subscribe((violations) => {
|
|
70
|
-
const refusals = violations.flatMap((violation) => {
|
|
71
|
-
if (recorded.has(violation))
|
|
72
|
-
return [];
|
|
73
|
-
recorded.add(violation);
|
|
74
|
-
const host = refusedTunnelHost(violation.line);
|
|
75
|
-
return host === undefined ? [] : [{ host, reason: "A tunnel Kitman cannot see into, such as SOCKS or a CONNECT without TLS, is refused." }];
|
|
76
|
-
});
|
|
77
|
-
if (refusals.length === 0)
|
|
78
|
-
return;
|
|
79
|
-
try {
|
|
80
|
-
appendSecretRefusals(registryFile, refusals);
|
|
81
|
-
} catch {}
|
|
82
|
-
});
|
|
83
|
-
const child = spawn(await SandboxManager.wrapWithSandbox(command.map(shellQuoted).join(" ")), {
|
|
84
|
-
shell: true,
|
|
85
|
-
stdio: "inherit"
|
|
86
|
-
});
|
|
87
|
-
child.on("exit", (code, signal) => {
|
|
88
|
-
SandboxManager.cleanupAfterCommand();
|
|
89
|
-
process.exit(signal === null ? code ?? 0 : signal === "SIGINT" || signal === "SIGTERM" ? 0 : 1);
|
|
90
|
-
});
|
|
91
|
-
child.on("error", (error) => {
|
|
92
|
-
console.error(`Failed to execute command: ${error.message}`);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
});
|
|
95
|
-
for (const signal of ["SIGINT", "SIGTERM"])
|
|
96
|
-
process.on(signal, () => child.kill(signal));
|
|
97
|
-
};
|
|
98
|
-
export {
|
|
99
|
-
runSrtStep
|
|
100
|
-
};
|
|
@@ -35,6 +35,14 @@ import {
|
|
|
35
35
|
} from "./chunk-d3ksv8y1.js";
|
|
36
36
|
import"./chunk-rrnee9mk.js";
|
|
37
37
|
import"./chunk-v01c0k6j.js";
|
|
38
|
+
import {
|
|
39
|
+
decodeText,
|
|
40
|
+
mkString,
|
|
41
|
+
FileSystem,
|
|
42
|
+
Path,
|
|
43
|
+
withDefault,
|
|
44
|
+
String2
|
|
45
|
+
} from "./chunk-dk0ftqwg.js";
|
|
38
46
|
import {
|
|
39
47
|
SecureRandom,
|
|
40
48
|
layerWebCrypto,
|
|
@@ -47,14 +55,6 @@ import {
|
|
|
47
55
|
kitmanMcpServer,
|
|
48
56
|
RunnerRpcProtocolVersion
|
|
49
57
|
} from "./chunk-9e452fhe.js";
|
|
50
|
-
import {
|
|
51
|
-
decodeText,
|
|
52
|
-
mkString,
|
|
53
|
-
FileSystem,
|
|
54
|
-
Path,
|
|
55
|
-
withDefault,
|
|
56
|
-
String2
|
|
57
|
-
} from "./chunk-dk0ftqwg.js";
|
|
58
58
|
import {
|
|
59
59
|
Packument,
|
|
60
60
|
tarballMatches
|