@kitman-sh/sdk 0.8.0 → 0.10.52
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/build/index.js +531 -116
- package/dist/{chunk-36jhc745.js → chunk-tcbgb4wm.js} +1841 -1574
- package/dist/{chunk-160ww64c.js → chunk-zvzw3zs4.js} +939 -535
- package/dist/loop/index.js +117 -3
- package/dist/program/index.js +10 -6
- package/package.json +6 -6
- package/types/packages/core/src/agent-capabilities.d.ts +6 -0
- package/types/packages/core/src/agent-environment.d.ts +32 -216
- package/types/packages/core/src/agent-sandbox-policy.d.ts +17 -3
- package/types/packages/core/src/change-request-review.d.ts +20 -5
- package/types/packages/core/src/change-request.d.ts +33 -208
- package/types/packages/core/src/crypto.d.ts +2 -0
- package/types/packages/core/src/index.d.ts +8 -4
- package/types/packages/core/src/{package-declarations.d.ts → kit-declarations.d.ts} +84 -180
- package/types/packages/core/src/kit-install.d.ts +130 -0
- package/types/packages/core/src/kit-setup.d.ts +316 -0
- package/types/packages/core/src/kit.d.ts +302 -1444
- package/types/packages/core/src/loop-layers.d.ts +33 -4
- package/types/packages/core/src/loop.d.ts +325 -55
- package/types/packages/core/src/marketplace.d.ts +51 -10
- package/types/packages/core/src/my-teams.d.ts +135 -20
- package/types/packages/core/src/network-host.d.ts +41 -0
- package/types/packages/core/src/notification.d.ts +8 -0
- package/types/packages/core/src/npm-registry.d.ts +65 -0
- package/types/packages/core/src/proposal.d.ts +0 -76
- package/types/packages/core/src/push.d.ts +68 -0
- package/types/packages/core/src/question-recommendation.d.ts +28 -0
- package/types/packages/core/src/redaction.d.ts +15 -3
- package/types/packages/core/src/resource-claim.d.ts +79 -0
- package/types/packages/core/src/run-display.d.ts +7 -1
- package/types/packages/core/src/run-graph.d.ts +1 -1
- package/types/packages/core/src/run-launch.d.ts +15 -2
- package/types/packages/core/src/run-queue.d.ts +28 -0
- package/types/packages/core/src/runner-eligibility.d.ts +21 -2
- package/types/packages/core/src/runner.d.ts +46 -15
- package/types/packages/core/src/secret.d.ts +18 -1
- package/types/packages/core/src/step-brief.d.ts +21 -7
- package/types/packages/core/src/sync-kit.d.ts +8 -0
- package/types/packages/core/src/task-detail.d.ts +15 -2
- package/types/packages/core/src/task.d.ts +9 -0
- package/types/packages/core/src/transcript.d.ts +24 -4
- package/types/packages/core/src/trust-panel.d.ts +1 -1
- package/types/packages/core/src/waiting-on.d.ts +14 -0
- package/types/packages/core/src/work-unit.d.ts +339 -8
- package/types/packages/live/src/rpc/runner-client.d.ts +1229 -566
- package/types/packages/live/src/rpc/runner-group.d.ts +2851 -441
- package/types/packages/sdk/src/build/index.d.ts +45 -9
- package/types/packages/sdk/src/loop/activities.d.ts +382 -2
- package/types/packages/sdk/src/loop/claims.d.ts +57 -0
- package/types/packages/sdk/src/loop/config.d.ts +38 -31
- package/types/packages/sdk/src/loop/continuing.d.ts +22 -0
- package/types/packages/sdk/src/loop/define.d.ts +30 -5
- package/types/packages/sdk/src/loop/evidence.d.ts +18 -3
- package/types/packages/sdk/src/loop/index.d.ts +4 -0
- package/types/packages/sdk/src/loop/measure.d.ts +132 -0
- package/types/packages/sdk/src/loop/plan.d.ts +2 -2
- package/types/packages/sdk/src/loop/steps.d.ts +127 -17
- package/types/packages/sdk/src/loop/sync.d.ts +53 -0
- package/types/packages/sdk/src/loop/test-loop.d.ts +25 -5
- package/types/packages/sdk/src/program/engine.d.ts +116 -17
- package/types/packages/sdk/src/program/host.d.ts +200 -38
- package/types/packages/core/src/engineering-resources.d.ts +0 -24
- package/types/packages/core/src/package.d.ts +0 -369
package/dist/build/index.js
CHANGED
|
@@ -1,81 +1,229 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
+
canonicalJson,
|
|
4
|
+
KitDigest,
|
|
3
5
|
AgentDeclaration,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
PackageDeclarations
|
|
7
|
-
} from "../chunk-36jhc745.js";
|
|
6
|
+
KitDeclarations
|
|
7
|
+
} from "../chunk-tcbgb4wm.js";
|
|
8
8
|
import {
|
|
9
9
|
digestSha256,
|
|
10
|
+
digestSha512,
|
|
11
|
+
digestWith,
|
|
12
|
+
kitmanMcpServer,
|
|
10
13
|
RunnerRpcProtocolVersion,
|
|
11
14
|
describeFlag2,
|
|
12
15
|
Described2
|
|
13
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-zvzw3zs4.js";
|
|
14
17
|
|
|
15
|
-
// packages/
|
|
18
|
+
// packages/core/src/json.ts
|
|
19
|
+
import * as Schema from "effect/Schema";
|
|
20
|
+
var JsonFromString = Schema.fromJsonString(Schema.Json);
|
|
21
|
+
var decodeJsonString = Schema.decodeUnknownEffect(JsonFromString);
|
|
22
|
+
var decodeJsonStringOption = Schema.decodeUnknownOption(JsonFromString);
|
|
23
|
+
var encodeJsonStringSync = Schema.encodeSync(JsonFromString);
|
|
24
|
+
var encodePrettyJsonString = Schema.encodeEffect(Schema.fromJsonString(Schema.Json, { space: 2 }));
|
|
25
|
+
var decodeJsonStringSync = Schema.decodeUnknownSync(JsonFromString);
|
|
26
|
+
var encodeUnknownJsonStringSync = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
|
|
27
|
+
|
|
28
|
+
// packages/core/src/npm-registry.ts
|
|
29
|
+
import * as Config from "effect/Config";
|
|
30
|
+
import * as Context from "effect/Context";
|
|
31
|
+
import * as Data from "effect/Data";
|
|
16
32
|
import * as Effect from "effect/Effect";
|
|
33
|
+
import * as Option from "effect/Option";
|
|
34
|
+
import * as Predicate from "effect/Predicate";
|
|
35
|
+
import * as Schema2 from "effect/Schema";
|
|
36
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
37
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
38
|
+
import * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
39
|
+
var KitManifest = Schema2.Struct({
|
|
40
|
+
bundle: Schema2.String,
|
|
41
|
+
digest: KitDigest,
|
|
42
|
+
protocolVersion: Schema2.Int,
|
|
43
|
+
declarations: Schema2.String,
|
|
44
|
+
setup: Schema2.Record(KitDigest, Schema2.String)
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
class NpmRegistryError extends Data.TaggedError("NpmRegistryError") {
|
|
48
|
+
}
|
|
49
|
+
var Dist = Schema2.Struct({
|
|
50
|
+
tarball: Schema2.String,
|
|
51
|
+
integrity: Schema2.optionalKey(Schema2.String),
|
|
52
|
+
shasum: Schema2.optionalKey(Schema2.String)
|
|
53
|
+
});
|
|
54
|
+
var Packument = Schema2.Struct({
|
|
55
|
+
"dist-tags": Schema2.Record(Schema2.String, Schema2.String),
|
|
56
|
+
versions: Schema2.Record(Schema2.String, Schema2.Struct({ dist: Dist }))
|
|
57
|
+
});
|
|
58
|
+
var integrityAlgorithms = ["sha512", "sha384", "sha256", "sha1"];
|
|
59
|
+
var tarballMatches = (tarball, dist) => {
|
|
60
|
+
if (dist.integrity === undefined)
|
|
61
|
+
return dist.shasum === digestWith("sha1", tarball, "hex");
|
|
62
|
+
const hashes = dist.integrity.split(/\s+/u).map((hash) => /^([^-]+)-([^?]+)/u.exec(hash));
|
|
63
|
+
const algorithm = integrityAlgorithms.find((candidate) => hashes.some((hash) => hash?.[1] === candidate));
|
|
64
|
+
return algorithm !== undefined && hashes.some((hash) => hash?.[1] === algorithm && hash[2] === digestWith(algorithm, tarball, "base64"));
|
|
65
|
+
};
|
|
66
|
+
var PackageJson = Schema2.Struct({ kitman: Schema2.optionalKey(Schema2.Unknown) });
|
|
67
|
+
var decodePackageJson = Schema2.decodeUnknownOption(Schema2.fromJsonString(PackageJson));
|
|
68
|
+
var decodeManifest = Schema2.decodeUnknownOption(KitManifest);
|
|
69
|
+
var decodeJson = Schema2.decodeUnknownOption(Schema2.fromJsonString(Schema2.Unknown));
|
|
70
|
+
var gunzip = (bytes) => Effect.tryPromise(() => new Response(new Blob([new Uint8Array(bytes)]).stream().pipeThrough(new DecompressionStream("gzip"))).arrayBuffer()).pipe(Effect.map((buffer) => new Uint8Array(buffer)));
|
|
71
|
+
var text = (bytes) => new TextDecoder().decode(bytes).replace(/\0.*$/su, "");
|
|
72
|
+
var untar = (tar) => {
|
|
73
|
+
const files = new Map;
|
|
74
|
+
let longName;
|
|
75
|
+
for (let offset = 0;offset + 512 <= tar.length; ) {
|
|
76
|
+
const header = tar.subarray(offset, offset + 512);
|
|
77
|
+
if (header.every((byte) => byte === 0))
|
|
78
|
+
break;
|
|
79
|
+
const size = Number.parseInt(text(header.subarray(124, 136)).trim() || "0", 8);
|
|
80
|
+
if (!Number.isSafeInteger(size) || size < 0 || offset + 512 + size > tar.length)
|
|
81
|
+
return;
|
|
82
|
+
const type = String.fromCharCode(header[156] ?? 0);
|
|
83
|
+
const prefix = text(header.subarray(345, 500));
|
|
84
|
+
const body = tar.subarray(offset + 512, offset + 512 + size);
|
|
85
|
+
const name = longName ?? (prefix === "" ? text(header.subarray(0, 100)) : `${prefix}/${text(header.subarray(0, 100))}`);
|
|
86
|
+
longName = undefined;
|
|
87
|
+
if (type === "x")
|
|
88
|
+
longName = /(?:^|\n)\d+ path=([^\n]*)\n/u.exec(new TextDecoder().decode(body))?.[1];
|
|
89
|
+
else if (type === "L")
|
|
90
|
+
longName = text(body);
|
|
91
|
+
else if (type === "0" || type === "\x00")
|
|
92
|
+
files.set(name.replace(/^[^/]*\//u, ""), body);
|
|
93
|
+
offset += 512 + Math.ceil(size / 512) * 512;
|
|
94
|
+
}
|
|
95
|
+
return files;
|
|
96
|
+
};
|
|
97
|
+
var registryUrl = Config.String("NPM_CONFIG_REGISTRY").pipe(Config.withDefault("https://registry.npmjs.org"));
|
|
98
|
+
var npmRegistryAt = (url = registryUrl) => {
|
|
99
|
+
const base = (Predicate.isString(url) ? Effect.succeed(url) : Effect.orDie(url)).pipe(Effect.map((value) => value.replace(/\/+$/u, "")));
|
|
100
|
+
const unreachable = (name, version) => (error) => new NpmRegistryError({
|
|
101
|
+
kind: "unreachable",
|
|
102
|
+
message: `npm did not answer for ${name}@${version}: ${error.message}; try again`
|
|
103
|
+
});
|
|
104
|
+
const packument = (name, version) => Effect.gen(function* () {
|
|
105
|
+
const response = yield* HttpClient.get(`${yield* base}/${name.replace("/", "%2F")}`).pipe(Effect.mapError(unreachable(name, version)), Effect.provide(FetchHttpClient.layer));
|
|
106
|
+
if (response.status === 404)
|
|
107
|
+
return yield* new NpmRegistryError({ kind: "not_found", message: `npm has no package ${name}` });
|
|
108
|
+
return yield* HttpClientResponse.filterStatusOk(response).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Packument)), Effect.mapError(unreachable(name, version)));
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
latest: (name) => Effect.flatMap(packument(name, "latest"), (found) => {
|
|
112
|
+
const latest = found["dist-tags"].latest;
|
|
113
|
+
return latest === undefined ? Effect.fail(new NpmRegistryError({ kind: "not_found", message: `npm has no latest ${name}` })) : Effect.succeed(latest);
|
|
114
|
+
}),
|
|
115
|
+
kit: (name, wanted) => Effect.gen(function* () {
|
|
116
|
+
const found = yield* packument(name, wanted);
|
|
117
|
+
const version = found["dist-tags"][wanted] ?? wanted;
|
|
118
|
+
const dist = found.versions[version]?.dist;
|
|
119
|
+
if (dist === undefined)
|
|
120
|
+
return yield* new NpmRegistryError({ kind: "not_found", message: `npm has no ${name}@${wanted}` });
|
|
121
|
+
const tarball = yield* HttpClient.get(dist.tarball).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((response) => response.arrayBuffer), Effect.map((buffer) => new Uint8Array(buffer)), Effect.mapError(unreachable(name, version)), Effect.provide(FetchHttpClient.layer));
|
|
122
|
+
if (!tarballMatches(tarball, dist))
|
|
123
|
+
return yield* new NpmRegistryError({
|
|
124
|
+
kind: "integrity",
|
|
125
|
+
message: `${name}@${version}'s tarball does not match npm's integrity: expected ${dist.integrity ?? `sha1 ${dist.shasum}`}, got sha512-${digestSha512(tarball, "base64")}; its publisher must publish a new version`
|
|
126
|
+
});
|
|
127
|
+
const notAKit = new NpmRegistryError({
|
|
128
|
+
kind: "not_a_kit",
|
|
129
|
+
message: `${name}@${version} is not a Kitman Kit: add \`kitman kit build\` to its prepack and publish again`
|
|
130
|
+
});
|
|
131
|
+
const files = untar(yield* gunzip(tarball).pipe(Effect.mapError(() => notAKit)));
|
|
132
|
+
if (files === undefined)
|
|
133
|
+
return yield* notAKit;
|
|
134
|
+
const file = (path) => Option.fromUndefinedOr(files.get(path.replace(/^\.\//u, "")));
|
|
135
|
+
const manifest = Option.flatMap(file("package.json"), (bytes) => Option.flatMap(decodePackageJson(new TextDecoder().decode(bytes)), (json) => decodeManifest(json.kitman)));
|
|
136
|
+
if (Option.isNone(manifest))
|
|
137
|
+
return yield* notAKit;
|
|
138
|
+
const bundle = file(manifest.value.bundle);
|
|
139
|
+
const declarations = file(manifest.value.declarations);
|
|
140
|
+
const setupArchives = Object.entries(manifest.value.setup).flatMap(([digest, path]) => Option.match(file(path), {
|
|
141
|
+
onNone: () => [],
|
|
142
|
+
onSome: (archive) => digestSha256(archive, "hex") === digest ? [{ digest, archive }] : []
|
|
143
|
+
}));
|
|
144
|
+
if (Option.isNone(bundle) || Option.isNone(declarations) || digestSha256(bundle.value, "hex") !== manifest.value.digest || setupArchives.length !== Object.keys(manifest.value.setup).length)
|
|
145
|
+
return yield* notAKit;
|
|
146
|
+
const parsed = decodeJson(new TextDecoder().decode(declarations.value));
|
|
147
|
+
if (Option.isNone(parsed))
|
|
148
|
+
return yield* notAKit;
|
|
149
|
+
return {
|
|
150
|
+
name,
|
|
151
|
+
version,
|
|
152
|
+
integrity: dist.integrity ?? `sha1-${digestWith("sha1", tarball, "base64")}`,
|
|
153
|
+
bundle: bundle.value,
|
|
154
|
+
digest: manifest.value.digest,
|
|
155
|
+
protocolVersion: manifest.value.protocolVersion,
|
|
156
|
+
declarations: parsed.value,
|
|
157
|
+
setupArchives
|
|
158
|
+
};
|
|
159
|
+
})
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
var NpmRegistry = Context.Reference("kitman/core/NpmRegistry", {
|
|
163
|
+
defaultValue: () => npmRegistryAt()
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// packages/sdk/src/build/index.ts
|
|
167
|
+
import * as Config2 from "effect/Config";
|
|
168
|
+
import * as Context2 from "effect/Context";
|
|
169
|
+
import * as Effect2 from "effect/Effect";
|
|
17
170
|
import * as FileSystem from "effect/FileSystem";
|
|
18
171
|
import * as Path from "effect/Path";
|
|
19
|
-
import * as
|
|
20
|
-
import * as
|
|
172
|
+
import * as Predicate2 from "effect/Predicate";
|
|
173
|
+
import * as Schema3 from "effect/Schema";
|
|
21
174
|
import * as Stream from "effect/Stream";
|
|
175
|
+
import { Base64 } from "effect/encoding";
|
|
176
|
+
import * as FetchHttpClient2 from "effect/http/FetchHttpClient";
|
|
177
|
+
import * as HttpClient2 from "effect/http/HttpClient";
|
|
178
|
+
import * as HttpClientResponse2 from "effect/http/HttpClientResponse";
|
|
22
179
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
23
|
-
class DependencyInstallFailed extends
|
|
24
|
-
reason:
|
|
180
|
+
class DependencyInstallFailed extends Schema3.TaggedError()("DependencyInstallFailed", {
|
|
181
|
+
reason: Schema3.String
|
|
25
182
|
}) {
|
|
26
183
|
get message() {
|
|
27
184
|
return `Dependencies do not install from the lockfile: ${this.reason}`;
|
|
28
185
|
}
|
|
29
186
|
}
|
|
30
187
|
|
|
31
|
-
class BundleFailed extends
|
|
188
|
+
class BundleFailed extends Schema3.TaggedError()("BundleFailed", { reason: Schema3.String }) {
|
|
32
189
|
get message() {
|
|
33
|
-
return `The
|
|
190
|
+
return `The Kit does not bundle: ${this.reason}`;
|
|
34
191
|
}
|
|
35
192
|
}
|
|
36
193
|
|
|
37
|
-
class DeclarationsInvalid extends
|
|
38
|
-
reason:
|
|
194
|
+
class DeclarationsInvalid extends Schema3.TaggedError()("DeclarationsInvalid", {
|
|
195
|
+
reason: Schema3.String
|
|
39
196
|
}) {
|
|
40
197
|
get message() {
|
|
41
|
-
return `The
|
|
198
|
+
return `The Kit's declarations are invalid: ${this.reason}`;
|
|
42
199
|
}
|
|
43
200
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
201
|
+
|
|
202
|
+
class SourceFetchFailed extends Schema3.TaggedError()("SourceFetchFailed", {
|
|
203
|
+
reason: Schema3.String
|
|
204
|
+
}) {
|
|
205
|
+
get message() {
|
|
206
|
+
return `A setup source does not fetch: ${this.reason}`;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
var RepositoryText = Schema3.Union([Schema3.String, Schema3.Struct({ file: Schema3.String })]);
|
|
210
|
+
var ConfigAgents = Schema3.Record(Schema3.String, Schema3.Struct({
|
|
211
|
+
...AgentDeclaration.fields,
|
|
212
|
+
instructions: Schema3.optionalKey(RepositoryText)
|
|
54
213
|
}));
|
|
55
|
-
var
|
|
56
|
-
KitInstallationDeclaration,
|
|
57
|
-
KitAssignmentDeclaration,
|
|
58
|
-
Schema.Struct({
|
|
59
|
-
name: Schema.String,
|
|
60
|
-
kind: Schema.optionalKey(Schema.Literals(["technology", "brand", "domain", "process", "other"])),
|
|
61
|
-
description: Schema.optionalKey(Schema.String),
|
|
62
|
-
context: RepositoryText,
|
|
63
|
-
skills: Schema.optionalKey(Schema.Array(Schema.String))
|
|
64
|
-
})
|
|
65
|
-
]));
|
|
66
|
-
var run = (command, args, cwd) => Effect.gen(function* () {
|
|
214
|
+
var run = (command, args, cwd) => Effect2.gen(function* () {
|
|
67
215
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
68
216
|
const handle = yield* spawner.spawn(ChildProcess.make(command, args, { cwd, stdin: "ignore" }));
|
|
69
|
-
const [stdout, stderr, exitCode] = yield*
|
|
217
|
+
const [stdout, stderr, exitCode] = yield* Effect2.all([
|
|
70
218
|
Stream.mkString(Stream.decodeText(handle.stdout)),
|
|
71
219
|
Stream.mkString(Stream.decodeText(handle.stderr)),
|
|
72
220
|
handle.exitCode
|
|
73
221
|
], { concurrency: 3 });
|
|
74
222
|
return { stdout, stderr, ok: exitCode === 0 };
|
|
75
|
-
}).pipe(
|
|
223
|
+
}).pipe(Effect2.scoped);
|
|
76
224
|
var failure = (result) => (result.stderr.trim() || result.stdout.trim()).slice(-2000);
|
|
77
|
-
var provenance = (directory) =>
|
|
78
|
-
const git = (...args) => run("git", args, directory).pipe(
|
|
225
|
+
var provenance = (directory) => Effect2.gen(function* () {
|
|
226
|
+
const git = (...args) => run("git", args, directory).pipe(Effect2.map((result) => result.ok ? result.stdout.trim() : undefined), Effect2.orElseSucceed(() => {
|
|
79
227
|
return;
|
|
80
228
|
}));
|
|
81
229
|
const repository = yield* git("remote", "get-url", "origin");
|
|
@@ -87,128 +235,395 @@ var provenance = (directory) => Effect.gen(function* () {
|
|
|
87
235
|
clean: commit !== undefined && status === ""
|
|
88
236
|
};
|
|
89
237
|
});
|
|
90
|
-
var
|
|
238
|
+
var inKit = (directory, file, what) => Effect2.gen(function* () {
|
|
91
239
|
const path = yield* Path.Path;
|
|
92
240
|
const resolved = path.resolve(directory, file);
|
|
93
241
|
if (path.relative(directory, resolved).startsWith(".."))
|
|
94
|
-
return yield* new DeclarationsInvalid({ reason: `${what} ${file} is outside the
|
|
242
|
+
return yield* new DeclarationsInvalid({ reason: `${what} ${file} is outside the Kit` });
|
|
95
243
|
return resolved;
|
|
96
244
|
});
|
|
97
|
-
var readText = (directory, text, what) =>
|
|
98
|
-
if (
|
|
245
|
+
var readText = (directory, text, what) => Effect2.gen(function* () {
|
|
246
|
+
if (Predicate2.isString(text))
|
|
99
247
|
return text;
|
|
100
248
|
const fs = yield* FileSystem.FileSystem;
|
|
101
|
-
const file = yield*
|
|
102
|
-
return yield* fs.readFileString(file).pipe(
|
|
249
|
+
const file = yield* inKit(directory, text.file, what);
|
|
250
|
+
return yield* fs.readFileString(file).pipe(Effect2.mapError(() => new DeclarationsInvalid({ reason: `${what} ${text.file} does not read` })));
|
|
103
251
|
});
|
|
104
|
-
var readAgents = (directory, agents) =>
|
|
252
|
+
var readAgents = (directory, agents) => Effect2.forEach(Object.entries(agents), ([id, { instructions, ...agent }]) => instructions === undefined ? Effect2.succeed([id, agent]) : Effect2.map(readText(directory, instructions, `Agent "${id}" instructions`), (text) => [id, { ...agent, instructions: text.trim() }])).pipe(Effect2.map(Object.fromEntries));
|
|
105
253
|
var utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
106
|
-
var
|
|
254
|
+
var setupFile = (bytes) => {
|
|
255
|
+
try {
|
|
256
|
+
const text = utf8.decode(bytes);
|
|
257
|
+
return text.includes("\x00") ? { base64: Base64.encode(bytes) } : { utf8: text };
|
|
258
|
+
} catch {
|
|
259
|
+
return { base64: Base64.encode(bytes) };
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var confined = (boundary, file, what) => Effect2.gen(function* () {
|
|
263
|
+
const fs = yield* FileSystem.FileSystem;
|
|
264
|
+
const path = yield* Path.Path;
|
|
265
|
+
const unreadable = () => new DeclarationsInvalid({ reason: `${what} does not read` });
|
|
266
|
+
const real = yield* fs.realPath(path.join(boundary, file)).pipe(Effect2.mapError(unreadable));
|
|
267
|
+
if (path.relative(yield* fs.realPath(boundary).pipe(Effect2.mapError(unreadable)), real).startsWith(".."))
|
|
268
|
+
return yield* new DeclarationsInvalid({ reason: `${what}: ${file} links outside its source` });
|
|
269
|
+
return real;
|
|
270
|
+
});
|
|
271
|
+
var readTree = (root, what, fetched = false) => Effect2.gen(function* () {
|
|
107
272
|
const fs = yield* FileSystem.FileSystem;
|
|
108
273
|
const path = yield* Path.Path;
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const entries = yield* fs.readDirectory(root, { recursive: true }).pipe(Effect.mapError(unreadable));
|
|
274
|
+
const unreadable = () => new DeclarationsInvalid({ reason: `${what} does not read` });
|
|
275
|
+
const entries = yield* fs.readDirectory(root, { recursive: true }).pipe(Effect2.mapError(unreadable));
|
|
112
276
|
const files = {};
|
|
113
277
|
for (const entry of entries.toSorted()) {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
278
|
+
const relative = entry.split(path.sep).join("/");
|
|
279
|
+
if (relative === ".git" || relative.startsWith(".git/"))
|
|
116
280
|
continue;
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
if (text.includes("\x00"))
|
|
123
|
-
return yield* new DeclarationsInvalid({ reason: `${what} ${skill} holds a binary file, ${entry}` });
|
|
124
|
-
files[entry.split(path.sep).join("/")] = text;
|
|
281
|
+
const file = fetched ? yield* confined(root, entry, what) : path.join(root, entry);
|
|
282
|
+
if ((yield* fs.stat(file).pipe(Effect2.mapError(unreadable))).type !== "File")
|
|
283
|
+
continue;
|
|
284
|
+
files[relative] = yield* fs.readFile(file).pipe(Effect2.mapError(unreadable));
|
|
125
285
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
286
|
+
return files;
|
|
287
|
+
});
|
|
288
|
+
var licenseFiles = ["LICENSE", "LICENSE.md", "LICENSE.txt", "COPYING"];
|
|
289
|
+
var fetchedTree = (root, path, what) => Effect2.gen(function* () {
|
|
290
|
+
const fs = yield* FileSystem.FileSystem;
|
|
291
|
+
const paths = yield* Path.Path;
|
|
292
|
+
const tree = yield* readTree(path === undefined ? root : yield* confined(root, path, what), what, true);
|
|
293
|
+
if (path === undefined)
|
|
294
|
+
return tree;
|
|
295
|
+
for (const license of licenseFiles)
|
|
296
|
+
if (tree[license] === undefined && (yield* fs.exists(paths.join(root, license))))
|
|
297
|
+
return { ...tree, [license]: yield* fs.readFile(yield* confined(root, license, what)).pipe(Effect2.orDie) };
|
|
298
|
+
return tree;
|
|
299
|
+
});
|
|
300
|
+
var GitSourceLocation = Context2.Reference("kitman/sdk/build/GitSourceLocation", { defaultValue: () => (declared) => declared });
|
|
301
|
+
var fetchGit = (declared, what) => Effect2.gen(function* () {
|
|
302
|
+
const fs = yield* FileSystem.FileSystem;
|
|
303
|
+
const { url, ref } = (yield* GitSourceLocation)(declared);
|
|
304
|
+
const checkout = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-git-" });
|
|
305
|
+
for (const args of [
|
|
306
|
+
["init", "--quiet"],
|
|
307
|
+
["fetch", "--quiet", "--depth", "1", "--", url, ref ?? "HEAD"],
|
|
308
|
+
["checkout", "--quiet", "FETCH_HEAD"]
|
|
309
|
+
]) {
|
|
310
|
+
const result = yield* run("git", args, checkout);
|
|
311
|
+
if (!result.ok)
|
|
312
|
+
return yield* new SourceFetchFailed({
|
|
313
|
+
reason: `${what}: git ${url}${ref === undefined ? "" : ` at ${ref}`}: ${failure(result)}`
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
return checkout;
|
|
317
|
+
});
|
|
318
|
+
var fetchNpm = (name, wanted, what) => Effect2.gen(function* () {
|
|
319
|
+
const fs = yield* FileSystem.FileSystem;
|
|
320
|
+
const path = yield* Path.Path;
|
|
321
|
+
const registry = yield* Config2.String("NPM_CONFIG_REGISTRY").pipe(Config2.withDefault("https://registry.npmjs.org"), Effect2.orDie);
|
|
322
|
+
const failed = (reason) => new SourceFetchFailed({ reason: `${what}: npm ${name}@${wanted}: ${reason}` });
|
|
323
|
+
const packument = yield* HttpClient2.get(`${registry.replace(/\/+$/, "")}/${name.replace("/", "%2F")}`).pipe(Effect2.flatMap(HttpClientResponse2.filterStatusOk), Effect2.flatMap(HttpClientResponse2.schemaBodyJson(Packument)), Effect2.mapError((error) => failed(error.message)));
|
|
324
|
+
const dist = packument.versions[packument["dist-tags"][wanted] ?? wanted]?.dist;
|
|
325
|
+
if (dist === undefined)
|
|
326
|
+
return yield* failed("npm has no such version or dist-tag");
|
|
327
|
+
const tarball = yield* HttpClient2.get(dist.tarball).pipe(Effect2.flatMap(HttpClientResponse2.filterStatusOk), Effect2.flatMap((response) => response.arrayBuffer), Effect2.map((buffer) => new Uint8Array(buffer)), Effect2.mapError((error) => failed(error.message)));
|
|
328
|
+
if (!tarballMatches(tarball, dist))
|
|
329
|
+
return yield* failed(`the tarball does not match its integrity ${dist.integrity ?? `sha1 ${dist.shasum}`}`);
|
|
330
|
+
const directory = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-npm-" });
|
|
331
|
+
yield* fs.writeFile(path.join(directory, "package.tgz"), tarball).pipe(Effect2.orDie);
|
|
332
|
+
const extracted = yield* run("tar", ["-xzf", "package.tgz"], directory);
|
|
333
|
+
if (!extracted.ok)
|
|
334
|
+
return yield* failed(`the tarball does not extract: ${failure(extracted)}`);
|
|
335
|
+
return yield* confined(directory, "package", what);
|
|
336
|
+
}).pipe(Effect2.provide(FetchHttpClient2.layer));
|
|
337
|
+
var sourceTree = (directory, source, what, checkouts) => Effect2.gen(function* () {
|
|
338
|
+
switch (source.kind) {
|
|
339
|
+
case "local_path":
|
|
340
|
+
return yield* readTree(yield* inKit(directory, source.path, what), what);
|
|
341
|
+
case "git": {
|
|
342
|
+
const key = `${source.url}#${source.ref ?? ""}`;
|
|
343
|
+
const checkout = checkouts.get(key) ?? (yield* fetchGit({ url: source.url, ref: source.ref }, what));
|
|
344
|
+
checkouts.set(key, checkout);
|
|
345
|
+
return yield* fetchedTree(checkout, "path" in source ? source.path : undefined, what);
|
|
143
346
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
347
|
+
case "package":
|
|
348
|
+
return yield* fetchedTree(yield* fetchNpm(source.packageName, source.version ?? "latest", what), "path" in source ? source.path : undefined, what);
|
|
349
|
+
default:
|
|
350
|
+
return yield* new DeclarationsInvalid({
|
|
351
|
+
reason: `${what} comes from a ${source.kind} source, which a build cannot copy in; use a local path, git or npm`
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
var setupArchive = (directory, kit, setup, checkouts) => Effect2.gen(function* () {
|
|
356
|
+
const fs = yield* FileSystem.FileSystem;
|
|
357
|
+
const files = {};
|
|
358
|
+
const place = (prefix, tree) => {
|
|
359
|
+
for (const [file, bytes] of Object.entries(tree))
|
|
360
|
+
files[`${prefix}/${file}`] = setupFile(bytes);
|
|
361
|
+
return { kind: "local_path", path: prefix };
|
|
362
|
+
};
|
|
363
|
+
if (setup.instructions !== undefined) {
|
|
364
|
+
const what = `Kit ${kit} instructions`;
|
|
365
|
+
const file = yield* inKit(directory, setup.instructions, what);
|
|
366
|
+
files["instructions.md"] = setupFile(yield* fs.readFile(file).pipe(Effect2.mapError(() => new DeclarationsInvalid({ reason: `${what} do not read` }))));
|
|
367
|
+
}
|
|
368
|
+
const skills = setup.skills === undefined ? undefined : yield* Effect2.forEach(setup.skills, (skill) => Effect2.map(sourceTree(directory, skill.source, `Kit ${kit} skill ${skill.name}`, checkouts), (tree) => ({
|
|
369
|
+
...skill,
|
|
370
|
+
source: place(`skills/${skill.name}`, tree)
|
|
371
|
+
})));
|
|
372
|
+
const plugins = setup.plugins === undefined ? undefined : yield* Effect2.forEach(setup.plugins, (plugin) => Effect2.map(sourceTree(directory, plugin.sources[0], `Kit ${kit} plugin ${plugin.name}`, checkouts), (tree) => ({
|
|
373
|
+
...plugin,
|
|
374
|
+
sources: [place(`plugins/${plugin.name}`, tree)]
|
|
375
|
+
})));
|
|
376
|
+
const archive = {
|
|
377
|
+
setup: {
|
|
378
|
+
...setup,
|
|
379
|
+
...setup.instructions === undefined ? undefined : { instructions: "instructions.md" },
|
|
380
|
+
...skills === undefined ? undefined : { skills },
|
|
381
|
+
...plugins === undefined ? undefined : { plugins }
|
|
382
|
+
},
|
|
383
|
+
files
|
|
384
|
+
};
|
|
385
|
+
const bytes = new TextEncoder().encode(canonicalJson(archive));
|
|
386
|
+
return { digest: digestSha256(bytes, "hex"), archive: bytes };
|
|
387
|
+
});
|
|
388
|
+
var PackageJson2 = Schema3.fromJsonString(Schema3.Struct({
|
|
389
|
+
name: Schema3.String,
|
|
390
|
+
version: Schema3.optionalKey(Schema3.String),
|
|
391
|
+
dependencies: Schema3.optionalKey(Schema3.Record(Schema3.String, Schema3.String))
|
|
392
|
+
}));
|
|
393
|
+
var readPackageJson = (directory) => Effect2.gen(function* () {
|
|
394
|
+
const fs = yield* FileSystem.FileSystem;
|
|
395
|
+
const path = yield* Path.Path;
|
|
396
|
+
return yield* fs.readFileString(path.join(directory, "package.json")).pipe(Effect2.flatMap(Schema3.decodeEffect(PackageJson2)), Effect2.mapError(() => new DeclarationsInvalid({ reason: `${directory}/package.json has no name` })));
|
|
397
|
+
});
|
|
398
|
+
var identityOf = (manifest) => ({
|
|
399
|
+
name: manifest.name,
|
|
400
|
+
version: manifest.version ?? "0.0.0"
|
|
401
|
+
});
|
|
402
|
+
var installedPackage = (name, directory) => Effect2.gen(function* () {
|
|
403
|
+
const fs = yield* FileSystem.FileSystem;
|
|
404
|
+
const path = yield* Path.Path;
|
|
405
|
+
for (let at = directory;; at = path.dirname(at)) {
|
|
406
|
+
const candidate = path.join(at, "node_modules", name);
|
|
407
|
+
if (yield* fs.exists(path.join(candidate, "package.json")))
|
|
408
|
+
return yield* fs.realPath(candidate);
|
|
409
|
+
if (path.dirname(at) === at)
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
var importedFile = (file, specifier) => Effect2.gen(function* () {
|
|
414
|
+
const fs = yield* FileSystem.FileSystem;
|
|
415
|
+
const path = yield* Path.Path;
|
|
416
|
+
const target = path.resolve(path.dirname(file), specifier);
|
|
417
|
+
for (const candidate of [target, `${target}.ts`, path.join(target, "kitman.config.ts")])
|
|
418
|
+
if (yield* fs.stat(candidate).pipe(Effect2.map((info) => info.type === "File"), Effect2.orElseSucceed(() => false)))
|
|
419
|
+
return candidate;
|
|
420
|
+
return;
|
|
421
|
+
});
|
|
422
|
+
var packageDirectory = (file) => Effect2.gen(function* () {
|
|
423
|
+
const fs = yield* FileSystem.FileSystem;
|
|
424
|
+
const path = yield* Path.Path;
|
|
425
|
+
for (let at = path.dirname(file);; at = path.dirname(at)) {
|
|
426
|
+
if (yield* fs.exists(path.join(at, "package.json")).pipe(Effect2.orElseSucceed(() => false)))
|
|
427
|
+
return at;
|
|
428
|
+
if (path.dirname(at) === at)
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
var kitModules = (root) => Effect2.gen(function* () {
|
|
148
433
|
const fs = yield* FileSystem.FileSystem;
|
|
149
434
|
const path = yield* Path.Path;
|
|
150
|
-
|
|
435
|
+
const found = [];
|
|
436
|
+
const visited = new Set;
|
|
437
|
+
const packages = new Set([root]);
|
|
438
|
+
const known = (directory) => packages.has(directory);
|
|
439
|
+
const visitPackage = (directory) => Effect2.gen(function* () {
|
|
440
|
+
for (const name of Object.keys((yield* readPackageJson(directory)).dependencies ?? {})) {
|
|
441
|
+
const installed = yield* installedPackage(name, directory).pipe(Effect2.orElseSucceed(() => {
|
|
442
|
+
return;
|
|
443
|
+
}));
|
|
444
|
+
const module = installed === undefined ? undefined : path.join(installed, "kitman.config.ts");
|
|
445
|
+
if (installed === undefined || module === undefined || known(installed))
|
|
446
|
+
continue;
|
|
447
|
+
if (!(yield* fs.exists(module).pipe(Effect2.orElseSucceed(() => false))))
|
|
448
|
+
continue;
|
|
449
|
+
packages.add(installed);
|
|
450
|
+
found.push({ directory: installed, module, identity: identityOf(yield* readPackageJson(installed)) });
|
|
451
|
+
yield* visitPackage(installed);
|
|
452
|
+
yield* visitImports(module);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
const visitImports = (file) => Effect2.gen(function* () {
|
|
456
|
+
if (visited.has(file))
|
|
457
|
+
return;
|
|
458
|
+
visited.add(file);
|
|
459
|
+
const transpiled = yield* run("bun", ["build", "--no-bundle", file], root).pipe(Effect2.map((result) => result.ok ? result.stdout : ""), Effect2.orElseSucceed(() => ""));
|
|
460
|
+
for (const [, specifier] of transpiled.matchAll(/\b(?:from|import)\s*"(\.{1,2}\/[^"]+)"/gu)) {
|
|
461
|
+
const module = specifier === undefined ? undefined : yield* importedFile(file, specifier);
|
|
462
|
+
if (module === undefined || visited.has(module))
|
|
463
|
+
continue;
|
|
464
|
+
const directory = yield* packageDirectory(module);
|
|
465
|
+
if (directory !== undefined && directory !== root) {
|
|
466
|
+
found.push({ directory, module, identity: identityOf(yield* readPackageJson(directory)) });
|
|
467
|
+
if (!known(directory)) {
|
|
468
|
+
packages.add(directory);
|
|
469
|
+
yield* visitPackage(directory);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
yield* visitImports(module);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
yield* visitPackage(root);
|
|
476
|
+
yield* visitImports(path.join(root, "kitman.config.ts"));
|
|
477
|
+
return found;
|
|
151
478
|
});
|
|
479
|
+
var buildEntry = (identity, modules) => [
|
|
480
|
+
"import config from './kitman.config.ts'",
|
|
481
|
+
"import { runKit } from '@kitman-sh/sdk/program'",
|
|
482
|
+
...modules.map((kit, index) => `import * as kit${index} from ${JSON.stringify(kit.module)}`),
|
|
483
|
+
"",
|
|
484
|
+
`runKit(config, ${JSON.stringify(identity)}, [${modules.map((kit, index) => `[kit${index}.default, ${JSON.stringify(kit.identity)}]`).join(", ")}])`,
|
|
485
|
+
""
|
|
486
|
+
].join(`
|
|
487
|
+
`);
|
|
152
488
|
var renamedKey = (declarations, what, from, to) => {
|
|
153
|
-
const key = Object.keys(declarations ?? {}).find((key) =>
|
|
489
|
+
const key = Object.keys(declarations ?? {}).find((key) => Predicate2.hasProperty(declarations?.[key], from));
|
|
154
490
|
return key === undefined ? undefined : new DeclarationsInvalid({ reason: `${what} "${key}" names \`${from}\`, which is now \`${to}\`` });
|
|
155
491
|
};
|
|
156
|
-
var
|
|
157
|
-
|
|
492
|
+
var resolveNames = (names) => {
|
|
493
|
+
const winners = new Map;
|
|
494
|
+
const overrides = [];
|
|
495
|
+
for (const named of names) {
|
|
496
|
+
const key = `${named.what}\x00${named.name}`;
|
|
497
|
+
const winner = winners.get(key);
|
|
498
|
+
if (winner === undefined)
|
|
499
|
+
winners.set(key, named.kit);
|
|
500
|
+
else if (winner === named.kit)
|
|
501
|
+
return new DeclarationsInvalid({ reason: `Kit ${named.kit} names ${named.what} ${named.name} twice` });
|
|
502
|
+
else
|
|
503
|
+
overrides.push({ what: named.what, name: named.name, winner, replaced: named.kit });
|
|
504
|
+
}
|
|
505
|
+
return { wins: (named) => winners.get(`${named.what}\x00${named.name}`) === named.kit, overrides };
|
|
506
|
+
};
|
|
507
|
+
var buildKit = (directory, options = {}) => Effect2.gen(function* () {
|
|
158
508
|
const fs = yield* FileSystem.FileSystem;
|
|
159
509
|
const path = yield* Path.Path;
|
|
160
510
|
const root = path.resolve(directory);
|
|
161
511
|
const source = yield* provenance(root);
|
|
162
|
-
const
|
|
163
|
-
const installed = yield* run("bun", ["install", "--frozen-lockfile", "--ignore-scripts"], root).pipe(
|
|
512
|
+
const identity = { ...identityOf(yield* readPackageJson(root)), ...options };
|
|
513
|
+
const installed = yield* run("bun", ["install", "--frozen-lockfile", "--ignore-scripts"], root).pipe(Effect2.mapError((error) => new DependencyInstallFailed({ reason: error.message })));
|
|
164
514
|
if (!installed.ok)
|
|
165
515
|
return yield* new DependencyInstallFailed({ reason: failure(installed) });
|
|
516
|
+
const packages = yield* kitModules(root);
|
|
166
517
|
const entry = path.join(root, ".kitman-build-entry.ts");
|
|
167
|
-
yield*
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
runPackage(config)
|
|
171
|
-
`), () => Effect.ignore(fs.remove(entry)));
|
|
172
|
-
const out = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-package-" });
|
|
518
|
+
yield* Effect2.acquireRelease(fs.writeFileString(entry, buildEntry(identity, packages)), () => Effect2.ignore(fs.remove(entry)));
|
|
519
|
+
const out = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-kit-" });
|
|
173
520
|
const bundleFile = path.join(out, "bundle.js");
|
|
174
521
|
const bundled = yield* run("bun", ["build", entry, "--target=bun", "--format=esm", "--outfile", bundleFile], root);
|
|
175
522
|
if (!bundled.ok)
|
|
176
|
-
return yield*
|
|
523
|
+
return yield* failure(bundled).includes('"defineConfig"') ? new DeclarationsInvalid({
|
|
524
|
+
reason: "defineConfig is now defineKit; import defineKit from @kitman-sh/sdk/loop"
|
|
525
|
+
}) : new BundleFailed({ reason: failure(bundled) });
|
|
177
526
|
const described = yield* run("bun", [bundleFile, describeFlag2], out);
|
|
178
527
|
if (!described.ok)
|
|
179
528
|
return yield* new BundleFailed({ reason: `the bundle does not load: ${failure(described)}` });
|
|
180
|
-
const result = yield*
|
|
181
|
-
if (
|
|
529
|
+
const result = yield* Schema3.decodeEffect(Schema3.fromJsonString(Schema3.toCodecJson(Described2)))(described.stdout).pipe(Effect2.mapError((error) => new BundleFailed({ reason: `the bundle described itself wrongly: ${error.message}` })));
|
|
530
|
+
if (Predicate2.isTagged(result, "Refused"))
|
|
182
531
|
return yield* result.error;
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
return yield* new
|
|
186
|
-
const renamed = renamedKey(
|
|
532
|
+
const [own] = result.kits;
|
|
533
|
+
if (own === undefined)
|
|
534
|
+
return yield* new BundleFailed({ reason: "the bundle described no Kit" });
|
|
535
|
+
const renamed = renamedKey(own.schedules, "Schedule", "project", "team") ?? renamedKey(own.schedules, "Schedule", "codebase", "team");
|
|
187
536
|
if (renamed !== undefined)
|
|
188
537
|
return yield* renamed;
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
538
|
+
const directories = new Map([
|
|
539
|
+
[`${identity.name}@${identity.version}`, root],
|
|
540
|
+
...packages.map((kit) => [`${kit.identity.name}@${kit.identity.version}`, kit.directory])
|
|
541
|
+
]);
|
|
542
|
+
const kits = yield* Effect2.forEach(result.kits, (kit) => Effect2.gen(function* () {
|
|
543
|
+
const at = directories.get(`${kit.name}@${kit.version}`) ?? root;
|
|
544
|
+
const invalid = (error) => new DeclarationsInvalid({ reason: `Kit ${kit.name}: ${error.message}` });
|
|
545
|
+
const agents = yield* Schema3.decodeUnknownEffect(ConfigAgents)(kit.agents).pipe(Effect2.mapError(invalid));
|
|
546
|
+
return { ...kit, directory: at, agents: yield* readAgents(at, agents), setup: kit.setup ?? {} };
|
|
547
|
+
}));
|
|
548
|
+
const named = kits.flatMap((kit) => [
|
|
549
|
+
...kit.loops.map((loop) => ({ kit: kit.name, what: "Loop", name: loop.id })),
|
|
550
|
+
...Object.keys(kit.agents).map((agent) => ({ kit: kit.name, what: "Agent", name: agent })),
|
|
551
|
+
...(kit.setup.skills ?? []).map((skill) => ({ kit: kit.name, what: "skill", name: skill.name })),
|
|
552
|
+
...(kit.setup.plugins ?? []).map((plugin) => ({ kit: kit.name, what: "plugin", name: plugin.name })),
|
|
553
|
+
...(kit.setup.mcpServers ?? []).map((server) => ({ kit: kit.name, what: "MCP server", name: server.name }))
|
|
554
|
+
]);
|
|
555
|
+
const kitman = named.find((entry) => entry.what === "MCP server" && entry.name === kitmanMcpServer);
|
|
556
|
+
if (kitman !== undefined)
|
|
557
|
+
return yield* new DeclarationsInvalid({
|
|
558
|
+
reason: `Kit ${kitman.kit} declares an MCP server named ${kitmanMcpServer}, which Kitman provides`
|
|
559
|
+
});
|
|
560
|
+
const names = resolveNames(named);
|
|
561
|
+
if (Predicate2.isTagged(names, "DeclarationsInvalid"))
|
|
562
|
+
return yield* names;
|
|
563
|
+
const winning = kits.map((kit) => ({
|
|
564
|
+
...kit,
|
|
565
|
+
loops: kit.loops.filter((loop) => names.wins({ kit: kit.name, what: "Loop", name: loop.id })),
|
|
566
|
+
agents: Object.fromEntries(Object.entries(kit.agents).filter(([agent]) => names.wins({ kit: kit.name, what: "Agent", name: agent })))
|
|
567
|
+
}));
|
|
568
|
+
const checkouts = new Map;
|
|
569
|
+
const archives = yield* Effect2.forEach(kits, (kit) => setupArchive(kit.directory, kit.name, kit.setup, checkouts));
|
|
570
|
+
const carried = winning.map((kit, index) => ({
|
|
571
|
+
name: kit.name,
|
|
572
|
+
version: kit.version,
|
|
573
|
+
setup: archives[index]?.digest ?? "",
|
|
574
|
+
loops: kit.loops.map((loop) => loop.id),
|
|
575
|
+
agents: Object.keys(kit.agents)
|
|
576
|
+
}));
|
|
577
|
+
const declarations = yield* Schema3.decodeUnknownEffect(KitDeclarations)({
|
|
578
|
+
loops: winning.flatMap((kit) => kit.loops),
|
|
579
|
+
agents: Object.assign({}, ...winning.map((kit) => kit.agents)),
|
|
580
|
+
...own.schedules === undefined ? undefined : { schedules: own.schedules },
|
|
581
|
+
carried
|
|
582
|
+
}).pipe(Effect2.mapError((error) => new DeclarationsInvalid({ reason: error.message })));
|
|
583
|
+
const bundle = yield* fs.readFile(bundleFile).pipe(Effect2.mapError((error) => new BundleFailed({ reason: error.message })));
|
|
198
584
|
return {
|
|
199
|
-
|
|
585
|
+
...identity,
|
|
200
586
|
bundle,
|
|
201
587
|
digest: digestSha256(bundle, "hex"),
|
|
202
588
|
declarations,
|
|
589
|
+
setupArchives: archives,
|
|
203
590
|
protocolVersion: RunnerRpcProtocolVersion,
|
|
204
|
-
provenance: source
|
|
591
|
+
provenance: source,
|
|
592
|
+
overrides: names.overrides
|
|
205
593
|
};
|
|
206
|
-
}).pipe(
|
|
207
|
-
PlatformError: (error) =>
|
|
594
|
+
}).pipe(Effect2.scoped, Effect2.catchTags({
|
|
595
|
+
PlatformError: (error) => Effect2.fail(new BundleFailed({ reason: error.message }))
|
|
208
596
|
}));
|
|
597
|
+
var writeKit = (directory, built, out) => Effect2.gen(function* () {
|
|
598
|
+
const fs = yield* FileSystem.FileSystem;
|
|
599
|
+
const path = yield* Path.Path;
|
|
600
|
+
const inKit = (file) => path.relative(directory, path.join(out, file));
|
|
601
|
+
const setup = {};
|
|
602
|
+
yield* fs.makeDirectory(path.join(out, "setup"), { recursive: true });
|
|
603
|
+
yield* fs.writeFile(path.join(out, "bundle.js"), built.bundle);
|
|
604
|
+
yield* fs.writeFileString(path.join(out, "declarations.json"), yield* encodePrettyJsonString(built.declarations));
|
|
605
|
+
for (const { digest, archive } of built.setupArchives) {
|
|
606
|
+
yield* fs.writeFile(path.join(out, "setup", `${digest}.json`), archive);
|
|
607
|
+
setup[digest] = inKit(`setup/${digest}.json`);
|
|
608
|
+
}
|
|
609
|
+
const manifestPath = path.join(directory, "package.json");
|
|
610
|
+
const manifest = yield* fs.readFileString(manifestPath).pipe(Effect2.flatMap(Schema3.decodeEffect(Schema3.fromJsonString(Schema3.Record(Schema3.String, Schema3.Json)))));
|
|
611
|
+
const kitman = {
|
|
612
|
+
bundle: inKit("bundle.js"),
|
|
613
|
+
digest: built.digest,
|
|
614
|
+
protocolVersion: built.protocolVersion,
|
|
615
|
+
declarations: inKit("declarations.json"),
|
|
616
|
+
setup
|
|
617
|
+
};
|
|
618
|
+
yield* fs.writeFileString(manifestPath, `${yield* encodePrettyJsonString({ ...manifest, kitman })}
|
|
619
|
+
`);
|
|
620
|
+
});
|
|
209
621
|
export {
|
|
210
622
|
BundleFailed,
|
|
211
623
|
DeclarationsInvalid,
|
|
212
624
|
DependencyInstallFailed,
|
|
213
|
-
|
|
625
|
+
GitSourceLocation,
|
|
626
|
+
SourceFetchFailed,
|
|
627
|
+
buildKit,
|
|
628
|
+
writeKit
|
|
214
629
|
};
|