@kitman-sh/sdk 0.6.0 → 0.7.1

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.
@@ -4,12 +4,12 @@ import {
4
4
  KitInstallationDeclaration,
5
5
  KitAssignmentDeclaration,
6
6
  PackageDeclarations
7
- } from "../chunk-jnxc0c54.js";
7
+ } from "../chunk-zp7s46pz.js";
8
8
  import {
9
9
  RunnerRpcProtocolVersion,
10
10
  describeFlag2,
11
11
  Described2
12
- } from "../chunk-f3d6cvkg.js";
12
+ } from "../chunk-7hwhxthh.js";
13
13
 
14
14
  // packages/core/src/crypto.ts
15
15
  import { createHash, createHmac, createSign, randomBytes, timingSafeEqual } from "crypto";
@@ -1,9 +1,9 @@
1
1
  // @bun
2
2
  import {
3
+ AgentSandboxNetwork,
3
4
  guard,
4
5
  Id,
5
6
  Timestamp,
6
- AgentSandboxNetwork,
7
7
  LoopStepOutput,
8
8
  StepBrief,
9
9
  LoopManifest,
@@ -19,7 +19,7 @@ import {
19
19
  executeLoop2,
20
20
  LoopCheckError2,
21
21
  checkLoop2
22
- } from "./chunk-jnxc0c54.js";
22
+ } from "./chunk-zp7s46pz.js";
23
23
  // packages/core/src/repo-binding.ts
24
24
  import * as Schema from "effect/Schema";
25
25
  var RepoBindingPathPrefixPattern = "^(?!/)(?!.*\\\\)(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*//)\\S+$";
@@ -64,6 +64,10 @@ var RepoBinding = Schema.Struct({
64
64
  expectedCommitSha: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1))),
65
65
  credentialRefs: Schema.Array(RepoBindingCredentialRef),
66
66
  status: RepoBindingStatus,
67
+ webhook: Schema.optionalKey(Schema.Struct({
68
+ status: Schema.Literals(["active", "missing"]),
69
+ message: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(240)))
70
+ })),
67
71
  createdAt: Timestamp,
68
72
  updatedAt: Timestamp
69
73
  });
