@kitman-sh/cli 0.7.2 → 0.7.4
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-226sychh.js → chunk-120n71cv.js} +66 -39
- package/dist/{chunk-6f4anyec.js → chunk-20nwe9zd.js} +1 -1
- package/dist/{chunk-gzhxsb25.js → chunk-5cc3fb7y.js} +3 -3
- package/dist/{chunk-6kdj8psf.js → chunk-7f20xrc0.js} +2 -2
- package/dist/{chunk-hrd7xmax.js → chunk-gew1a5p6.js} +9 -9
- package/dist/{chunk-ztmwmk8t.js → chunk-h12rxyy9.js} +12 -12
- package/dist/{chunk-ae7gx971.js → chunk-j2psmzr4.js} +4 -4
- package/dist/{chunk-704ws2q7.js → chunk-jd09xfx0.js} +433 -257
- package/dist/{chunk-epdajq63.js → chunk-jwtkec1w.js} +2 -2
- package/dist/{chunk-dwgta18y.js → chunk-q6hk9ceb.js} +5 -5
- package/dist/{chunk-vjnae06n.js → chunk-s92k78ef.js} +784 -141
- package/dist/index.js +103 -235
- package/package.json +1 -1
|
@@ -13,10 +13,13 @@ import {
|
|
|
13
13
|
none,
|
|
14
14
|
isNone,
|
|
15
15
|
isSome,
|
|
16
|
+
match,
|
|
16
17
|
getOrUndefined,
|
|
18
|
+
flatMap,
|
|
17
19
|
Service,
|
|
18
20
|
get,
|
|
19
21
|
merge,
|
|
22
|
+
Reference,
|
|
20
23
|
succeed,
|
|
21
24
|
fail,
|
|
22
25
|
isResult,
|
|
@@ -36,11 +39,14 @@ import {
|
|
|
36
39
|
isSuccess2,
|
|
37
40
|
isFailure2,
|
|
38
41
|
makeUnsafe,
|
|
42
|
+
make6,
|
|
39
43
|
_await,
|
|
44
|
+
fail3,
|
|
40
45
|
isDoneUnsafe,
|
|
46
|
+
succeed3,
|
|
41
47
|
doneUnsafe,
|
|
42
48
|
Scope,
|
|
43
|
-
|
|
49
|
+
make7,
|
|
44
50
|
provide,
|
|
45
51
|
addFinalizer,
|
|
46
52
|
close,
|
|
@@ -61,7 +67,7 @@ import {
|
|
|
61
67
|
fail4,
|
|
62
68
|
die3,
|
|
63
69
|
withFiber,
|
|
64
|
-
|
|
70
|
+
flatMap2,
|
|
65
71
|
andThen,
|
|
66
72
|
tap,
|
|
67
73
|
result,
|
|
@@ -88,6 +94,7 @@ import {
|
|
|
88
94
|
matchEffect,
|
|
89
95
|
context,
|
|
90
96
|
contextWith,
|
|
97
|
+
provide3,
|
|
91
98
|
provideContext,
|
|
92
99
|
updateContext,
|
|
93
100
|
provideService,
|
|
@@ -106,28 +113,38 @@ import {
|
|
|
106
113
|
logError,
|
|
107
114
|
interrupt3,
|
|
108
115
|
makeUnsafe3,
|
|
116
|
+
unbounded,
|
|
117
|
+
offer,
|
|
109
118
|
makeUnsafe4,
|
|
110
|
-
|
|
119
|
+
make11,
|
|
120
|
+
fromQueue,
|
|
111
121
|
map4,
|
|
112
122
|
filter,
|
|
113
123
|
decodeText,
|
|
114
124
|
splitLines,
|
|
115
125
|
runHead,
|
|
126
|
+
runForEach,
|
|
116
127
|
FileSystem,
|
|
117
128
|
Path,
|
|
118
129
|
ChildProcessSpawner,
|
|
119
|
-
|
|
130
|
+
make13,
|
|
120
131
|
Crypto,
|
|
121
132
|
value,
|
|
122
133
|
is,
|
|
134
|
+
decodeUnknownEffect,
|
|
135
|
+
decodeUnknownOption,
|
|
123
136
|
optionalKey,
|
|
137
|
+
optional,
|
|
124
138
|
Literal,
|
|
139
|
+
Unknown,
|
|
125
140
|
String2,
|
|
126
141
|
Boolean2,
|
|
127
142
|
Struct,
|
|
128
143
|
ArraySchema,
|
|
129
144
|
Union,
|
|
130
145
|
Literals,
|
|
146
|
+
NullOr,
|
|
147
|
+
Finite,
|
|
131
148
|
Int,
|
|
132
149
|
Date2,
|
|
133
150
|
TaggedError2,
|
|
@@ -136,6 +153,7 @@ import {
|
|
|
136
153
|
findForbiddenRepoBindingSecretPath,
|
|
137
154
|
runnerAgentSandboxPolicy,
|
|
138
155
|
agentNetworkAllowlist,
|
|
156
|
+
AgentHarnessIds,
|
|
139
157
|
findForbiddenAgentEnvironmentSecretPath,
|
|
140
158
|
redactedOutputTail,
|
|
141
159
|
canonicalSha256,
|
|
@@ -147,12 +165,17 @@ import {
|
|
|
147
165
|
encodePrettyJsonString,
|
|
148
166
|
decodeJsonStringSync,
|
|
149
167
|
encodeUnknownJsonStringSync,
|
|
168
|
+
schemaBodyJson,
|
|
169
|
+
filterStatusOk,
|
|
170
|
+
get4,
|
|
171
|
+
layer4,
|
|
150
172
|
digestSha256,
|
|
151
173
|
RunSettled,
|
|
152
174
|
CommandFailed2,
|
|
153
175
|
runCommand2,
|
|
154
176
|
shellOutputTailChars2,
|
|
155
177
|
runProcess2,
|
|
178
|
+
CommandRunner2,
|
|
156
179
|
runShell2,
|
|
157
180
|
optionalEnv,
|
|
158
181
|
detectHarnesses2,
|
|
@@ -161,7 +184,7 @@ import {
|
|
|
161
184
|
herdrRunnerCapabilitiesForRepoBinding2,
|
|
162
185
|
herdrRunnerCapabilities2,
|
|
163
186
|
assertHerdrRunnerCapability2
|
|
164
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-jd09xfx0.js";
|
|
165
188
|
import {
|
|
166
189
|
make4,
|
|
167
190
|
readerString,
|
|
@@ -172,17 +195,17 @@ import {
|
|
|
172
195
|
SocketUpgradeError,
|
|
173
196
|
SocketCloseError,
|
|
174
197
|
SocketError
|
|
175
|
-
} from "./chunk-
|
|
176
|
-
import"./chunk-
|
|
198
|
+
} from "./chunk-q6hk9ceb.js";
|
|
199
|
+
import"./chunk-h12rxyy9.js";
|
|
177
200
|
import {
|
|
178
201
|
programLease,
|
|
179
202
|
programHostsEnv,
|
|
180
203
|
undeclaredHostsEnv
|
|
181
|
-
} from "./chunk-
|
|
182
|
-
import"./chunk-
|
|
204
|
+
} from "./chunk-7f20xrc0.js";
|
|
205
|
+
import"./chunk-j2psmzr4.js";
|
|
183
206
|
import {
|
|
184
207
|
nextInt
|
|
185
|
-
} from "./chunk-
|
|
208
|
+
} from "./chunk-20nwe9zd.js";
|
|
186
209
|
|
|
187
210
|
// packages/core/src/runner-trust.ts
|
|
188
211
|
import { createHash } from "crypto";
|
|
@@ -524,7 +547,7 @@ var optionalString = (value, label) => gen(function* () {
|
|
|
524
547
|
return yield* invalid(`${label} must be a non-empty string when provided`);
|
|
525
548
|
});
|
|
526
549
|
// packages/agent-runtime/src/config-convergence.ts
|
|
527
|
-
var writeAgentEnvironmentConfigFile = fnUntraced(function* (file, content) {
|
|
550
|
+
var writeAgentEnvironmentConfigFile = fnUntraced(function* (file, content, keep = {}) {
|
|
528
551
|
const fs = yield* FileSystem;
|
|
529
552
|
const paths = {
|
|
530
553
|
path: yield* Path,
|
|
@@ -537,14 +560,16 @@ var writeAgentEnvironmentConfigFile = fnUntraced(function* (file, content) {
|
|
|
537
560
|
return mergeCodexAgents(existing, content);
|
|
538
561
|
if (file.endsWith("opencode.json")) {
|
|
539
562
|
const managed = yield* readManagedNamesForConfig(paths, file);
|
|
540
|
-
return mergeOpenCodeConfig(existing, content, managed.mcpNames, managed.
|
|
563
|
+
return mergeOpenCodeConfig(existing, content, managed.mcpNames, without(managed.packageNames, keep.packages));
|
|
541
564
|
}
|
|
542
565
|
if (file.endsWith(".codex/config.toml")) {
|
|
543
566
|
const managed = yield* readManagedNamesForConfig(paths, file);
|
|
544
|
-
return mergeCodexConfig(existing, content, managed.mcpNames, managed.skillPaths);
|
|
567
|
+
return mergeCodexConfig(existing, content, managed.mcpNames, managed.skillPaths, keep.kitmanPlugins === true);
|
|
568
|
+
}
|
|
569
|
+
if (isPiConfigPath(paths.path, file, "settings.json")) {
|
|
570
|
+
const managed = yield* readManagedNamesForConfig(paths, file);
|
|
571
|
+
return mergePiSettingsConfig(existing, content, without(managed.packageNames, keep.packages));
|
|
545
572
|
}
|
|
546
|
-
if (isPiConfigPath(paths.path, file, "settings.json"))
|
|
547
|
-
return mergePiSettingsConfig(existing, content);
|
|
548
573
|
if (isPiConfigPath(paths.path, file, "mcp.json")) {
|
|
549
574
|
const managed = yield* readManagedNamesForConfig(paths, file);
|
|
550
575
|
return mergePiMcpConfig(existing, content, managed.mcpNames);
|
|
@@ -553,6 +578,7 @@ var writeAgentEnvironmentConfigFile = fnUntraced(function* (file, content) {
|
|
|
553
578
|
});
|
|
554
579
|
yield* fs.writeFileString(file, merged);
|
|
555
580
|
});
|
|
581
|
+
var without = (names, kept = []) => new Set([...names].filter((name) => !kept.includes(name)));
|
|
556
582
|
var KitmanAgentsStartMarker = "<!-- KITMAN_AGENT_ENVIRONMENT_START -->";
|
|
557
583
|
var KitmanAgentsEndMarker = "<!-- KITMAN_AGENT_ENVIRONMENT_END -->";
|
|
558
584
|
var KitmanGeneratedAgentsPrefix = "# Kitman AgentEnvironment\n\nThis file is generated by `kitman sync`";
|
|
@@ -575,27 +601,34 @@ ${KitmanAgentsEndMarker}`;
|
|
|
575
601
|
${block}
|
|
576
602
|
`;
|
|
577
603
|
}
|
|
578
|
-
var readExistingText = (file) =>
|
|
604
|
+
var readExistingText = (file) => flatMap2(FileSystem, (fs) => fs.readFileString(file).pipe(catchReason("PlatformError", "NotFound", () => succeed5(""))));
|
|
579
605
|
var readManagedNamesForConfig = (paths, file) => gen(function* () {
|
|
580
606
|
const manifest = parseJsonObject(yield* readExistingText(yield* managedManifestPathForConfig(paths, file)));
|
|
581
607
|
const servers = Array.isArray(manifest.mcpServers) ? manifest.mcpServers : [];
|
|
582
608
|
const plugins = Array.isArray(manifest.plugins) ? manifest.plugins : [];
|
|
583
|
-
const materializedSkills = Array.isArray(manifest.materializedSkills) ? manifest.materializedSkills : [];
|
|
584
609
|
return {
|
|
585
610
|
mcpNames: new Set(servers.flatMap((entry) => {
|
|
586
611
|
const item = objectValue(objectValue(entry).item);
|
|
587
612
|
return isString2(item.name) ? [item.name] : [];
|
|
588
613
|
})),
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
return
|
|
614
|
+
packageNames: new Set(plugins.flatMap((entry) => {
|
|
615
|
+
const { source: legacySource, sources } = objectValue(objectValue(entry).item);
|
|
616
|
+
return (Array.isArray(sources) ? sources : legacySource === undefined ? [] : [legacySource]).flatMap((source) => {
|
|
617
|
+
const { kind, packageName } = objectValue(source);
|
|
618
|
+
return kind === "package" && isString2(packageName) ? [packageName] : [];
|
|
619
|
+
});
|
|
592
620
|
})),
|
|
593
|
-
skillPaths:
|
|
594
|
-
const item = objectValue(entry);
|
|
595
|
-
return isString2(item.skillPath) ? [item.skillPath] : [];
|
|
596
|
-
}))
|
|
621
|
+
skillPaths: manifestSkillPaths(manifest)
|
|
597
622
|
};
|
|
598
623
|
});
|
|
624
|
+
function manifestSkillPaths(manifest) {
|
|
625
|
+
const materializedSkills = Array.isArray(manifest.materializedSkills) ? manifest.materializedSkills : [];
|
|
626
|
+
return new Set(materializedSkills.flatMap((entry) => {
|
|
627
|
+
const item = objectValue(entry);
|
|
628
|
+
return isString2(item.skillPath) ? [item.skillPath] : [];
|
|
629
|
+
}));
|
|
630
|
+
}
|
|
631
|
+
var readManifestSkillPaths = (manifestPath) => map3(readExistingText(manifestPath), (text) => manifestSkillPaths(parseJsonObject(text)));
|
|
599
632
|
var managedManifestPathForConfig = (paths, file) => gen(function* () {
|
|
600
633
|
const fs = yield* FileSystem;
|
|
601
634
|
const manifestRoot = managedManifestRootPath(paths, file);
|
|
@@ -655,13 +688,13 @@ function manifestFileForHarness(harness) {
|
|
|
655
688
|
return "agent-environment.manifest.json";
|
|
656
689
|
return `agent-environment.${harness}.manifest.json`;
|
|
657
690
|
}
|
|
658
|
-
function mergeOpenCodeConfig(existingText, generatedText, managedMcpNames,
|
|
691
|
+
function mergeOpenCodeConfig(existingText, generatedText, managedMcpNames, managedPackageNames) {
|
|
659
692
|
const existing = parseJsonObject(existingText);
|
|
660
693
|
const generated = parseJsonObject(generatedText);
|
|
661
694
|
const existingMcp = { ...objectValue(existing.mcp) };
|
|
662
695
|
for (const name of managedMcpNames)
|
|
663
696
|
delete existingMcp[name];
|
|
664
|
-
const existingPlugins = uniqueStrings(arrayValue(existing.plugin).filter((entry) => !
|
|
697
|
+
const existingPlugins = uniqueStrings(arrayValue(existing.plugin).filter((entry) => !isManagedPackageReference(entry, managedPackageNames)));
|
|
665
698
|
const merged = {
|
|
666
699
|
...existing,
|
|
667
700
|
...generated,
|
|
@@ -679,22 +712,25 @@ function mergeOpenCodeConfig(existingText, generatedText, managedMcpNames, manag
|
|
|
679
712
|
return `${JSON.stringify(merged, null, 2)}
|
|
680
713
|
`;
|
|
681
714
|
}
|
|
682
|
-
function
|
|
715
|
+
function isManagedPackageReference(entry, managedPackageNames, prefix = "") {
|
|
683
716
|
if (!isString2(entry))
|
|
684
717
|
return false;
|
|
685
|
-
for (const name of
|
|
686
|
-
if (entry === name || entry.startsWith(`${name}@`))
|
|
718
|
+
for (const name of managedPackageNames) {
|
|
719
|
+
if (entry === `${prefix}${name}` || entry.startsWith(`${prefix}${name}@`))
|
|
687
720
|
return true;
|
|
688
721
|
}
|
|
689
722
|
return false;
|
|
690
723
|
}
|
|
691
|
-
function mergePiSettingsConfig(existingText, generatedText) {
|
|
724
|
+
function mergePiSettingsConfig(existingText, generatedText, managedPackageNames) {
|
|
692
725
|
const existing = parseJsonObject(existingText);
|
|
693
726
|
const generated = parseJsonObject(generatedText);
|
|
694
727
|
const merged = {
|
|
695
728
|
...existing,
|
|
696
729
|
...generated,
|
|
697
|
-
packages: uniquePackageEntries([
|
|
730
|
+
packages: uniquePackageEntries([
|
|
731
|
+
...packageEntries(existing.packages).filter((entry) => !isManagedPackageReference(entry, managedPackageNames, "npm:")),
|
|
732
|
+
...packageEntries(generated.packages)
|
|
733
|
+
])
|
|
698
734
|
};
|
|
699
735
|
return `${JSON.stringify(merged, null, 2)}
|
|
700
736
|
`;
|
|
@@ -732,8 +768,8 @@ function isPiConfigPath(path, file, name) {
|
|
|
732
768
|
const parent = path.dirname(file);
|
|
733
769
|
return path.basename(file) === name && (path.basename(parent) === ".pi" || path.basename(parent) === "agent" && path.basename(path.dirname(parent)) === ".pi");
|
|
734
770
|
}
|
|
735
|
-
function mergeCodexConfig(existingText, generatedText, managedMcpNames, managedSkillPaths
|
|
736
|
-
const preserved = stripTomlTables(existingText, new Set([...managedMcpNames, ...mcpServerNames(generatedText)]), new Set([...managedSkillPaths, ...codexSkillPaths(generatedText)])).trimEnd();
|
|
771
|
+
function mergeCodexConfig(existingText, generatedText, managedMcpNames, managedSkillPaths, keepKitmanPlugins) {
|
|
772
|
+
const preserved = stripTomlTables(existingText, new Set([...managedMcpNames, ...mcpServerNames(generatedText)]), new Set([...managedSkillPaths, ...codexSkillPaths(generatedText)]), keepKitmanPlugins ? undefined : kitmanPluginNames(generatedText)).trimEnd();
|
|
737
773
|
const generated = generatedText.trim();
|
|
738
774
|
if (preserved.length === 0)
|
|
739
775
|
return `${generated}
|
|
@@ -746,13 +782,17 @@ function mergeCodexConfig(existingText, generatedText, managedMcpNames, managedS
|
|
|
746
782
|
${generated}
|
|
747
783
|
`;
|
|
748
784
|
}
|
|
749
|
-
function stripTomlTables(text, removeMcpServerNames, removeSkillPaths) {
|
|
785
|
+
function stripTomlTables(text, removeMcpServerNames, removeSkillPaths, keptKitmanPlugins) {
|
|
750
786
|
const lines = text.split(`
|
|
751
787
|
`);
|
|
788
|
+
const staleHookState = (key) => {
|
|
789
|
+
const plugin = /^([^@]+)@kitman:/u.exec(key ?? "")?.[1];
|
|
790
|
+
return keptKitmanPlugins !== undefined && plugin !== undefined && !keptKitmanPlugins.has(plugin);
|
|
791
|
+
};
|
|
752
792
|
const kept = [];
|
|
753
793
|
for (let index = 0;index < lines.length; ) {
|
|
754
794
|
const line = lines[index] ?? "";
|
|
755
|
-
const table =
|
|
795
|
+
const table = tomlTableHeader(line);
|
|
756
796
|
if (table === undefined) {
|
|
757
797
|
kept.push(line);
|
|
758
798
|
index += 1;
|
|
@@ -760,23 +800,65 @@ function stripTomlTables(text, removeMcpServerNames, removeSkillPaths) {
|
|
|
760
800
|
}
|
|
761
801
|
const block = [line];
|
|
762
802
|
index += 1;
|
|
763
|
-
while (index < lines.length &&
|
|
803
|
+
while (index < lines.length && tomlTableHeader(lines[index] ?? "") === undefined) {
|
|
764
804
|
block.push(lines[index] ?? "");
|
|
765
805
|
index += 1;
|
|
766
806
|
}
|
|
767
807
|
const skillPath = table === "skills.config" ? codexSkillPathFromBlock(block) : undefined;
|
|
768
|
-
const
|
|
769
|
-
|
|
808
|
+
const [first, second, hookKey, ...rest] = tomlKeyPath(table);
|
|
809
|
+
const hookState = first === "hooks" && second === "state";
|
|
810
|
+
const drop = keptKitmanPlugins !== undefined && isKitmanPluginTable([first ?? "", second ?? ""]) || hookState && rest.length === 0 && staleHookState(hookKey) || codexMcpServerName(table, removeMcpServerNames) !== undefined || skillPath !== undefined && removeSkillPaths.has(skillPath);
|
|
811
|
+
if (drop)
|
|
812
|
+
continue;
|
|
813
|
+
if (hookState && hookKey === undefined)
|
|
814
|
+
kept.push(...block.filter((entry) => !staleHookState(tomlKeyPath(/^([^=]+)=/u.exec(entry)?.[1] ?? "")[0])));
|
|
815
|
+
else
|
|
770
816
|
kept.push(...block);
|
|
771
817
|
}
|
|
772
818
|
return kept.join(`
|
|
773
819
|
`);
|
|
774
820
|
}
|
|
821
|
+
function tomlTableHeader(line) {
|
|
822
|
+
return /^\s*\[\[?((?:"(?:[^"\\]|\\.)*"|'[^']*'|[^"'[\]#])+)\]\]?\s*(?:#.*)?$/u.exec(line)?.[1];
|
|
823
|
+
}
|
|
824
|
+
function kitmanPluginNames(toml) {
|
|
825
|
+
return new Set(toml.split(`
|
|
826
|
+
`).flatMap((line) => {
|
|
827
|
+
const [table, name] = tomlKeyPath(tomlTableHeader(line) ?? "");
|
|
828
|
+
return table === "plugins" && name?.endsWith("@kitman") === true ? [name.slice(0, -"@kitman".length)] : [];
|
|
829
|
+
}));
|
|
830
|
+
}
|
|
831
|
+
function isKitmanPluginTable([table, name]) {
|
|
832
|
+
return table === "marketplaces" && name === "kitman" || table === "plugins" && name?.endsWith("@kitman") === true;
|
|
833
|
+
}
|
|
834
|
+
function tomlKeyPath(key) {
|
|
835
|
+
const part = /\s*(?:"((?:[^"\\]|\\.)*)"|'([^']*)'|([\w-]+))\s*(?:\.|$)/uy;
|
|
836
|
+
const parts = [];
|
|
837
|
+
while (part.lastIndex < key.length) {
|
|
838
|
+
const match = part.exec(key);
|
|
839
|
+
if (match === null)
|
|
840
|
+
return [];
|
|
841
|
+
parts.push(match[3] ?? match[2] ?? unescapeTomlString(match[1] ?? ""));
|
|
842
|
+
}
|
|
843
|
+
return parts;
|
|
844
|
+
}
|
|
845
|
+
function unescapeTomlString(text) {
|
|
846
|
+
const escapes = new Map([
|
|
847
|
+
["b", "\b"],
|
|
848
|
+
["t", "\t"],
|
|
849
|
+
["n", `
|
|
850
|
+
`],
|
|
851
|
+
["f", "\f"],
|
|
852
|
+
["r", "\r"],
|
|
853
|
+
["e", "\x1B"]
|
|
854
|
+
]);
|
|
855
|
+
return text.replace(/\\(?:u([\da-fA-F]{4})|U([\da-fA-F]{8})|(.))/gu, (_, short, long, char) => short !== undefined || long !== undefined ? String.fromCodePoint(Number.parseInt(short ?? long, 16)) : escapes.get(char) ?? char);
|
|
856
|
+
}
|
|
775
857
|
function mcpServerNames(toml) {
|
|
776
858
|
const names = new Set;
|
|
777
859
|
for (const line of toml.split(`
|
|
778
860
|
`)) {
|
|
779
|
-
const table =
|
|
861
|
+
const table = tomlTableHeader(line);
|
|
780
862
|
if (table === undefined)
|
|
781
863
|
continue;
|
|
782
864
|
const name = codexMcpServerTableName(table);
|
|
@@ -791,13 +873,13 @@ function codexSkillPaths(toml) {
|
|
|
791
873
|
`);
|
|
792
874
|
for (let index = 0;index < lines.length; ) {
|
|
793
875
|
const line = lines[index] ?? "";
|
|
794
|
-
if (
|
|
876
|
+
if (!/^\s*\[\[\s*skills\.config\s*\]\]/u.test(line)) {
|
|
795
877
|
index += 1;
|
|
796
878
|
continue;
|
|
797
879
|
}
|
|
798
880
|
const block = [line];
|
|
799
881
|
index += 1;
|
|
800
|
-
while (index < lines.length &&
|
|
882
|
+
while (index < lines.length && tomlTableHeader(lines[index] ?? "") === undefined) {
|
|
801
883
|
block.push(lines[index] ?? "");
|
|
802
884
|
index += 1;
|
|
803
885
|
}
|
|
@@ -838,6 +920,46 @@ function uniqueStrings(values) {
|
|
|
838
920
|
return [...new Set(values)];
|
|
839
921
|
}
|
|
840
922
|
|
|
923
|
+
// packages/agent-runtime/src/source-resolver.ts
|
|
924
|
+
import { tmpdir } from "os";
|
|
925
|
+
class SourceResolutionFailed extends TaggedError("SourceResolutionFailed") {
|
|
926
|
+
get message() {
|
|
927
|
+
return this.reason;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
var isCommitHash = (ref) => ref !== undefined && /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u.test(ref);
|
|
931
|
+
var DistTags = Struct({ latest: String2 });
|
|
932
|
+
var gitCommit = (url, ref) => gen(function* () {
|
|
933
|
+
const patterns = ref === undefined ? ["HEAD"] : [ref, `${ref}^{}`];
|
|
934
|
+
const output = yield* runCommand2("git", ["ls-remote", "--", url, ...patterns], {
|
|
935
|
+
cwd: tmpdir(),
|
|
936
|
+
timeout: "1 minute"
|
|
937
|
+
}).pipe(mapError((error) => new SourceResolutionFailed({ reason: error.message })));
|
|
938
|
+
const lines = output.split(`
|
|
939
|
+
`).map((line) => line.split("\t"));
|
|
940
|
+
const names = ref === undefined ? ["HEAD"] : ref.startsWith("refs/") ? [ref] : [`refs/heads/${ref}`, `refs/tags/${ref}`];
|
|
941
|
+
const commit = (lines.find(([, name]) => names.some((candidate) => name === `${candidate}^{}`)) ?? lines.find(([, name]) => names.includes(name ?? "")))?.[0];
|
|
942
|
+
if (!isCommitHash(commit))
|
|
943
|
+
return yield* new SourceResolutionFailed({ reason: `${url} has no ${ref ?? "default branch"}` });
|
|
944
|
+
return commit;
|
|
945
|
+
});
|
|
946
|
+
var npmLatest = (packageName) => get4(`https://registry.npmjs.org/-/package/${packageName.replace("/", "%2F")}/dist-tags`).pipe(flatMap2(filterStatusOk), flatMap2(schemaBodyJson(DistTags)), map3((tags) => tags.latest), provide3(layer4), mapError((error) => new SourceResolutionFailed({ reason: `npm has no latest ${packageName}: ${error.message}` })));
|
|
947
|
+
var SourceResolver = Reference("kitman/agent-runtime/SourceResolver", {
|
|
948
|
+
defaultValue: () => ({ gitCommit, npmLatest })
|
|
949
|
+
});
|
|
950
|
+
var withSourceResolutionCache = (effect) => gen(function* () {
|
|
951
|
+
const resolver = yield* SourceResolver;
|
|
952
|
+
const answers = new Map;
|
|
953
|
+
const once = (key, resolve) => suspend(() => answers.get(key) ?? resolve.pipe(exit, flatMap2((exit) => {
|
|
954
|
+
answers.set(key, exit);
|
|
955
|
+
return exit;
|
|
956
|
+
})));
|
|
957
|
+
return yield* effect.pipe(provideService(SourceResolver, {
|
|
958
|
+
gitCommit: (url, ref) => once(`git:${url}#${ref ?? ""}`, resolver.gitCommit(url, ref)),
|
|
959
|
+
npmLatest: (packageName) => once(`npm:${packageName}`, resolver.npmLatest(packageName))
|
|
960
|
+
}));
|
|
961
|
+
});
|
|
962
|
+
|
|
841
963
|
// packages/agent-runtime/src/config-writer.ts
|
|
842
964
|
var Sources = {
|
|
843
965
|
opencodeMcp: "https://opencode.ai/docs/mcp-servers/",
|
|
@@ -882,16 +1004,17 @@ var previewAgentEnvironmentConfigSync = fnUntraced(function* (previewInput) {
|
|
|
882
1004
|
const syncKitPath = input.syncKit === undefined ? undefined : findForbiddenAgentEnvironmentSecretPath(input.syncKit);
|
|
883
1005
|
if (syncKitPath !== undefined)
|
|
884
1006
|
plan.issues.push({ message: `Sync Kit contains forbidden secret-like value at ${syncKitPath}` });
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1007
|
+
const context = { ...input, ...yield* resolvePackagePlugins(input, plan) };
|
|
1008
|
+
if (context.environment.harness === "opencode")
|
|
1009
|
+
renderOpenCode(context, plan);
|
|
1010
|
+
else if (context.environment.harness === "codex")
|
|
1011
|
+
renderCodex(context, plan);
|
|
1012
|
+
else if (context.environment.harness === "claude-code")
|
|
1013
|
+
renderClaudeCode(context, plan);
|
|
1014
|
+
else if (context.environment.harness === "pi")
|
|
1015
|
+
renderPi(context, plan);
|
|
1016
|
+
renderSkillFiles(context, plan);
|
|
1017
|
+
renderManifest(context, plan);
|
|
895
1018
|
if (plan.operations.some((operation) => isUnsafeConfigWritePath(operation.path))) {
|
|
896
1019
|
plan.issues.push({ message: "AgentEnvironment config sync produced an unsafe relative write path." });
|
|
897
1020
|
}
|
|
@@ -904,13 +1027,53 @@ var previewAgentEnvironmentConfigSync = fnUntraced(function* (previewInput) {
|
|
|
904
1027
|
...input.syncKit === undefined ? undefined : { syncKitHash: input.syncKit.syncKitHash, kitRefs: input.syncKit.kitRefs }
|
|
905
1028
|
};
|
|
906
1029
|
});
|
|
1030
|
+
var resolvePackagePlugins = (input, plan) => gen(function* () {
|
|
1031
|
+
const harness = input.environment.harness;
|
|
1032
|
+
if (input.noInstall === true || harness !== "opencode" && harness !== "pi")
|
|
1033
|
+
return { environment: input.environment, unresolvedPlugins: [] };
|
|
1034
|
+
const resolver = yield* SourceResolver;
|
|
1035
|
+
const unresolvedPlugins = [];
|
|
1036
|
+
const plugins = yield* forEach(input.environment.plugins, (plugin) => {
|
|
1037
|
+
const source = packageSource(plugin);
|
|
1038
|
+
if (source === undefined || source.version !== undefined && input.versions !== "latest")
|
|
1039
|
+
return succeed5([plugin]);
|
|
1040
|
+
return resolver.npmLatest(source.packageName).pipe(map3((version) => [
|
|
1041
|
+
{
|
|
1042
|
+
...plugin,
|
|
1043
|
+
item: {
|
|
1044
|
+
...plugin.item,
|
|
1045
|
+
sources: [
|
|
1046
|
+
{ ...source, version },
|
|
1047
|
+
...plugin.item.sources.filter((candidate) => candidate !== source)
|
|
1048
|
+
]
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
]), catch_((error) => sync(() => {
|
|
1052
|
+
const issue = {
|
|
1053
|
+
message: `Kitman could not resolve plugin ${plugin.item.name} from npm package ${source.packageName}: ${error.message}`,
|
|
1054
|
+
source: sourceLabel2(plugin.source)
|
|
1055
|
+
};
|
|
1056
|
+
if (plugin.item.policy === "required")
|
|
1057
|
+
plan.issues.push(issue);
|
|
1058
|
+
else {
|
|
1059
|
+
plan.warnings.push(issue);
|
|
1060
|
+
unresolvedPlugins.push(plugin);
|
|
1061
|
+
}
|
|
1062
|
+
return [];
|
|
1063
|
+
})));
|
|
1064
|
+
});
|
|
1065
|
+
return { environment: { ...input.environment, plugins: plugins.flat() }, unresolvedPlugins };
|
|
1066
|
+
});
|
|
907
1067
|
var applyAgentEnvironmentConfigSync = fnUntraced(function* (plan) {
|
|
908
1068
|
if (plan.operations.some((operation) => isUnsafeConfigWritePath(operation.path)))
|
|
909
1069
|
return yield* new AgentEnvironmentConfigSyncError({
|
|
910
1070
|
issues: [{ message: "AgentEnvironment config apply rejected an unsafe relative write path." }]
|
|
911
1071
|
});
|
|
912
1072
|
for (const operation of plan.operations)
|
|
913
|
-
yield* writeAgentEnvironmentConfigFile(operation.path, operation.content
|
|
1073
|
+
yield* writeAgentEnvironmentConfigFile(operation.path, operation.content, {
|
|
1074
|
+
kitmanPlugins: operation.keepKitmanPlugins === true,
|
|
1075
|
+
...operation.keepPackages === undefined ? undefined : { packages: operation.keepPackages }
|
|
1076
|
+
});
|
|
914
1077
|
return plan;
|
|
915
1078
|
});
|
|
916
1079
|
var SafeModelReference = /^(?!.*(?:https?:\/\/|ftp:\/\/|file:\/\/|Bearer\s|secret|token|password))(?!.*(?:^|[\s])(?:\/|~\/|[A-Za-z]:\\\\))[A-Za-z0-9][A-Za-z0-9_.:/@-]{0,239}$/iu;
|
|
@@ -973,6 +1136,7 @@ function renderOpenCode(input, plan) {
|
|
|
973
1136
|
kind: "write_file",
|
|
974
1137
|
path: targetPath(input, "opencode.json"),
|
|
975
1138
|
description: "OpenCode project config for Kitman AgentEnvironment MCP servers and package plugins.",
|
|
1139
|
+
...keepUnresolvedPackages(input),
|
|
976
1140
|
content: json({
|
|
977
1141
|
$schema: "https://opencode.ai/config.json",
|
|
978
1142
|
...model === undefined ? undefined : { model },
|
|
@@ -992,14 +1156,18 @@ function renderCodex(input, plan) {
|
|
|
992
1156
|
if (block !== undefined)
|
|
993
1157
|
serverBlocks.push(block);
|
|
994
1158
|
}
|
|
995
|
-
|
|
1159
|
+
const materializedPlugins = input.materializedPlugins ?? [];
|
|
1160
|
+
const pluginBlocks = renderCodexPlugins(materializedPlugins);
|
|
1161
|
+
for (const plugin of unhandledPlugins(input))
|
|
1162
|
+
reportUnsupported(plan, plugin.item.policy, `plugin ${plugin.item.name}`, input.environment.harness, input.scope !== "user" && plugin.item.sources.some((source) => source.kind === "git") ? "Codex git plugins install only at user scope" : unsupportedPluginReason(plugin), sourceLabel2(plugin.source));
|
|
996
1163
|
renderCodexInstructions(input, plan);
|
|
997
1164
|
plan.operations.push({
|
|
998
1165
|
kind: "write_file",
|
|
999
1166
|
path: targetPath(input, ".codex/config.toml"),
|
|
1000
1167
|
description: "Codex project config.toml MCP server entries for Kitman AgentEnvironment.",
|
|
1001
|
-
|
|
1002
|
-
|
|
1168
|
+
...(input.skippedPlugins ?? []).length === 0 ? undefined : { keepKitmanPlugins: true },
|
|
1169
|
+
content: serverBlocks.length === 0 && pluginBlocks.length === 0 && model === undefined ? `# Kitman Codex MCP config generated by kitman sync
|
|
1170
|
+
` : `${[...model === undefined ? [] : [`model = ${tomlString(model)}`], ...serverBlocks, ...pluginBlocks].join(`
|
|
1003
1171
|
|
|
1004
1172
|
`)}
|
|
1005
1173
|
`,
|
|
@@ -1027,7 +1195,7 @@ function renderCodexInstructions(input, plan) {
|
|
|
1027
1195
|
"",
|
|
1028
1196
|
"## Plugins",
|
|
1029
1197
|
"",
|
|
1030
|
-
...markdownList(input.environment.plugins.map((entry) => `${entry.item.name} (${entry.item.policy}, ${entry
|
|
1198
|
+
...markdownList(input.environment.plugins.map((entry) => `${entry.item.name} (${entry.item.policy}, ${sourceKinds(entry, ", ")})`)),
|
|
1031
1199
|
"",
|
|
1032
1200
|
"## Harness Settings",
|
|
1033
1201
|
"",
|
|
@@ -1070,7 +1238,7 @@ function renderCodexInstructions(input, plan) {
|
|
|
1070
1238
|
}
|
|
1071
1239
|
function renderClaudeCode(input, plan) {
|
|
1072
1240
|
const mcpServers = objectMap(input.environment.mcpServers, (server) => renderClaudeMcpServer(input, server, plan));
|
|
1073
|
-
renderUnsupportedPlugins(input
|
|
1241
|
+
renderUnsupportedPlugins(unhandledPlugins(input), input.environment.harness, plan);
|
|
1074
1242
|
const model = renderModelSetting(input, plan);
|
|
1075
1243
|
const skillOverrides = Object.fromEntries((input.materializedSkills ?? []).map((skill) => [skill.name, "on"]));
|
|
1076
1244
|
if (Object.keys(mcpServers).length > 0 || Object.keys(skillOverrides).length > 0) {
|
|
@@ -1103,18 +1271,17 @@ function renderPi(input, plan) {
|
|
|
1103
1271
|
...Object.keys(mcpServers).length === 0 ? [] : ["npm:pi-mcp-adapter"],
|
|
1104
1272
|
...renderPiPlugins(input.environment.plugins, plan)
|
|
1105
1273
|
];
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
}
|
|
1274
|
+
plan.operations.push({
|
|
1275
|
+
kind: "write_file",
|
|
1276
|
+
path: targetPath(input, ".pi/settings.json"),
|
|
1277
|
+
description: "Pi project settings generated from resolved non-secret MCP and model intent.",
|
|
1278
|
+
...keepUnresolvedPackages(input),
|
|
1279
|
+
content: json({
|
|
1280
|
+
...packages.length === 0 ? undefined : { packages },
|
|
1281
|
+
...model === undefined ? undefined : { model }
|
|
1282
|
+
}),
|
|
1283
|
+
sources: [Sources.piReadme, Sources.piMcpAdapter]
|
|
1284
|
+
});
|
|
1118
1285
|
plan.operations.push({
|
|
1119
1286
|
kind: "write_file",
|
|
1120
1287
|
path: targetPath(input, ".pi/mcp.json"),
|
|
@@ -1126,8 +1293,9 @@ function renderPi(input, plan) {
|
|
|
1126
1293
|
function renderPiPlugins(plugins, plan) {
|
|
1127
1294
|
const packages = [];
|
|
1128
1295
|
for (const plugin of plugins) {
|
|
1129
|
-
|
|
1130
|
-
|
|
1296
|
+
const source = packageSource(plugin);
|
|
1297
|
+
if (source) {
|
|
1298
|
+
packages.push(`npm:${packageReference(source.packageName, source.version)}`);
|
|
1131
1299
|
continue;
|
|
1132
1300
|
}
|
|
1133
1301
|
reportUnsupported(plan, plugin.item.policy, `plugin ${plugin.item.name}`, "pi", "Pi package plugins require an npm package source", sourceLabel2(plugin.source));
|
|
@@ -1250,20 +1418,52 @@ function hasNameArgument(args) {
|
|
|
1250
1418
|
function renderOpenCodePlugins(plugins, plan) {
|
|
1251
1419
|
const packages = [];
|
|
1252
1420
|
for (const plugin of plugins) {
|
|
1253
|
-
|
|
1254
|
-
|
|
1421
|
+
const source = packageSource(plugin);
|
|
1422
|
+
if (source) {
|
|
1423
|
+
packages.push(packageReference(source.packageName, source.version));
|
|
1255
1424
|
continue;
|
|
1256
1425
|
}
|
|
1257
1426
|
reportUnsupported(plan, plugin.item.policy, `plugin ${plugin.item.name}`, "opencode", "this writer only has source-verified OpenCode plugin config for package sources", sourceLabel2(plugin.source));
|
|
1258
1427
|
}
|
|
1259
1428
|
return packages;
|
|
1260
1429
|
}
|
|
1430
|
+
function keepUnresolvedPackages(input) {
|
|
1431
|
+
const packages = (input.unresolvedPlugins ?? []).flatMap((plugin) => packageSource(plugin)?.packageName ?? []);
|
|
1432
|
+
return packages.length === 0 ? undefined : { keepPackages: packages };
|
|
1433
|
+
}
|
|
1434
|
+
function unhandledPlugins(input) {
|
|
1435
|
+
const handled = new Set([
|
|
1436
|
+
...(input.materializedPlugins ?? []).map((plugin) => plugin.name),
|
|
1437
|
+
...input.skippedPlugins ?? []
|
|
1438
|
+
]);
|
|
1439
|
+
return input.environment.plugins.filter((plugin) => !handled.has(plugin.item.name));
|
|
1440
|
+
}
|
|
1441
|
+
function renderCodexPlugins(plugins) {
|
|
1442
|
+
const marketplaceRoot = plugins[0]?.marketplaceRoot;
|
|
1443
|
+
if (marketplaceRoot === undefined)
|
|
1444
|
+
return [];
|
|
1445
|
+
return [
|
|
1446
|
+
`[marketplaces.kitman]
|
|
1447
|
+
source_type = "local"
|
|
1448
|
+
source = ${tomlString(marketplaceRoot)}`,
|
|
1449
|
+
...plugins.map((plugin) => `[plugins.${tomlString(`${plugin.name}@kitman`)}]
|
|
1450
|
+
enabled = true`)
|
|
1451
|
+
];
|
|
1452
|
+
}
|
|
1261
1453
|
function renderUnsupportedPlugins(plugins, harness, plan) {
|
|
1262
1454
|
for (const plugin of plugins) {
|
|
1263
|
-
|
|
1264
|
-
reportUnsupported(plan, plugin.item.policy, `plugin ${plugin.item.name}`, harness, `this writer has no source-verified plugin installer for ${sourceKind}`, sourceLabel2(plugin.source));
|
|
1455
|
+
reportUnsupported(plan, plugin.item.policy, `plugin ${plugin.item.name}`, harness, unsupportedPluginReason(plugin), sourceLabel2(plugin.source));
|
|
1265
1456
|
}
|
|
1266
1457
|
}
|
|
1458
|
+
function unsupportedPluginReason(plugin) {
|
|
1459
|
+
return `this writer has no source-verified plugin installer for ${sourceKinds(plugin, " or ")} sources`;
|
|
1460
|
+
}
|
|
1461
|
+
function packageSource(plugin) {
|
|
1462
|
+
return plugin.item.sources.find((source) => source.kind === "package");
|
|
1463
|
+
}
|
|
1464
|
+
function sourceKinds(plugin, separator) {
|
|
1465
|
+
return [...new Set(plugin.item.sources.map((source) => source.kind))].join(separator);
|
|
1466
|
+
}
|
|
1267
1467
|
function renderSkillFiles(input, plan) {
|
|
1268
1468
|
const materializedSkillNames = new Set((input.materializedSkills ?? []).map((skill) => skill.name));
|
|
1269
1469
|
for (const skill of input.environment.skills) {
|
|
@@ -1312,7 +1512,7 @@ function renderManifest(input, plan) {
|
|
|
1312
1512
|
generatedBy: "kitman",
|
|
1313
1513
|
harness: input.environment.harness,
|
|
1314
1514
|
skills: input.environment.skills,
|
|
1315
|
-
plugins: input.environment.plugins,
|
|
1515
|
+
plugins: [...input.environment.plugins, ...input.unresolvedPlugins ?? []],
|
|
1316
1516
|
mcpServers: input.environment.mcpServers,
|
|
1317
1517
|
harnessSettings: input.environment.harnessSettings,
|
|
1318
1518
|
providerAuthRefs: input.environment.providerAuthRefs,
|
|
@@ -1596,6 +1796,37 @@ function repositoryUrl(repoBinding) {
|
|
|
1596
1796
|
return repoBinding.repositoryUrl ?? `https://${repoBinding.host}/${repoBinding.owner}/${repoBinding.name}.git`;
|
|
1597
1797
|
}
|
|
1598
1798
|
// packages/agent-runtime/src/skill-materializer.ts
|
|
1799
|
+
var gitSourceKey = (source) => `${source.url}#${source.ref ?? ""}:${source.path ?? ""}`;
|
|
1800
|
+
var approvedCommits = (approved, source) => approved.flatMap((entry) => {
|
|
1801
|
+
const [commit, path] = entry.startsWith(`${source.url}#`) ? entry.slice(source.url.length + 1).split(/:(.*)/su) : [];
|
|
1802
|
+
return isCommitHash(commit) && path === (source.path ?? "") ? [commit] : [];
|
|
1803
|
+
});
|
|
1804
|
+
var resolveGitSource = (subject, source, input) => gen(function* () {
|
|
1805
|
+
if (!source.url.startsWith("https://"))
|
|
1806
|
+
return { problem: `${subject} must come from an https URL, not ${source.url}.` };
|
|
1807
|
+
const resolver = yield* SourceResolver;
|
|
1808
|
+
const latest = input.versions === "latest";
|
|
1809
|
+
const ref = latest ? undefined : source.ref;
|
|
1810
|
+
const resolved = (isCommitHash(ref) ? succeed5(ref) : resolver.gitCommit(source.url, ref)).pipe(map3((commit) => ({ commit })), catch_((error) => succeed5({
|
|
1811
|
+
problem: `Kitman could not resolve ${subject} from ${source.url}${ref === undefined ? "" : ` at ${ref}`}: ${error.message}`
|
|
1812
|
+
})));
|
|
1813
|
+
if (input.requireReviewedSources !== true)
|
|
1814
|
+
return yield* resolved;
|
|
1815
|
+
const approved = approvedCommits(input.approvedSkillSources ?? [], source);
|
|
1816
|
+
const wanted = isCommitHash(ref) ? { commit: ref } : undefined;
|
|
1817
|
+
if (ref === undefined && !latest && approved.length > 0)
|
|
1818
|
+
return { commit: approved.at(-1) ?? "" };
|
|
1819
|
+
const candidate = wanted ?? (yield* resolved);
|
|
1820
|
+
if ("problem" in candidate)
|
|
1821
|
+
return { rejected: true, problem: `${candidate.problem} Under review Kitman keeps no copy it cannot check.` };
|
|
1822
|
+
if (approved.includes(candidate.commit))
|
|
1823
|
+
return candidate;
|
|
1824
|
+
return {
|
|
1825
|
+
rejected: true,
|
|
1826
|
+
problem: `${subject} is not approved at ${candidate.commit}; approve it by adding ${source.url}#${candidate.commit}:${source.path ?? ""} to approvedSkillSources in your Kitman user config, or remove it from the Kit.`
|
|
1827
|
+
};
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1599
1830
|
class AgentEnvironmentMaterializationError extends TaggedError("AgentEnvironmentMaterializationError") {
|
|
1600
1831
|
get message() {
|
|
1601
1832
|
return `AgentEnvironment materialization failed: ${this.issues.map((issue) => issue.message).join("; ")}`;
|
|
@@ -1605,6 +1836,7 @@ class AgentEnvironmentMaterializationError extends TaggedError("AgentEnvironment
|
|
|
1605
1836
|
class SkillMaterializationRefused extends TaggedError("SkillMaterializationRefused") {
|
|
1606
1837
|
}
|
|
1607
1838
|
var previewAgentEnvironmentMaterialization = fnUntraced(function* (input) {
|
|
1839
|
+
const fs = yield* FileSystem;
|
|
1608
1840
|
const path = yield* Path;
|
|
1609
1841
|
const home = input.rootDir ?? (yield* optionalEnv("HOME")) ?? ".";
|
|
1610
1842
|
const harness = input.environment.harness;
|
|
@@ -1612,68 +1844,341 @@ var previewAgentEnvironmentMaterialization = fnUntraced(function* (input) {
|
|
|
1612
1844
|
const targetRoot = skillBasePath2(path, harness, input.scope, input.rootDir, home);
|
|
1613
1845
|
const operations = [];
|
|
1614
1846
|
const materializedSkills = [];
|
|
1847
|
+
const materializedPlugins = [];
|
|
1848
|
+
const skippedPlugins = [];
|
|
1615
1849
|
const warnings = [];
|
|
1850
|
+
const unresolvedLinks = [];
|
|
1616
1851
|
for (const skill of input.environment.skills) {
|
|
1617
1852
|
const skillKey = skillIdentity(skill.item);
|
|
1853
|
+
const targetPath = path.join(targetRoot, `${skillSlug2(skill.item.name)}-${hashSegment(skillKey)}`);
|
|
1618
1854
|
const source = skill.item.source;
|
|
1619
|
-
const
|
|
1620
|
-
|
|
1855
|
+
const fromKit = skill.source.label === undefined ? "" : ` (from Kit ${skill.source.label})`;
|
|
1856
|
+
const gitSkillPath = path.join(sourceRoot, "git", hashSegment(skillKey), skillSlug2(skill.item.name));
|
|
1857
|
+
const problem = source.kind === "kitman" ? source.contentMarkdown === undefined ? `Kitman skill ${skill.item.name} has no contentMarkdown to materialize.` : undefined : input.noInstall === true ? `Skipping materialization for ${skill.item.name} because sync was run with noInstall.` : source.kind !== "git" ? `Skill ${skill.item.name}${fromKit} comes from ${source.kind}, which Kitman cannot install yet; only Kitman and git skills are installed. Remove it from the Kit or make it recommended.` : undefined;
|
|
1858
|
+
const resolution = problem === undefined && source.kind === "git" ? yield* resolveGitSource(`Git skill ${skill.item.name}${fromKit}`, source, input) : undefined;
|
|
1859
|
+
const issueMessage = problem ?? (resolution !== undefined && "problem" in resolution ? resolution.problem : undefined);
|
|
1860
|
+
if (issueMessage !== undefined) {
|
|
1861
|
+
const issue = { message: issueMessage, skillName: skill.item.name, skillKey };
|
|
1621
1862
|
if (skill.item.policy === "required")
|
|
1622
|
-
return new AgentEnvironmentMaterializationError({ issues: [issue] });
|
|
1863
|
+
return yield* new AgentEnvironmentMaterializationError({ issues: [issue] });
|
|
1623
1864
|
warnings.push(issue);
|
|
1624
|
-
|
|
1865
|
+
if (resolution !== undefined && "rejected" in resolution)
|
|
1866
|
+
continue;
|
|
1867
|
+
unresolvedLinks.push(targetPath);
|
|
1868
|
+
if (source.kind === "git" && (yield* fs.exists(gitSkillPath).pipe(orElseSucceed(() => false))))
|
|
1869
|
+
materializedSkills.push({
|
|
1870
|
+
name: skill.item.name,
|
|
1871
|
+
key: skillKey,
|
|
1872
|
+
harness,
|
|
1873
|
+
source,
|
|
1874
|
+
skillPath: gitSkillPath,
|
|
1875
|
+
managed: true
|
|
1876
|
+
});
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
const commit = resolution !== undefined && "commit" in resolution ? resolution.commit : undefined;
|
|
1880
|
+
const expose = (skillPath) => {
|
|
1881
|
+
operations.push({
|
|
1882
|
+
kind: "symlink_skill",
|
|
1883
|
+
skillName: skill.item.name,
|
|
1884
|
+
skillKey,
|
|
1885
|
+
sourcePath: skillPath,
|
|
1886
|
+
targetPath,
|
|
1887
|
+
sourceRoot,
|
|
1888
|
+
targetRoot
|
|
1889
|
+
});
|
|
1890
|
+
materializedSkills.push({
|
|
1891
|
+
name: skill.item.name,
|
|
1892
|
+
key: skillKey,
|
|
1893
|
+
harness,
|
|
1894
|
+
source,
|
|
1895
|
+
skillPath,
|
|
1896
|
+
managed: true,
|
|
1897
|
+
...commit === undefined ? undefined : { commit }
|
|
1898
|
+
});
|
|
1625
1899
|
};
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1900
|
+
if (source.kind === "git" && commit !== undefined) {
|
|
1901
|
+
operations.push({
|
|
1902
|
+
kind: "fetch_git_skill",
|
|
1903
|
+
skillName: skill.item.name,
|
|
1904
|
+
skillKey,
|
|
1905
|
+
url: source.url,
|
|
1906
|
+
ref: commit,
|
|
1907
|
+
...source.path === undefined ? undefined : { repositoryPath: source.path },
|
|
1908
|
+
path: gitSkillPath
|
|
1909
|
+
});
|
|
1910
|
+
expose(gitSkillPath);
|
|
1911
|
+
} else if (source.kind === "kitman" && source.contentMarkdown !== undefined) {
|
|
1912
|
+
const skillPath = path.join(sourceRoot, "kitman", hashSegment(source.skillId ?? source.name ?? skill.item.name), skillSlug2(skill.item.name));
|
|
1913
|
+
operations.push({
|
|
1914
|
+
kind: "write_skill",
|
|
1915
|
+
skillName: skill.item.name,
|
|
1916
|
+
skillKey,
|
|
1917
|
+
path: path.join(skillPath, "SKILL.md"),
|
|
1918
|
+
content: skillMarkdown(skill.item.name, source.contentMarkdown)
|
|
1919
|
+
});
|
|
1920
|
+
expose(skillPath);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const codexMarketplace = harness === "codex" && input.scope === "user";
|
|
1924
|
+
const marketplaceRoot = path.join(sourceRoot, "codex-marketplace");
|
|
1925
|
+
const claudePluginsRoot = path.join(sourceRoot, "git-plugins");
|
|
1926
|
+
for (const plugin of harness === "claude-code" || codexMarketplace ? input.environment.plugins : []) {
|
|
1927
|
+
const source = plugin.item.sources.find((candidate) => candidate.kind === "git");
|
|
1928
|
+
if (source === undefined)
|
|
1929
|
+
continue;
|
|
1930
|
+
const name = plugin.item.name;
|
|
1931
|
+
const pluginKey = `plugin:git:${gitSourceKey(source)}:${name}`;
|
|
1932
|
+
const fromKit = plugin.source.label === undefined ? "" : ` (from Kit ${plugin.source.label})`;
|
|
1933
|
+
if (input.noInstall === true) {
|
|
1934
|
+
skippedPlugins.push(name);
|
|
1935
|
+
warnings.push({
|
|
1936
|
+
message: `Skipping materialization for plugin ${name} because sync was run with noInstall.`,
|
|
1937
|
+
skillName: name,
|
|
1938
|
+
skillKey: pluginKey
|
|
1939
|
+
});
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
const directoryName = `${skillSlug2(name)}-${hashSegment(pluginKey)}`;
|
|
1943
|
+
const [pluginPath, manifest] = harness === "codex" ? [path.join(marketplaceRoot, "plugins", directoryName), ".codex-plugin/plugin.json"] : [path.join(claudePluginsRoot, hashSegment(pluginKey), skillSlug2(name)), ".claude-plugin/plugin.json"];
|
|
1944
|
+
const resolution = yield* resolveGitSource(`Git plugin ${name}${fromKit}`, source, input);
|
|
1945
|
+
if ("problem" in resolution) {
|
|
1946
|
+
const issue = { message: resolution.problem, skillName: name, skillKey: pluginKey };
|
|
1947
|
+
if (plugin.item.policy === "required")
|
|
1948
|
+
return yield* new AgentEnvironmentMaterializationError({ issues: [issue] });
|
|
1949
|
+
warnings.push(issue);
|
|
1950
|
+
if ("rejected" in resolution)
|
|
1951
|
+
continue;
|
|
1952
|
+
unresolvedLinks.push(path.join(targetRoot, directoryName));
|
|
1953
|
+
if (yield* fs.exists(path.join(pluginPath, manifest)).pipe(orElseSucceed(() => false)))
|
|
1954
|
+
materializedPlugins.push({
|
|
1955
|
+
name,
|
|
1956
|
+
key: pluginKey,
|
|
1957
|
+
harness,
|
|
1958
|
+
source,
|
|
1959
|
+
pluginPath,
|
|
1960
|
+
...harness === "codex" ? { marketplaceRoot } : undefined
|
|
1961
|
+
});
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
operations.push({
|
|
1965
|
+
kind: "fetch_git_plugin",
|
|
1966
|
+
pluginName: name,
|
|
1967
|
+
pluginKey,
|
|
1968
|
+
url: source.url,
|
|
1969
|
+
ref: resolution.commit,
|
|
1970
|
+
manifest,
|
|
1971
|
+
path: pluginPath,
|
|
1972
|
+
sourceRoot
|
|
1973
|
+
});
|
|
1974
|
+
if (harness === "codex") {
|
|
1975
|
+
materializedPlugins.push({
|
|
1976
|
+
name,
|
|
1977
|
+
key: pluginKey,
|
|
1978
|
+
harness,
|
|
1979
|
+
source,
|
|
1980
|
+
pluginPath,
|
|
1981
|
+
marketplaceRoot,
|
|
1982
|
+
commit: resolution.commit
|
|
1983
|
+
});
|
|
1630
1984
|
continue;
|
|
1631
|
-
|
|
1985
|
+
}
|
|
1632
1986
|
operations.push({
|
|
1633
|
-
kind: "write_skill",
|
|
1634
|
-
skillName: skill.item.name,
|
|
1635
|
-
skillKey,
|
|
1636
|
-
path: path.join(skillPath, "SKILL.md"),
|
|
1637
|
-
content: skillMarkdown(skill.item.name, source.contentMarkdown)
|
|
1638
|
-
}, {
|
|
1639
1987
|
kind: "symlink_skill",
|
|
1640
|
-
skillName:
|
|
1641
|
-
skillKey,
|
|
1642
|
-
sourcePath:
|
|
1643
|
-
targetPath: path.join(targetRoot,
|
|
1988
|
+
skillName: name,
|
|
1989
|
+
skillKey: pluginKey,
|
|
1990
|
+
sourcePath: pluginPath,
|
|
1991
|
+
targetPath: path.join(targetRoot, directoryName),
|
|
1644
1992
|
sourceRoot,
|
|
1645
1993
|
targetRoot
|
|
1646
1994
|
});
|
|
1647
|
-
|
|
1995
|
+
materializedPlugins.push({ name, key: pluginKey, harness, source, pluginPath, commit: resolution.commit });
|
|
1648
1996
|
}
|
|
1649
|
-
|
|
1997
|
+
if (input.noInstall !== true)
|
|
1998
|
+
operations.push({
|
|
1999
|
+
kind: "unlink_stale_links",
|
|
2000
|
+
path: targetRoot,
|
|
2001
|
+
sourceRoot,
|
|
2002
|
+
keepPaths: [
|
|
2003
|
+
...operations.flatMap((operation) => operation.kind === "symlink_skill" ? [operation.targetPath] : []),
|
|
2004
|
+
...unresolvedLinks
|
|
2005
|
+
],
|
|
2006
|
+
sharedWith: AgentHarnessIds.filter((other) => other !== harness && skillBasePath2(path, other, input.scope, input.rootDir, home) === targetRoot).map((other) => ({
|
|
2007
|
+
harness: other,
|
|
2008
|
+
manifestPath: path.join(input.scope === "user" ? home : input.rootDir ?? ".", ".kitman", `agent-environment.${other}.manifest.json`)
|
|
2009
|
+
}))
|
|
2010
|
+
});
|
|
2011
|
+
const marketplacePath = path.join(marketplaceRoot, ".agents", "plugins", "marketplace.json");
|
|
2012
|
+
if (codexMarketplace && input.noInstall !== true)
|
|
2013
|
+
operations.push({
|
|
2014
|
+
kind: "write_codex_marketplace",
|
|
2015
|
+
path: marketplacePath,
|
|
2016
|
+
content: `${yield* encodePrettyJsonString({
|
|
2017
|
+
name: "kitman",
|
|
2018
|
+
interface: { displayName: "Kitman" },
|
|
2019
|
+
plugins: materializedPlugins.map((plugin) => ({
|
|
2020
|
+
name: plugin.name,
|
|
2021
|
+
source: { source: "local", path: `./${path.relative(marketplaceRoot, plugin.pluginPath)}` },
|
|
2022
|
+
policy: { installation: "INSTALLED_BY_DEFAULT", authentication: "ON_INSTALL" },
|
|
2023
|
+
category: "Productivity"
|
|
2024
|
+
}))
|
|
2025
|
+
}).pipe(orDie)}
|
|
2026
|
+
`,
|
|
2027
|
+
sourceRoot
|
|
2028
|
+
});
|
|
2029
|
+
return {
|
|
2030
|
+
harness,
|
|
2031
|
+
operations,
|
|
2032
|
+
materializedSkills,
|
|
2033
|
+
materializedPlugins,
|
|
2034
|
+
skippedPlugins,
|
|
2035
|
+
warnings,
|
|
2036
|
+
...codexMarketplace && materializedPlugins.length > 0 ? { codexHookTrust: { home, marketplacePath } } : undefined
|
|
2037
|
+
};
|
|
1650
2038
|
});
|
|
1651
|
-
var
|
|
2039
|
+
var gitCheckouts = gen(function* () {
|
|
2040
|
+
const scope = yield* Scope;
|
|
2041
|
+
const trees = new Map;
|
|
2042
|
+
const checkout = (url, ref) => gen(function* () {
|
|
2043
|
+
const key = `${url}#${ref}`;
|
|
2044
|
+
const known = trees.get(key);
|
|
2045
|
+
if (known !== undefined)
|
|
2046
|
+
return known;
|
|
2047
|
+
const tree = yield* checkoutGitTree(url, ref).pipe(provide(scope));
|
|
2048
|
+
trees.set(key, tree);
|
|
2049
|
+
return tree;
|
|
2050
|
+
});
|
|
2051
|
+
return checkout;
|
|
2052
|
+
});
|
|
2053
|
+
var applyAgentEnvironmentMaterialization = (plan, checkout) => gen(function* () {
|
|
2054
|
+
const checkoutTree = checkout ?? (yield* gitCheckouts);
|
|
1652
2055
|
for (const operation of plan.operations) {
|
|
1653
2056
|
if (operation.kind === "write_skill")
|
|
1654
2057
|
yield* writeSkill(plan, operation);
|
|
2058
|
+
else if (operation.kind === "fetch_git_skill")
|
|
2059
|
+
yield* fetchGitSkill(plan, operation, checkoutTree);
|
|
2060
|
+
else if (operation.kind === "fetch_git_plugin")
|
|
2061
|
+
yield* fetchGitPlugin(operation, checkoutTree);
|
|
2062
|
+
else if (operation.kind === "write_codex_marketplace")
|
|
2063
|
+
yield* writeCodexMarketplace(operation);
|
|
2064
|
+
else if (operation.kind === "unlink_stale_links")
|
|
2065
|
+
yield* unlinkStaleLinks(operation);
|
|
1655
2066
|
else
|
|
1656
2067
|
yield* exposeSkill(operation);
|
|
1657
2068
|
}
|
|
1658
2069
|
return plan;
|
|
1659
|
-
});
|
|
2070
|
+
}).pipe(scoped);
|
|
1660
2071
|
var refuse = (message) => fail4(new SkillMaterializationRefused({ message }));
|
|
1661
|
-
var
|
|
1662
|
-
const
|
|
1663
|
-
const path = yield* Path;
|
|
1664
|
-
const boundary = plan.operations.find((candidate) => candidate.kind === "symlink_skill" && candidate.skillKey === operation.skillKey);
|
|
2072
|
+
var managedSkillPath = (plan, skillKey, file) => gen(function* () {
|
|
2073
|
+
const boundary = plan.operations.find((candidate) => candidate.kind === "symlink_skill" && candidate.skillKey === skillKey);
|
|
1665
2074
|
if (boundary === undefined)
|
|
1666
2075
|
return yield* refuse("SKILL_SYMLINK_BOUNDARY_MISSING");
|
|
1667
|
-
|
|
1668
|
-
|
|
2076
|
+
return yield* managedPath(boundary.sourceRoot, file);
|
|
2077
|
+
});
|
|
2078
|
+
var managedPath = (sourceRoot, file) => gen(function* () {
|
|
2079
|
+
const path = yield* Path;
|
|
2080
|
+
const root = path.resolve(sourceRoot);
|
|
2081
|
+
const target = path.resolve(file);
|
|
1669
2082
|
if (!isPathWithin(path, root, target) || target === root)
|
|
1670
2083
|
return yield* refuse("SKILL_WRITE_OUTSIDE_ROOT");
|
|
1671
2084
|
yield* assertNoSymlinkComponents(path.dirname(target), "SKILL_WRITE_OUTSIDE_ROOT");
|
|
2085
|
+
return target;
|
|
2086
|
+
});
|
|
2087
|
+
var writeSkill = (plan, operation) => gen(function* () {
|
|
2088
|
+
const fs = yield* FileSystem;
|
|
2089
|
+
const path = yield* Path;
|
|
2090
|
+
const target = yield* managedSkillPath(plan, operation.skillKey, operation.path);
|
|
1672
2091
|
if (yield* isSymbolicLink(target))
|
|
1673
2092
|
return yield* refuse("SKILL_WRITE_SYMLINK");
|
|
1674
2093
|
yield* fs.makeDirectory(path.dirname(target), { recursive: true });
|
|
1675
2094
|
yield* fs.writeFileString(target, operation.content);
|
|
1676
2095
|
});
|
|
2096
|
+
var checkoutGitTree = (url, ref) => gen(function* () {
|
|
2097
|
+
const fs = yield* FileSystem;
|
|
2098
|
+
const path = yield* Path;
|
|
2099
|
+
const scratch = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-git-" });
|
|
2100
|
+
const repository = path.join(scratch, "repository");
|
|
2101
|
+
const tree = path.join(scratch, "tree");
|
|
2102
|
+
const git = (...args) => runCommand2("git", ["--git-dir", repository, ...args]);
|
|
2103
|
+
yield* fs.makeDirectory(tree);
|
|
2104
|
+
yield* runCommand2("git", ["init", "--quiet", "--bare", repository]);
|
|
2105
|
+
yield* git("fetch", "--quiet", "--depth", "1", "--", url, ref);
|
|
2106
|
+
yield* git("--work-tree", tree, "checkout", "FETCH_HEAD", "--", ".");
|
|
2107
|
+
return tree;
|
|
2108
|
+
});
|
|
2109
|
+
var replaceWithCopy = (directory, target) => gen(function* () {
|
|
2110
|
+
const fs = yield* FileSystem;
|
|
2111
|
+
const path = yield* Path;
|
|
2112
|
+
const staged = `${target}.partial`;
|
|
2113
|
+
yield* fs.remove(staged, { recursive: true, force: true });
|
|
2114
|
+
yield* fs.makeDirectory(path.dirname(target), { recursive: true });
|
|
2115
|
+
yield* fs.copy(directory, staged);
|
|
2116
|
+
yield* fs.remove(target, { recursive: true, force: true });
|
|
2117
|
+
yield* fs.rename(staged, target);
|
|
2118
|
+
});
|
|
2119
|
+
var fetchGitSkill = (plan, operation, checkout) => gen(function* () {
|
|
2120
|
+
const fs = yield* FileSystem;
|
|
2121
|
+
const path = yield* Path;
|
|
2122
|
+
const target = yield* managedSkillPath(plan, operation.skillKey, operation.path);
|
|
2123
|
+
const tree = yield* checkout(operation.url, operation.ref);
|
|
2124
|
+
const skillDirectory = path.resolve(tree, operation.repositoryPath ?? ".");
|
|
2125
|
+
if (!isPathWithin(path, tree, skillDirectory))
|
|
2126
|
+
return yield* refuse("SKILL_SOURCE_OUTSIDE_ROOT");
|
|
2127
|
+
if (yield* containsSymbolicLink(tree, skillDirectory))
|
|
2128
|
+
return yield* refuse(`Git skill ${operation.skillName} links to a file outside itself in ${operation.url}@${operation.ref}; Kitman does not install linked files.`);
|
|
2129
|
+
if (!(yield* fs.exists(path.join(skillDirectory, "SKILL.md"))))
|
|
2130
|
+
return yield* refuse(`Git skill ${operation.skillName} has no SKILL.md at ${operation.repositoryPath ?? "the repository root"} in ${operation.url}@${operation.ref}.`);
|
|
2131
|
+
yield* replaceWithCopy(skillDirectory, target);
|
|
2132
|
+
});
|
|
2133
|
+
var writeCodexMarketplace = (operation) => gen(function* () {
|
|
2134
|
+
const fs = yield* FileSystem;
|
|
2135
|
+
const path = yield* Path;
|
|
2136
|
+
const target = yield* managedPath(operation.sourceRoot, operation.path);
|
|
2137
|
+
if (yield* isSymbolicLink(target))
|
|
2138
|
+
return yield* refuse("SKILL_WRITE_SYMLINK");
|
|
2139
|
+
yield* fs.makeDirectory(path.dirname(target), { recursive: true });
|
|
2140
|
+
yield* fs.writeFileString(target, operation.content);
|
|
2141
|
+
});
|
|
2142
|
+
var fetchGitPlugin = (operation, checkout) => gen(function* () {
|
|
2143
|
+
const fs = yield* FileSystem;
|
|
2144
|
+
const path = yield* Path;
|
|
2145
|
+
const target = yield* managedPath(operation.sourceRoot, operation.path);
|
|
2146
|
+
const tree = yield* checkout(operation.url, operation.ref);
|
|
2147
|
+
if (yield* containsSymbolicLink(tree, tree))
|
|
2148
|
+
return yield* refuse(`Git plugin ${operation.pluginName} links to a file in ${operation.url}@${operation.ref}; Kitman does not install linked files.`);
|
|
2149
|
+
if (!(yield* fs.exists(path.join(tree, operation.manifest))))
|
|
2150
|
+
return yield* refuse(`Git plugin ${operation.pluginName} has no ${operation.manifest} at the root of ${operation.url}@${operation.ref}.`);
|
|
2151
|
+
yield* replaceWithCopy(tree, target);
|
|
2152
|
+
});
|
|
2153
|
+
var unlinkStaleLinks = (operation) => gen(function* () {
|
|
2154
|
+
const fs = yield* FileSystem;
|
|
2155
|
+
const path = yield* Path;
|
|
2156
|
+
if (!(yield* fs.exists(operation.path)))
|
|
2157
|
+
return;
|
|
2158
|
+
yield* assertNoSymlinkComponents(operation.path, "SKILL_TARGET_SYMLINK_PARENT");
|
|
2159
|
+
const sharedSkillPaths = new Set((yield* forEach(operation.sharedWith, (shared) => readManifestSkillPaths(shared.manifestPath))).flatMap((paths) => [...paths].map((skillPath) => path.resolve(skillPath))));
|
|
2160
|
+
for (const entry of yield* fs.readDirectory(operation.path)) {
|
|
2161
|
+
const link = path.join(operation.path, entry);
|
|
2162
|
+
if (operation.keepPaths.includes(link) || !(yield* isSymbolicLink(link)))
|
|
2163
|
+
continue;
|
|
2164
|
+
const linkTarget = path.resolve(operation.path, yield* fs.readLink(link));
|
|
2165
|
+
if (isPathWithin(path, operation.sourceRoot, linkTarget) && !sharedSkillPaths.has(linkTarget))
|
|
2166
|
+
yield* fs.remove(link);
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
var containsSymbolicLink = (root, directory) => gen(function* () {
|
|
2170
|
+
const fs = yield* FileSystem;
|
|
2171
|
+
const path = yield* Path;
|
|
2172
|
+
for (let current = directory;current !== root; current = path.dirname(current))
|
|
2173
|
+
if (yield* isSymbolicLink(current))
|
|
2174
|
+
return true;
|
|
2175
|
+
if (!(yield* fs.exists(directory)))
|
|
2176
|
+
return false;
|
|
2177
|
+
for (const entry of yield* fs.readDirectory(directory, { recursive: true }))
|
|
2178
|
+
if (yield* isSymbolicLink(path.join(directory, entry)))
|
|
2179
|
+
return true;
|
|
2180
|
+
return false;
|
|
2181
|
+
});
|
|
1677
2182
|
var exposeSkill = (operation) => gen(function* () {
|
|
1678
2183
|
const fs = yield* FileSystem;
|
|
1679
2184
|
const path = yield* Path;
|
|
@@ -1707,8 +2212,8 @@ var exposeSkill = (operation) => gen(function* () {
|
|
|
1707
2212
|
}
|
|
1708
2213
|
yield* fs.symlink(path.resolve(operation.sourcePath), targetPath);
|
|
1709
2214
|
});
|
|
1710
|
-
var isSymbolicLink = (file) =>
|
|
1711
|
-
var realPathOr = (file, reason) =>
|
|
2215
|
+
var isSymbolicLink = (file) => flatMap2(FileSystem, (fs) => fs.readLink(file).pipe(as(true), orElseSucceed(() => false)));
|
|
2216
|
+
var realPathOr = (file, reason) => flatMap2(FileSystem, (fs) => fs.realPath(file).pipe(mapError(() => new SkillMaterializationRefused({ message: reason }))));
|
|
1712
2217
|
var assertNoSymlinkComponents = (file, reason) => gen(function* () {
|
|
1713
2218
|
const path = yield* Path;
|
|
1714
2219
|
for (let current = path.resolve(file);; current = path.dirname(current)) {
|
|
@@ -1729,7 +2234,7 @@ function skillIdentity(skill) {
|
|
|
1729
2234
|
if (source.kind === "kitman")
|
|
1730
2235
|
return `kitman:${source.skillId ?? source.name ?? skill.name}:${skill.name}`;
|
|
1731
2236
|
if (source.kind === "git")
|
|
1732
|
-
return `git:${source.url}#${source.ref}:${source.path ?? ""}:${skill.name}`;
|
|
2237
|
+
return `git:${source.url}#${source.ref ?? ""}:${source.path ?? ""}:${skill.name}`;
|
|
1733
2238
|
if (source.kind === "package")
|
|
1734
2239
|
return `package:${source.packageName}@${source.version}:${source.path ?? ""}:${skill.name}`;
|
|
1735
2240
|
if (source.kind === "url")
|
|
@@ -1743,7 +2248,9 @@ var ProjectSkillDirectories = {
|
|
|
1743
2248
|
pi: ".pi/skills"
|
|
1744
2249
|
};
|
|
1745
2250
|
function skillBasePath2(path, harness, scope, rootDir, home) {
|
|
1746
|
-
|
|
2251
|
+
if (scope !== "user")
|
|
2252
|
+
return path.join(rootDir ?? ".", ProjectSkillDirectories[harness]);
|
|
2253
|
+
return harness === "claude-code" ? path.join(home, ".claude", "skills") : path.join(home, ".agents", "skills");
|
|
1747
2254
|
}
|
|
1748
2255
|
function skillMarkdown(name, contentMarkdown) {
|
|
1749
2256
|
const content = contentMarkdown.trimEnd();
|
|
@@ -1798,7 +2305,7 @@ var closeSocket = (conn, isOpen) => {
|
|
|
1798
2305
|
var makeNet = (options) => fromDuplex(contextWith((context) => {
|
|
1799
2306
|
let conn;
|
|
1800
2307
|
let isOpen = false;
|
|
1801
|
-
return
|
|
2308
|
+
return flatMap2(addFinalizer(get(context, Scope), sync(() => {
|
|
1802
2309
|
if (!conn)
|
|
1803
2310
|
return;
|
|
1804
2311
|
closeSocket(conn, isOpen);
|
|
@@ -2200,7 +2707,7 @@ class HerdrSocketClient {
|
|
|
2200
2707
|
});
|
|
2201
2708
|
}
|
|
2202
2709
|
sessionSnapshot() {
|
|
2203
|
-
return
|
|
2710
|
+
return flatMap2(this.request("session.snapshot", {}), (result) => {
|
|
2204
2711
|
const snapshot = isRecord(result) && result.type === "session_snapshot" ? result.snapshot : undefined;
|
|
2205
2712
|
if (!isRecord(snapshot) || !isString2(snapshot.version) || !isNumber(snapshot.protocol) || !Number.isInteger(snapshot.protocol) || !Array.isArray(snapshot.workspaces) || !Array.isArray(snapshot.tabs) || !Array.isArray(snapshot.panes) || !Array.isArray(snapshot.layouts) || !Array.isArray(snapshot.agents))
|
|
2206
2713
|
return fail4(fail2("herdr_session_snapshot_invalid"));
|
|
@@ -2315,7 +2822,7 @@ function isRecord(value) {
|
|
|
2315
2822
|
// packages/agent-runtime/src/herdr-server-supervisor.ts
|
|
2316
2823
|
class HerdrServerError extends TaggedError("HerdrServerError") {
|
|
2317
2824
|
}
|
|
2318
|
-
var
|
|
2825
|
+
var fail5 = (message) => new HerdrServerError({ message });
|
|
2319
2826
|
|
|
2320
2827
|
class HerdrServerSupervisor {
|
|
2321
2828
|
runnerId;
|
|
@@ -2347,9 +2854,9 @@ class HerdrServerSupervisor {
|
|
|
2347
2854
|
}
|
|
2348
2855
|
start = fnUntraced(function* () {
|
|
2349
2856
|
if (this.started && this.client !== undefined)
|
|
2350
|
-
return yield* this.client.sessionSnapshot().pipe(mapError((error) =>
|
|
2857
|
+
return yield* this.client.sessionSnapshot().pipe(mapError((error) => fail5(error.message)));
|
|
2351
2858
|
if (!["linux", "darwin"].includes(this.options.platform ?? process.platform))
|
|
2352
|
-
return yield*
|
|
2859
|
+
return yield* fail5("herdr_platform_unsupported");
|
|
2353
2860
|
const defaultSession = this.options.session === "default";
|
|
2354
2861
|
const runtime = defaultSession ? undefined : yield* this.prepareRuntime();
|
|
2355
2862
|
const socketClientOptions = this.options.socketClientOptions ?? {};
|
|
@@ -2390,7 +2897,7 @@ class HerdrServerSupervisor {
|
|
|
2390
2897
|
}
|
|
2391
2898
|
const snapshot = yield* awaitReadiness(socketClient, this.readinessTimeoutMs);
|
|
2392
2899
|
if (snapshot.protocol !== HERDR_SOCKET_PROTOCOL)
|
|
2393
|
-
return yield*
|
|
2900
|
+
return yield* fail5("herdr_protocol_mismatch");
|
|
2394
2901
|
return snapshot;
|
|
2395
2902
|
});
|
|
2396
2903
|
attachToDefaultSession = fnUntraced(function* (socketPath, socketClient) {
|
|
@@ -2398,7 +2905,7 @@ class HerdrServerSupervisor {
|
|
|
2398
2905
|
if (isFailure(running)) {
|
|
2399
2906
|
const message = running.failure.message;
|
|
2400
2907
|
if (message !== "herdr_socket_connect_failed" && message !== "herdr_socket_closed")
|
|
2401
|
-
return yield*
|
|
2908
|
+
return yield* fail5(message);
|
|
2402
2909
|
yield* (this.options.spawnServer ?? spawnServer)({
|
|
2403
2910
|
command: this.options.herdrCommand ?? "herdr",
|
|
2404
2911
|
args: ["server"],
|
|
@@ -2407,7 +2914,7 @@ class HerdrServerSupervisor {
|
|
|
2407
2914
|
}
|
|
2408
2915
|
const snapshot = isSuccess(running) ? running.success : yield* awaitReadiness(socketClient, this.readinessTimeoutMs);
|
|
2409
2916
|
if (snapshot.protocol !== HERDR_SOCKET_PROTOCOL)
|
|
2410
|
-
return yield*
|
|
2917
|
+
return yield* fail5("herdr_protocol_mismatch");
|
|
2411
2918
|
return snapshot;
|
|
2412
2919
|
});
|
|
2413
2920
|
prepareRuntime = fnUntraced(function* () {
|
|
@@ -2419,7 +2926,7 @@ class HerdrServerSupervisor {
|
|
|
2419
2926
|
fs.makeDirectory(path.dirname(directory), { recursive: true, mode: 448 }),
|
|
2420
2927
|
fs.makeDirectory(directory, { recursive: true, mode: 448 }),
|
|
2421
2928
|
fs.chmod(directory, 448)
|
|
2422
|
-
]), () =>
|
|
2929
|
+
]), () => fail5("herdr_runtime_unavailable"));
|
|
2423
2930
|
const sessionName = `kitman-herdr-${digest(this.runnerId)}`;
|
|
2424
2931
|
const runtime = {
|
|
2425
2932
|
directory,
|
|
@@ -2453,7 +2960,7 @@ var reclaimStaleRuntime = (runtime, runnerId, isAlive) => gen(function* () {
|
|
|
2453
2960
|
if (!socketExists && owner === undefined)
|
|
2454
2961
|
return false;
|
|
2455
2962
|
if (owner === undefined || owner.runnerId !== runnerId || owner.socketPath !== runtime.socketPath)
|
|
2456
|
-
return yield*
|
|
2963
|
+
return yield* fail5(`herdr_socket_ownership_unknown:${socketExists ? "socket_present" : "socket_missing"}:${owner === undefined ? "owner_missing" : "owner_mismatch"}`);
|
|
2457
2964
|
if (yield* isAlive(owner.pid))
|
|
2458
2965
|
return true;
|
|
2459
2966
|
yield* removeRuntimeFiles(runtime);
|
|
@@ -2464,8 +2971,8 @@ var awaitReadiness = (client, readinessTimeoutMs) => gen(function* () {
|
|
|
2464
2971
|
for (;; ) {
|
|
2465
2972
|
const remaining = deadline - (yield* currentTimeMillis);
|
|
2466
2973
|
if (remaining <= 0)
|
|
2467
|
-
return yield*
|
|
2468
|
-
const attempt = yield* result(client.sessionSnapshot().pipe(mapError((error) =>
|
|
2974
|
+
return yield* fail5("herdr_socket_connect_failed");
|
|
2975
|
+
const attempt = yield* result(client.sessionSnapshot().pipe(mapError((error) => fail5(error.message)), timeoutOrElse({ duration: remaining, orElse: () => fail4(fail5("herdr_readiness_timeout")) })));
|
|
2469
2976
|
if (isSuccess(attempt))
|
|
2470
2977
|
return attempt.success;
|
|
2471
2978
|
const message = attempt.failure.message;
|
|
@@ -2493,15 +3000,15 @@ var homeDirectory = map3(optionalEnv("HOME"), (home) => home ?? "/");
|
|
|
2493
3000
|
var temporaryDirectory = map3(optionalEnv("TMPDIR"), (tmp) => tmp ?? "/tmp");
|
|
2494
3001
|
var spawnServer = (input) => gen(function* () {
|
|
2495
3002
|
const spawner = yield* ChildProcessSpawner;
|
|
2496
|
-
const scope = yield*
|
|
2497
|
-
const handle = yield* spawner.spawn(
|
|
3003
|
+
const scope = yield* make7();
|
|
3004
|
+
const handle = yield* spawner.spawn(make13(input.command, [...input.args], {
|
|
2498
3005
|
env: { HERDR_SOCKET_PATH: input.socketPath },
|
|
2499
3006
|
extendEnv: true,
|
|
2500
3007
|
detached: true,
|
|
2501
3008
|
stdin: "ignore",
|
|
2502
3009
|
stdout: "ignore",
|
|
2503
3010
|
stderr: "ignore"
|
|
2504
|
-
})).pipe(provide(scope), mapError(() =>
|
|
3011
|
+
})).pipe(provide(scope), mapError(() => fail5("herdr_server_start_failed")));
|
|
2505
3012
|
yield* ignore(handle.unref);
|
|
2506
3013
|
return {
|
|
2507
3014
|
pid: handle.pid,
|
|
@@ -2521,7 +3028,7 @@ var writeOwnerMarker = (path, marker) => gen(function* () {
|
|
|
2521
3028
|
yield* fs.writeFileString(path, `${encodeJsonStringSync(marker)}
|
|
2522
3029
|
`, { mode: 384 });
|
|
2523
3030
|
yield* fs.chmod(path, 384);
|
|
2524
|
-
}).pipe(mapError(() =>
|
|
3031
|
+
}).pipe(mapError(() => fail5("herdr_runtime_unavailable")));
|
|
2525
3032
|
var readOwnerMarker = (path) => gen(function* () {
|
|
2526
3033
|
const fs = yield* FileSystem;
|
|
2527
3034
|
const text = yield* fs.readFileString(path);
|
|
@@ -2570,7 +3077,7 @@ function shortId(value) {
|
|
|
2570
3077
|
function isRecord2(value) {
|
|
2571
3078
|
return isObject(value) && value !== null && !Array.isArray(value);
|
|
2572
3079
|
}
|
|
2573
|
-
var requestRecord = (client, method, params, expectedType) =>
|
|
3080
|
+
var requestRecord = (client, method, params, expectedType) => flatMap2(client.request(method, params), (response) => {
|
|
2574
3081
|
const result = recordValue(response);
|
|
2575
3082
|
return result === undefined || result.type !== expectedType ? fail4(new HerdrHarnessError({ message: `${method}_response_invalid` })) : succeed5(result);
|
|
2576
3083
|
});
|
|
@@ -2720,7 +3227,7 @@ var HarnessAdapters = [
|
|
|
2720
3227
|
auth: ["~/.codex"],
|
|
2721
3228
|
providerHosts: ["api.openai.com", "auth.openai.com", "chatgpt.com"],
|
|
2722
3229
|
effortArgs: ["-c", 'model_reasoning_effort="{effort}"'],
|
|
2723
|
-
autoPermissionArgs: ["--dangerously-bypass-approvals-and-sandbox"],
|
|
3230
|
+
autoPermissionArgs: ["--dangerously-bypass-approvals-and-sandbox", "--dangerously-bypass-hook-trust"],
|
|
2724
3231
|
firstMessage: "herdr_prompt",
|
|
2725
3232
|
mcp: { kind: "toml_overrides", flag: "-c" },
|
|
2726
3233
|
skills: { kind: "skills_config", root: "skills" },
|
|
@@ -3289,7 +3796,7 @@ var executeHerdrStepAttempt = (input) => gen(function* () {
|
|
|
3289
3796
|
});
|
|
3290
3797
|
yield* input.harness.prompt({ targetId: launched.targetId, text: firstMessage(input.agent, worktreePath) });
|
|
3291
3798
|
const steering = input.control.steering;
|
|
3292
|
-
const deliveries = steering === undefined ? undefined : yield* forkScoped(forever(
|
|
3799
|
+
const deliveries = steering === undefined ? undefined : yield* forkScoped(forever(flatMap2(steering.next, (note) => input.harness.prompt({ targetId: launched.targetId, text: note.text }).pipe(matchEffect({
|
|
3293
3800
|
onSuccess: () => steering.report(note, true),
|
|
3294
3801
|
onFailure: () => steering.report(note, false)
|
|
3295
3802
|
})))));
|
|
@@ -3700,7 +4207,7 @@ var findSession = (adapter, place) => gen(function* () {
|
|
|
3700
4207
|
}).pipe(orElseSucceed(() => {
|
|
3701
4208
|
return;
|
|
3702
4209
|
}));
|
|
3703
|
-
var packSession = (path) => map3(
|
|
4210
|
+
var packSession = (path) => map3(flatMap2(FileSystem, (fs) => fs.readFile(path)), (content) => new Uint8Array(gzipSync(content)));
|
|
3704
4211
|
var restoreSession = (adapter, place) => gen(function* () {
|
|
3705
4212
|
const fs = yield* FileSystem;
|
|
3706
4213
|
const location = adapter.session?.location;
|
|
@@ -3971,8 +4478,8 @@ function runReplica(control, executor, signals) {
|
|
|
3971
4478
|
if (work === undefined)
|
|
3972
4479
|
return { status: "idle" };
|
|
3973
4480
|
const lease = leaseInput(work);
|
|
3974
|
-
const workHeartbeat = sleep(heartbeatInterval(work)).pipe(
|
|
3975
|
-
const pushedStop = signals === undefined ? never : signals.stopped(work.id).pipe(
|
|
4481
|
+
const workHeartbeat = sleep(heartbeatInterval(work)).pipe(flatMap2(() => call("heartbeatWork", () => control.heartbeatWork(work.id, lease))), repeat({ until: (heartbeat) => heartbeat?.settled === true }), as({ status: "settled" }));
|
|
4482
|
+
const pushedStop = signals === undefined ? never : signals.stopped(work.id).pipe(flatMap2((stop) => stop === "canceled" ? fail4(new RunnerReplicaCancellation) : succeed5({ status: "settled" })), ensuring(signals.forget(work.id)));
|
|
3976
4483
|
let completed = false;
|
|
3977
4484
|
let leaseLost = false;
|
|
3978
4485
|
return yield* gen(function* () {
|
|
@@ -4356,7 +4863,101 @@ function runnerStepAttemptFromAgentStep(work) {
|
|
|
4356
4863
|
}
|
|
4357
4864
|
};
|
|
4358
4865
|
}
|
|
4866
|
+
// packages/agent-runtime/src/codex-hook-trust.ts
|
|
4867
|
+
class CodexAppServerFailed extends TaggedError("CodexAppServerFailed") {
|
|
4868
|
+
get message() {
|
|
4869
|
+
return this.reason;
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
var RpcResponse = Struct({
|
|
4873
|
+
id: Finite,
|
|
4874
|
+
result: optional(Unknown),
|
|
4875
|
+
error: optional(Struct({ message: String2 }))
|
|
4876
|
+
});
|
|
4877
|
+
var HooksList = Struct({
|
|
4878
|
+
data: ArraySchema(Struct({
|
|
4879
|
+
hooks: ArraySchema(Struct({
|
|
4880
|
+
key: String2,
|
|
4881
|
+
pluginId: optional(NullOr(String2)),
|
|
4882
|
+
currentHash: String2,
|
|
4883
|
+
trustStatus: String2
|
|
4884
|
+
}))
|
|
4885
|
+
}))
|
|
4886
|
+
});
|
|
4887
|
+
var codexAppServer = (binary = "codex") => (env) => gen(function* () {
|
|
4888
|
+
const spawner = yield* ChildProcessSpawner;
|
|
4889
|
+
const input = yield* unbounded();
|
|
4890
|
+
const pending = new Map;
|
|
4891
|
+
let lastId = 0;
|
|
4892
|
+
const handle = yield* spawner.spawn(make13(binary, ["app-server"], {
|
|
4893
|
+
cwd: env.home,
|
|
4894
|
+
extendEnv: true,
|
|
4895
|
+
env: { HOME: env.home, CODEX_HOME: env.codexHome },
|
|
4896
|
+
stdin: fromQueue(input),
|
|
4897
|
+
stderr: "ignore"
|
|
4898
|
+
})).pipe(catchReason("PlatformError", "NotFound", () => fail4(new CodexAppServerFailed({ reason: `${binary} is not on the PATH` }))), mapError((error) => new CodexAppServerFailed({ reason: error.message })));
|
|
4899
|
+
const settle = (line) => match(flatMap(decodeJsonStringOption(line), decodeUnknownOption(RpcResponse)), {
|
|
4900
|
+
onNone: () => void_2,
|
|
4901
|
+
onSome: (response) => {
|
|
4902
|
+
const deferred = pending.get(response.id);
|
|
4903
|
+
if (deferred === undefined)
|
|
4904
|
+
return void_2;
|
|
4905
|
+
pending.delete(response.id);
|
|
4906
|
+
return response.error === undefined ? succeed3(deferred, response.result) : fail3(deferred, new CodexAppServerFailed({ reason: response.error.message }));
|
|
4907
|
+
}
|
|
4908
|
+
});
|
|
4909
|
+
yield* handle.stdout.pipe(decodeText, splitLines, runForEach(settle), ignore, ensuring(forEach(pending.values(), (deferred) => fail3(deferred, new CodexAppServerFailed({ reason: `${binary} app-server exited` })))), forkScoped);
|
|
4910
|
+
const send = (message) => offer(input, new TextEncoder().encode(`${encodeJsonStringSync(message)}
|
|
4911
|
+
`));
|
|
4912
|
+
return {
|
|
4913
|
+
request: (method, params) => gen(function* () {
|
|
4914
|
+
const id = ++lastId;
|
|
4915
|
+
const deferred = yield* make6();
|
|
4916
|
+
pending.set(id, deferred);
|
|
4917
|
+
yield* send({ id, method, params });
|
|
4918
|
+
return yield* _await(deferred).pipe(mapError((error) => new CodexAppServerFailed({ reason: `${method}: ${error.message}` })));
|
|
4919
|
+
}),
|
|
4920
|
+
notify: (method) => asVoid(send({ method }))
|
|
4921
|
+
};
|
|
4922
|
+
});
|
|
4923
|
+
var CodexAppServer = Reference("kitman/agent-runtime/CodexAppServer", {
|
|
4924
|
+
defaultValue: () => codexAppServer()
|
|
4925
|
+
});
|
|
4926
|
+
var trustCodexPluginHooks = (input) => gen(function* () {
|
|
4927
|
+
const launch = yield* CodexAppServer;
|
|
4928
|
+
const codex = yield* launch({ home: input.home, codexHome: `${input.home}/.codex` });
|
|
4929
|
+
const pluginIds = new Set(input.plugins.map((plugin) => `${plugin}@kitman`));
|
|
4930
|
+
yield* codex.request("initialize", { clientInfo: { name: "kitman", title: "Kitman", version: "1" } });
|
|
4931
|
+
yield* codex.notify("initialized");
|
|
4932
|
+
for (const pluginName of input.plugins)
|
|
4933
|
+
yield* codex.request("plugin/install", { marketplacePath: input.marketplacePath, pluginName });
|
|
4934
|
+
const listed = yield* decodeUnknownEffect(HooksList)(yield* codex.request("hooks/list", { cwds: [input.home] })).pipe(mapError(() => new CodexAppServerFailed({ reason: "hooks/list: unexpected response" })));
|
|
4935
|
+
const untrusted = listed.data.flatMap((entry) => entry.hooks).filter((hook) => pluginIds.has(hook.pluginId ?? "") && (hook.trustStatus === "untrusted" || hook.trustStatus === "modified"));
|
|
4936
|
+
if (untrusted.length > 0)
|
|
4937
|
+
yield* codex.request("config/batchWrite", {
|
|
4938
|
+
edits: [
|
|
4939
|
+
{
|
|
4940
|
+
keyPath: "hooks.state",
|
|
4941
|
+
mergeStrategy: "upsert",
|
|
4942
|
+
value: Object.fromEntries(untrusted.map((hook) => [hook.key, { trusted_hash: hook.currentHash }]))
|
|
4943
|
+
}
|
|
4944
|
+
],
|
|
4945
|
+
reloadUserConfig: true
|
|
4946
|
+
});
|
|
4947
|
+
return [];
|
|
4948
|
+
}).pipe(scoped, timeoutOrElse({
|
|
4949
|
+
duration: "1 minute",
|
|
4950
|
+
orElse: () => fail4(new CodexAppServerFailed({ reason: "timed out" }))
|
|
4951
|
+
}), catch_((error) => succeed5(input.plugins.map((plugin) => `Kitman could not record trust for ${plugin}'s hooks (${error.message}); Codex will ask in /hooks.`))));
|
|
4952
|
+
|
|
4359
4953
|
// packages/agent-runtime/src/materialization-plan.ts
|
|
4954
|
+
var HarnessUpdaters = {
|
|
4955
|
+
"claude-code": ["claude", "update"],
|
|
4956
|
+
codex: ["codex", "update"],
|
|
4957
|
+
opencode: ["opencode", "upgrade"],
|
|
4958
|
+
pi: ["pi", "update", "--self"]
|
|
4959
|
+
};
|
|
4960
|
+
|
|
4360
4961
|
class AgentEnvironmentPlanApplyError extends TaggedError("AgentEnvironmentPlanApplyError") {
|
|
4361
4962
|
get message() {
|
|
4362
4963
|
return `AgentEnvironment plan apply failed during ${this.phase}: ${this.cause.message}`;
|
|
@@ -4368,11 +4969,18 @@ var previewAgentEnvironmentPlan = fnUntraced(function* (input) {
|
|
|
4368
4969
|
...input.rootDir === undefined ? undefined : { rootDir: input.rootDir },
|
|
4369
4970
|
...input.scope === undefined ? undefined : { scope: input.scope },
|
|
4370
4971
|
...input.cacheRoot === undefined ? undefined : { cacheRoot: input.cacheRoot },
|
|
4371
|
-
...input.noInstall === undefined ? undefined : { noInstall: input.noInstall }
|
|
4972
|
+
...input.noInstall === undefined ? undefined : { noInstall: input.noInstall },
|
|
4973
|
+
...input.approvedSkillSources === undefined ? undefined : { approvedSkillSources: input.approvedSkillSources },
|
|
4974
|
+
...input.requireReviewedSources === undefined ? undefined : { requireReviewedSources: input.requireReviewedSources },
|
|
4975
|
+
...input.versions === undefined ? undefined : { versions: input.versions }
|
|
4372
4976
|
});
|
|
4373
4977
|
const harnessConfig = yield* previewAgentEnvironmentConfigSync({
|
|
4374
4978
|
environment: input.environment,
|
|
4375
4979
|
materializedSkills: skills.materializedSkills,
|
|
4980
|
+
materializedPlugins: skills.materializedPlugins,
|
|
4981
|
+
skippedPlugins: skills.skippedPlugins,
|
|
4982
|
+
...input.noInstall === undefined ? undefined : { noInstall: input.noInstall },
|
|
4983
|
+
...input.versions === undefined ? undefined : { versions: input.versions },
|
|
4376
4984
|
...input.syncKit === undefined ? undefined : { syncKit: input.syncKit },
|
|
4377
4985
|
...input.rootDir === undefined ? undefined : { rootDir: input.rootDir },
|
|
4378
4986
|
...input.scope === undefined ? undefined : { scope: input.scope },
|
|
@@ -4383,19 +4991,50 @@ var previewAgentEnvironmentPlan = fnUntraced(function* (input) {
|
|
|
4383
4991
|
...input.syncKit === undefined ? undefined : { syncKitHash: input.syncKit.syncKitHash },
|
|
4384
4992
|
skills,
|
|
4385
4993
|
harnessConfig,
|
|
4994
|
+
...input.versions === "latest" && input.noInstall !== true ? { harnessUpdater: HarnessUpdaters[input.environment.harness] } : undefined,
|
|
4386
4995
|
operationCount: skills.operations.length + harnessConfig.operations.length,
|
|
4387
4996
|
warnings: [...skills.warnings, ...harnessConfig.warnings].map((item) => item.message)
|
|
4388
4997
|
};
|
|
4389
4998
|
});
|
|
4390
4999
|
function composeAgentEnvironmentSyncPlan(targets) {
|
|
4391
|
-
|
|
5000
|
+
const unlinks = targets.flatMap((target) => target.skills.operations.flatMap((operation) => operation.kind === "unlink_stale_links" ? [operation] : []));
|
|
5001
|
+
const composed = targets.map((target) => ({
|
|
5002
|
+
...target,
|
|
5003
|
+
skills: {
|
|
5004
|
+
...target.skills,
|
|
5005
|
+
operations: target.skills.operations.map((operation) => operation.kind === "unlink_stale_links" ? {
|
|
5006
|
+
...operation,
|
|
5007
|
+
keepPaths: unlinks.flatMap((unlink) => unlink.path === operation.path ? unlink.keepPaths : []),
|
|
5008
|
+
sharedWith: operation.sharedWith.filter((shared) => !targets.some((other) => other.harness === shared.harness))
|
|
5009
|
+
} : operation)
|
|
5010
|
+
}
|
|
5011
|
+
}));
|
|
5012
|
+
return { version: 1, planId: canonicalSha256(composed), targets: composed };
|
|
4392
5013
|
}
|
|
5014
|
+
var updateHarness = (argv) => gen(function* () {
|
|
5015
|
+
const run = yield* CommandRunner2;
|
|
5016
|
+
const failure = yield* run(argv).pipe(map3((result) => result.exitCode === 0 ? undefined : result.output.trim().split(`
|
|
5017
|
+
`).at(-1) ?? `exit code ${result.exitCode}`), catch_((error) => succeed5(error.message)));
|
|
5018
|
+
return failure === undefined ? [] : [`Kitman could not update ${argv[0]} with \`${argv.join(" ")}\`: ${failure}`];
|
|
5019
|
+
});
|
|
4393
5020
|
var applyAgentEnvironmentPlans = fnUntraced(function* (plan) {
|
|
4394
|
-
|
|
4395
|
-
|
|
5021
|
+
yield* gen(function* () {
|
|
5022
|
+
const checkout = yield* gitCheckouts;
|
|
5023
|
+
for (const target of plan.targets)
|
|
5024
|
+
yield* applyAgentEnvironmentMaterialization(target.skills, checkout).pipe(mapError((cause) => new AgentEnvironmentPlanApplyError({ phase: "skills", cause })));
|
|
5025
|
+
}).pipe(scoped);
|
|
5026
|
+
const updateWarnings = yield* forEach(plan.targets, (target) => target.harnessUpdater === undefined ? succeed5([]) : updateHarness(target.harnessUpdater));
|
|
4396
5027
|
for (const target of plan.targets)
|
|
4397
5028
|
yield* applyAgentEnvironmentConfigSync(target.harnessConfig).pipe(mapError((cause) => new AgentEnvironmentPlanApplyError({ phase: "harness_config", cause })));
|
|
4398
|
-
|
|
5029
|
+
const targets = yield* forEach(plan.targets, (target, index) => map3(target.skills.codexHookTrust === undefined ? succeed5([]) : trustCodexPluginHooks({
|
|
5030
|
+
...target.skills.codexHookTrust,
|
|
5031
|
+
plugins: target.skills.materializedPlugins.map((plugin) => plugin.name)
|
|
5032
|
+
}), (trustWarnings) => [...updateWarnings[index] ?? [], ...trustWarnings]).pipe(map3((warnings) => ({
|
|
5033
|
+
...target,
|
|
5034
|
+
skills: { ...target.skills, warnings: [...target.skills.warnings, ...warnings.map((message) => ({ message }))] },
|
|
5035
|
+
warnings: [...target.warnings, ...warnings]
|
|
5036
|
+
}))));
|
|
5037
|
+
return { ...plan, targets };
|
|
4399
5038
|
});
|
|
4400
5039
|
// packages/agent-runtime/src/herdr-executor-surface.ts
|
|
4401
5040
|
class HerdrExecutorSurfaceError extends TaggedError("HerdrExecutorSurfaceError") {
|
|
@@ -4431,7 +5070,7 @@ function herdrExecutorSurface(input) {
|
|
|
4431
5070
|
executionClient: {
|
|
4432
5071
|
request: (method, params) => supervisor.executionClient.request(method, params).pipe(tap((response) => record(method, response)))
|
|
4433
5072
|
},
|
|
4434
|
-
start: () => supervisor.start().pipe(mapError((error) => new HerdrExecutorSurfaceError({ reason: "start_failed", message: error.message })),
|
|
5073
|
+
start: () => supervisor.start().pipe(mapError((error) => new HerdrExecutorSurfaceError({ reason: "start_failed", message: error.message })), flatMap2((readiness) => readiness.protocol === HERDR_SOCKET_PROTOCOL ? void_2 : fail4(new HerdrExecutorSurfaceError({ reason: "start_failed", message: "herdr_protocol_mismatch" })))),
|
|
4435
5074
|
stop: () => andThen(prune, supervisor.stop())
|
|
4436
5075
|
};
|
|
4437
5076
|
}
|
|
@@ -4451,10 +5090,10 @@ var handshake = [
|
|
|
4451
5090
|
`).join("");
|
|
4452
5091
|
var kitmanMcpPreflight = (mcp) => gen(function* () {
|
|
4453
5092
|
const spawner = yield* ChildProcessSpawner;
|
|
4454
|
-
const server = yield* spawner.spawn(
|
|
5093
|
+
const server = yield* spawner.spawn(make13(mcp.command, [...mcp.args], {
|
|
4455
5094
|
extendEnv: true,
|
|
4456
5095
|
env: mcp.env,
|
|
4457
|
-
stdin: { stream:
|
|
5096
|
+
stdin: { stream: make11(new TextEncoder().encode(handshake)), endOnDone: false },
|
|
4458
5097
|
stderr: "ignore"
|
|
4459
5098
|
}));
|
|
4460
5099
|
const reply = yield* server.stdout.pipe(decodeText, splitLines, map4((line) => recordValue(getOrUndefined(decodeJsonStringOption(line)))), filter((message) => message?.id === 2), runHead);
|
|
@@ -4526,8 +5165,8 @@ var programProcessExecutor = (input) => (work) => gen(function* () {
|
|
|
4526
5165
|
};
|
|
4527
5166
|
const exitCode = sandbox === undefined ? yield* gen(function* () {
|
|
4528
5167
|
const spawner = yield* ChildProcessSpawner;
|
|
4529
|
-
const handle = yield* spawner.spawn(
|
|
4530
|
-
stdin:
|
|
5168
|
+
const handle = yield* spawner.spawn(make13(input.bun ?? process.execPath, [file], {
|
|
5169
|
+
stdin: make11(new TextEncoder().encode(stdin)),
|
|
4531
5170
|
stdout: "inherit",
|
|
4532
5171
|
stderr: "inherit",
|
|
4533
5172
|
extendEnv: false,
|
|
@@ -4679,6 +5318,7 @@ export {
|
|
|
4679
5318
|
AgentRuntimePackageName,
|
|
4680
5319
|
BundleRefused,
|
|
4681
5320
|
CommandFailed2 as CommandFailed,
|
|
5321
|
+
CommandRunner2 as CommandRunner,
|
|
4682
5322
|
ContainerError,
|
|
4683
5323
|
EgressProxyImage,
|
|
4684
5324
|
HERDR_SOCKET_PROTOCOL,
|
|
@@ -4715,6 +5355,7 @@ export {
|
|
|
4715
5355
|
dockerContainerRunner,
|
|
4716
5356
|
dockerMissing,
|
|
4717
5357
|
executeHerdrStepAttempt,
|
|
5358
|
+
gitCheckouts,
|
|
4718
5359
|
harnessAdapter,
|
|
4719
5360
|
herdrAgentBackendLayer,
|
|
4720
5361
|
herdrExecutionExtension2 as herdrExecutionExtension,
|
|
@@ -4745,6 +5386,7 @@ export {
|
|
|
4745
5386
|
previewAgentEnvironmentPlan,
|
|
4746
5387
|
probeSandboxHost,
|
|
4747
5388
|
programProcessExecutor,
|
|
5389
|
+
readManifestSkillPaths,
|
|
4748
5390
|
recordList,
|
|
4749
5391
|
recordValue,
|
|
4750
5392
|
refuseAgentRequest,
|
|
@@ -4780,6 +5422,7 @@ export {
|
|
|
4780
5422
|
stringValue,
|
|
4781
5423
|
superviseReplica,
|
|
4782
5424
|
verificationExecutor,
|
|
5425
|
+
withSourceResolutionCache,
|
|
4783
5426
|
writeAgentEnvironmentConfigFile,
|
|
4784
5427
|
writeSrtSettings
|
|
4785
5428
|
};
|