@@ -171,6 +175,7 @@ var OrchestrationWorkUnit = Schema2.Struct({
171
175
  granted: Schema2.Array(Schema2.String),
172
176
  lease: WorkUnitLease,
173
177
  journal: RunJournal,
178
+ network: Schema2.optionalKey(AgentSandboxNetwork),
174
179
  program: Schema2.Struct({
175
180
  loopId: Schema2.String.check(Schema2.isMinLength(1)),
176
181
  credential: Schema2.String.check(Schema2.isMinLength(1))
@@ -837,7 +842,11 @@ import * as Layer2 from "effect/Layer";
837
842
  import * as Stream from "effect/Stream";
838
843
  import { RpcClient, RpcSerialization } from "effect/rpc";
839
844
  import * as Socket from "effect/socket/Socket";
840
- var runnerWebSocketProtocol = (url) => RpcClient.layerProtocolSocket({ retryTransientErrors: true }).pipe(Layer2.provide(Socket.layerWebSocket(url)), Layer2.provide(Socket.layerWebSocketConstructorGlobal), Layer2.provide(RpcSerialization.layerJson));
845
+ var proxiedWebSocket = (proxy) => Layer2.succeed(Socket.WebSocketConstructor)((target) => {
846
+ const options = { proxy };
847
+ return Reflect.construct(globalThis.WebSocket, [target, options]);
848
+ });
849
+ var runnerWebSocketProtocol = (url, options = {}) => RpcClient.layerProtocolSocket({ retryTransientErrors: true }).pipe(Layer2.provide(Socket.layerWebSocket(url)), Layer2.provide(options.proxy === undefined ? Socket.layerWebSocketConstructorGlobal : proxiedWebSocket(options.proxy)), Layer2.provide(RpcSerialization.layerJson));
841
850
  function runnerRpcUrl(apiUrl) {
842
851
  const url = new URL("/v1/runners/rpc", apiUrl);
843
852
  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
@@ -915,6 +924,81 @@ var programSecrets2 = (rpc, lease) => rpc.programSecrets({ lease }).pipe(Effect4
915
924
  lease,
916
925
  outcome: { status: "failed", failureSummary: `Kitman refused the Loop its Secrets: ${error.message}` }
917
926
  }), Option2.none())));
927
+ var programHostsEnv2 = "KITMAN_PROGRAM_HOSTS";
928
+ var undeclaredHostsEnv2 = "KITMAN_UNDECLARED_HOSTS";
929
+ var allows = (hosts, host) => hosts.some((entry) => entry === host || entry.startsWith("*.") && host.endsWith(entry.slice(1)));
930
+ var redirectStatuses = new Set([301, 302, 303, 307, 308]);
931
+ var maxRedirects = 20;
932
+ var requestBodyHeaders = [
933
+ "content-type",
934
+ "content-length",
935
+ "content-encoding",
936
+ "content-language",
937
+ "content-location"
938
+ ];
939
+ var credentialHeaders = ["authorization", "cookie", "proxy-authorization"];
940
+ var networkGuard2 = (input) => {
941
+ const warned = new Set;
942
+ let refused;
943
+ const refusalOf = (host) => {
944
+ if (allows(input.hosts, host))
945
+ return;
946
+ const undeclared = `The program called ${host}, a host its Loop does not declare. Add it to \`network\` in defineLoop`;
947
+ if (input.undeclared === "fail") {
948
+ refused ??= `${undeclared}.`;
949
+ return new Error(`${undeclared}.`);
950
+ }
951
+ if (!warned.has(host))
952
+ input.warn(`${undeclared}; a shared Runner refuses it.`);
953
+ warned.add(host);
954
+ return;
955
+ };
956
+ const follow = (first) => Effect4.gen(function* () {
957
+ let request = first;
958
+ for (let hops = 0;; hops++) {
959
+ const refusal = refusalOf(new URL(request.url).hostname);
960
+ if (refusal !== undefined)
961
+ return yield* Effect4.die(refusal);
962
+ const sent = request;
963
+ if (sent.redirect !== "follow")
964
+ return yield* Effect4.promise(() => input.fetch(sent));
965
+ const resend = sent.clone();
966
+ const response = yield* Effect4.promise(() => input.fetch(sent, { redirect: "manual" }));
967
+ const location = response.headers.get("location");
968
+ if (!redirectStatuses.has(response.status) || location === null)
969
+ return response;
970
+ if (hops === maxRedirects)
971
+ return yield* Effect4.die(new TypeError("The program followed too many redirects."));
972
+ response.body?.cancel();
973
+ const url = new URL(location, sent.url);
974
+ const asGet = response.status === 303 && sent.method !== "HEAD" || (response.status === 301 || response.status === 302) && sent.method === "POST";
975
+ const headers = new Headers(Array.from(resend.headers));
976
+ if (asGet)
977
+ for (const name of requestBodyHeaders)
978
+ headers.delete(name);
979
+ if (url.origin !== new URL(sent.url).origin)
980
+ for (const name of credentialHeaders)
981
+ headers.delete(name);
982
+ request = new Request(url.href, {
983
+ method: asGet ? "GET" : resend.method,
984
+ headers,
985
+ body: asGet || resend.body === null ? null : yield* Effect4.promise(() => resend.arrayBuffer()),
986
+ signal: sent.signal
987
+ });
988
+ }
989
+ });
990
+ const fetch = (resource, init) => Effect4.runPromise(Effect4.suspend(() => follow(resource instanceof Request ? new Request(resource, init) : new Request(resource instanceof URL ? resource.href : resource, init))));
991
+ const refusal = () => refused === undefined ? undefined : { status: "failed", failureSummary: refused };
992
+ const journal = (journal) => ({
993
+ ...journal,
994
+ suspend: Effect4.suspend(() => {
995
+ const failed = refusal();
996
+ return failed === undefined ? journal.suspend : journal.finish(failed);
997
+ }),
998
+ finish: (outcome) => Effect4.suspend(() => journal.finish(refusal() ?? outcome))
999
+ });
1000
+ return { fetch, journal };
1001
+ };
918
1002
  var describe = (config) => Effect4.gen(function* () {
919
1003
  const loops = yield* Effect4.forEach(config.loops, loopManifest2);
920
1004
  for (const loop of config.loops)
@@ -940,19 +1024,32 @@ var host = (config) => Effect4.gen(function* () {
940
1024
  const loop = config.loops.find((candidate) => candidate.id === loopId);
941
1025
  if (loop === undefined)
942
1026
  return yield* new ProgramRefused({ reason: `The Package declares no Loop "${loopId}"` });
1027
+ const hosts = yield* Config.option(Config.String(programHostsEnv2));
1028
+ const undeclared = yield* Config.option(Config.Literals(["fail", "warn"], undeclaredHostsEnv2));
1029
+ const proxy = yield* Config.option(Config.String("HTTPS_PROXY"));
943
1030
  const rpc = yield* makeRunnerRpcClient({
944
1031
  runnerId: work.lease.leaseOwnerId,
945
1032
  bearer: Effect4.sync(() => Redacted2.value(credential)),
946
- protocol: runnerWebSocketProtocol(runnerRpcUrl(apiUrl))
1033
+ protocol: runnerWebSocketProtocol(runnerRpcUrl(apiUrl), Option2.isSome(proxy) ? { proxy: proxy.value } : {})
947
1034
  });
948
1035
  const secrets = (loop.secrets ?? []).length === 0 ? Option2.some({}) : yield* programSecrets2(rpc, programLease2(work));
949
1036
  if (Option2.isNone(secrets))
950
1037
  return 0;
951
- return yield* runProgram2(loop, work, rpcProgramJournal2(rpc, work)).pipe(Effect4.provideService(ConfigProvider.ConfigProvider, ConfigProvider.fromUnknown(secrets.value)), Effect4.as(0), Effect4.catchTag("JournalWriteFailed", (error) => Effect4.as(Effect4.logError(error.message), 1)));
1038
+ const guard = Option2.isNone(hosts) ? undefined : networkGuard2({
1039
+ hosts: hosts.value.split(",").filter((host) => host.length > 0),
1040
+ undeclared: Option2.getOrElse(undeclared, () => "warn"),
1041
+ fetch: globalThis.fetch,
1042
+ warn: (line) => void process.stderr.write(`${line}
1043
+ `)
1044
+ });
1045
+ if (guard !== undefined)
1046
+ globalThis.fetch = Object.assign(guard.fetch, { preconnect: globalThis.fetch.preconnect });
1047
+ const journal = rpcProgramJournal2(rpc, work);
1048
+ return yield* runProgram2(loop, work, guard === undefined ? journal : guard.journal(journal)).pipe(Effect4.provideService(ConfigProvider.ConfigProvider, ConfigProvider.fromUnknown(secrets.value)), Effect4.as(0), Effect4.catchTag("JournalWriteFailed", (error) => Effect4.as(Effect4.logError(error.message), 1)));
952
1049
  }).pipe(Effect4.scoped, Effect4.catch((error) => Effect4.as(Effect4.logError(error.message), 2)));
953
1050
  var runPackage2 = (config) => {
954
1051
  const program = process.argv.includes(describeFlag2) ? describe(config) : host(config);
955
1052
  BunRuntime.runMain(program.pipe(Effect4.flatMap((code) => Effect4.sync(() => process.exit(code)))));
956
1053
  };
957
1054
 
958
- export { RunnerRpcProtocolVersion, JournalWriteFailed2, refusedStep2, runProgram2, programLease2, rpcProgramJournal2, describeFlag2, Described2, programSecrets2, runPackage2 };
1055
+ export { RunnerRpcProtocolVersion, JournalWriteFailed2, refusedStep2, runProgram2, programLease2, rpcProgramJournal2, describeFlag2, Described2, programSecrets2, programHostsEnv2, undeclaredHostsEnv2, networkGuard2, runPackage2 };