@dot-agent/compiler 0.1.0 → 0.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-IT25ILTQ.js → chunk-JIYQMCQQ.js} +30 -4
- package/dist/chunk-JIYQMCQQ.js.map +1 -0
- package/dist/{core-DLZGpzev.d.cts → core-b-7c6X6q.d.cts} +29 -70
- package/dist/{core-DLZGpzev.d.ts → core-b-7c6X6q.d.ts} +29 -70
- package/dist/core.cjs +31 -5
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -1
- package/dist/core.d.ts +2 -1
- package/dist/core.js +7 -3
- package/dist/index.cjs +286 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +263 -65
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/dist/chunk-IT25ILTQ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -33,7 +33,9 @@ __export(index_exports, {
|
|
|
33
33
|
aboutmeToJson: () => aboutmeToJson,
|
|
34
34
|
buildAboutme: () => buildAboutme,
|
|
35
35
|
buildId: () => buildId,
|
|
36
|
+
bundleFromDir: () => bundleFromDir,
|
|
36
37
|
collectFiles: () => collectFiles,
|
|
38
|
+
consolidate: () => consolidate,
|
|
37
39
|
createLinter: () => createLinter,
|
|
38
40
|
createZip: () => createZip,
|
|
39
41
|
extractDigest: () => extractDigest,
|
|
@@ -57,8 +59,8 @@ __export(index_exports, {
|
|
|
57
59
|
parseSync: () => parseSync,
|
|
58
60
|
positionToOffset: () => positionToOffset,
|
|
59
61
|
readZip: () => readZip,
|
|
60
|
-
validateMagicBytes: () =>
|
|
61
|
-
validateZipBomb: () =>
|
|
62
|
+
validateMagicBytes: () => validateMagicBytes2,
|
|
63
|
+
validateZipBomb: () => validateZipBomb2,
|
|
62
64
|
writeZip: () => writeZip
|
|
63
65
|
});
|
|
64
66
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -70,7 +72,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
70
72
|
// src/parser.ts
|
|
71
73
|
var import_module = require("module");
|
|
72
74
|
var import_web_tree_sitter = require("web-tree-sitter");
|
|
73
|
-
var import_parser_dsl =
|
|
75
|
+
var import_parser_dsl = require("@dot-agent/parser-dsl");
|
|
74
76
|
var require2 = (0, import_module.createRequire)(importMetaUrl);
|
|
75
77
|
var { descriptionWasmPath, behaviorWasmPath } = require2("@dot-agent/tree-sitter");
|
|
76
78
|
var _initialized = false;
|
|
@@ -79,7 +81,7 @@ var _behaviorParser;
|
|
|
79
81
|
var _bpInitialized = false;
|
|
80
82
|
async function initBehaviorParser() {
|
|
81
83
|
if (_bpInitialized) return;
|
|
82
|
-
await (0, import_parser_dsl.
|
|
84
|
+
await (0, import_parser_dsl.init)();
|
|
83
85
|
_bpInitialized = true;
|
|
84
86
|
}
|
|
85
87
|
function parseBehaviorFile(text) {
|
|
@@ -234,6 +236,18 @@ async function lintDescription(text, file = "agent.description") {
|
|
|
234
236
|
message: "No domain declared \u2014 agent will be packaged as unknown/name. Interoperability between runtimes is not guaranteed."
|
|
235
237
|
});
|
|
236
238
|
}
|
|
239
|
+
const behaviorBlocks = nodesOfType(tree, "behavior_block");
|
|
240
|
+
if (behaviorBlocks.length > 1) {
|
|
241
|
+
const { line, col } = nodePosition(behaviorBlocks[1]);
|
|
242
|
+
messages.push({
|
|
243
|
+
file,
|
|
244
|
+
line,
|
|
245
|
+
col,
|
|
246
|
+
severity: "error",
|
|
247
|
+
code: "E017",
|
|
248
|
+
message: "Multiple 'behavior' declarations \u2014 only one is allowed. To combine multiple files, use 'merge' in your .behavior file."
|
|
249
|
+
});
|
|
250
|
+
}
|
|
237
251
|
const declaredTypes = new Set(
|
|
238
252
|
nodesOfType(tree, "type_decl").map((n) => n.childForFieldName("name")?.text).filter(Boolean)
|
|
239
253
|
);
|
|
@@ -332,7 +346,7 @@ function collectMergedStates(tree, docDir, states, visited = /* @__PURE__ */ new
|
|
|
332
346
|
collectMergedStates(mergedTree, (0, import_path.dirname)(absPath), states, visited);
|
|
333
347
|
}
|
|
334
348
|
}
|
|
335
|
-
async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
349
|
+
async function lintBehavior(text, file = "agent.behavior", docPath, consolidated = false, externalStates, mergedText) {
|
|
336
350
|
await initBehaviorParser();
|
|
337
351
|
if (!text.endsWith("\n")) text = text + "\n";
|
|
338
352
|
const tree = await parse("behavior", text);
|
|
@@ -353,7 +367,7 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
353
367
|
col,
|
|
354
368
|
severity: "warning",
|
|
355
369
|
code: "W013",
|
|
356
|
-
message: `'interact' without 'goal'
|
|
370
|
+
message: `'interact' without 'goal'. Add 'goal "..."' before 'interact'.`
|
|
357
371
|
});
|
|
358
372
|
}
|
|
359
373
|
if (hasGoal && !hasInteract) {
|
|
@@ -364,7 +378,7 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
364
378
|
col,
|
|
365
379
|
severity: "warning",
|
|
366
380
|
code: "W012",
|
|
367
|
-
message: `'goal' is only valid in an oriented state. Add 'interact' or remove 'goal'
|
|
381
|
+
message: `'goal' is only valid in an oriented state. Add 'interact' or remove 'goal'.`
|
|
368
382
|
});
|
|
369
383
|
}
|
|
370
384
|
if (hasInteract && intentHandlers.length === 0) {
|
|
@@ -495,7 +509,7 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
495
509
|
col,
|
|
496
510
|
severity: "warning",
|
|
497
511
|
code: "W002",
|
|
498
|
-
message: `Text block exceeds 280 characters (${content.length}). Consider using '
|
|
512
|
+
message: `Text block exceeds 280 characters (${content.length}). Consider using 'guide' to load long orientation text from an external text file.`
|
|
499
513
|
});
|
|
500
514
|
}
|
|
501
515
|
}
|
|
@@ -514,7 +528,7 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
514
528
|
col,
|
|
515
529
|
severity: "warning",
|
|
516
530
|
code: "W010",
|
|
517
|
-
message: `'guide' text in state '${stateName}' is ${content.length} characters
|
|
531
|
+
message: `'guide' text in state '${stateName}' is ${content.length} characters. Consider using an external file.`
|
|
518
532
|
});
|
|
519
533
|
}
|
|
520
534
|
}
|
|
@@ -527,6 +541,9 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
527
541
|
} catch {
|
|
528
542
|
}
|
|
529
543
|
}
|
|
544
|
+
if (externalStates) {
|
|
545
|
+
for (const name of externalStates) definedStates.add(name);
|
|
546
|
+
}
|
|
530
547
|
const stateNameList = Array.from(definedStates);
|
|
531
548
|
for (const transitionNode of nodesOfType(tree, "transition_stmt")) {
|
|
532
549
|
const stateNode = transitionNode.childForFieldName("state");
|
|
@@ -578,7 +595,9 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
578
595
|
return messages;
|
|
579
596
|
}
|
|
580
597
|
const fsm = fsmResult.ok;
|
|
581
|
-
const
|
|
598
|
+
const wholeFsm = mergedText !== void 0 ? parseBehaviorFile(mergedText).ok : null;
|
|
599
|
+
const reachabilityStates = wholeFsm?.states ?? fsm.states;
|
|
600
|
+
const allTargets = new Set(collectTransitionTargets(reachabilityStates.flatMap((s) => s.body)));
|
|
582
601
|
for (const state of fsm.states) {
|
|
583
602
|
const hasOutgoing = collectTransitionTargets(state.body).length > 0;
|
|
584
603
|
const hasIncoming = allTargets.has(state.name);
|
|
@@ -608,15 +627,62 @@ async function lintBehavior(text, file = "agent.behavior", docPath) {
|
|
|
608
627
|
});
|
|
609
628
|
}
|
|
610
629
|
}
|
|
630
|
+
if (consolidated) {
|
|
631
|
+
const wholeTreeFsm = wholeFsm ?? fsm;
|
|
632
|
+
const seenStates = /* @__PURE__ */ new Set();
|
|
633
|
+
for (const state of wholeTreeFsm.states) {
|
|
634
|
+
if (seenStates.has(state.name)) {
|
|
635
|
+
messages.push({
|
|
636
|
+
file,
|
|
637
|
+
line: 1,
|
|
638
|
+
col: 1,
|
|
639
|
+
severity: "error",
|
|
640
|
+
code: "E015",
|
|
641
|
+
message: `Duplicate state '${state.name}' across merged files \u2014 each state name must be unique in the consolidated FSM.`
|
|
642
|
+
});
|
|
643
|
+
} else {
|
|
644
|
+
seenStates.add(state.name);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (!wholeTreeFsm.states.some((s) => s.name === "init")) {
|
|
648
|
+
messages.push({
|
|
649
|
+
file,
|
|
650
|
+
line: 1,
|
|
651
|
+
col: 1,
|
|
652
|
+
severity: "error",
|
|
653
|
+
code: "E016",
|
|
654
|
+
message: `Required 'init' state is missing. Add a state named 'init' as the entry point of your behavior.`
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
const seenTriggers = /* @__PURE__ */ new Set();
|
|
658
|
+
for (const trigger of wholeTreeFsm.global_triggers) {
|
|
659
|
+
if (seenTriggers.has(trigger.event)) {
|
|
660
|
+
messages.push({
|
|
661
|
+
file,
|
|
662
|
+
line: 1,
|
|
663
|
+
col: 1,
|
|
664
|
+
severity: "warning",
|
|
665
|
+
code: "W014",
|
|
666
|
+
message: `Duplicate global trigger '${trigger.event}' across merged files \u2014 only the last definition takes effect.`
|
|
667
|
+
});
|
|
668
|
+
} else {
|
|
669
|
+
seenTriggers.add(trigger.event);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
611
673
|
return messages;
|
|
612
674
|
}
|
|
613
675
|
async function createLinter() {
|
|
614
676
|
return { lintDescription, lintBehavior };
|
|
615
677
|
}
|
|
616
678
|
|
|
679
|
+
// src/generated-version.ts
|
|
680
|
+
var DSL_VERSION = "0.1-alpha";
|
|
681
|
+
var COMPILER_VERSION = "0.5.0-alpha.1";
|
|
682
|
+
|
|
617
683
|
// src/manifest.ts
|
|
618
684
|
function parseAboutme(json) {
|
|
619
|
-
if (!json.
|
|
685
|
+
if (!json.dslVersion) throw new Error("Missing dslVersion in aboutme.json");
|
|
620
686
|
if (!json.id) throw new Error("Missing id in aboutme.json");
|
|
621
687
|
if (!json.name) throw new Error("Missing name in aboutme.json");
|
|
622
688
|
if (!json.description) throw new Error("Missing description in aboutme.json");
|
|
@@ -628,7 +694,7 @@ function parseAboutme(json) {
|
|
|
628
694
|
if (!Array.isArray(json.requires)) throw new Error("Missing requires array in aboutme.json");
|
|
629
695
|
if (!json.integrity) throw new Error("Missing integrity in aboutme.json");
|
|
630
696
|
return {
|
|
631
|
-
|
|
697
|
+
dslVersion: json.dslVersion,
|
|
632
698
|
id: json.id,
|
|
633
699
|
name: json.name,
|
|
634
700
|
description: json.description,
|
|
@@ -646,7 +712,7 @@ function parseAboutme(json) {
|
|
|
646
712
|
}
|
|
647
713
|
function buildAboutme(opts) {
|
|
648
714
|
return {
|
|
649
|
-
|
|
715
|
+
dslVersion: `dot-agent/${DSL_VERSION}`,
|
|
650
716
|
id: opts.id,
|
|
651
717
|
name: opts.name,
|
|
652
718
|
description: opts.description,
|
|
@@ -767,6 +833,25 @@ var import_promises = require("fs/promises");
|
|
|
767
833
|
|
|
768
834
|
// src/zip-core.ts
|
|
769
835
|
var import_jszip = __toESM(require("jszip"), 1);
|
|
836
|
+
var MAX_ZIP_SIZE = 500 * 1024 * 1024;
|
|
837
|
+
var MAX_COMPRESSION_RATIO = 100;
|
|
838
|
+
function validateMagicBytes(bytes) {
|
|
839
|
+
if (!(bytes[0] === 80 && bytes[1] === 75 && bytes[2] === 3 && bytes[3] === 4)) {
|
|
840
|
+
throw new Error("Not a valid .agent file (invalid magic bytes)");
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
function validateZipBomb(zip, compressedSize) {
|
|
844
|
+
let totalUncompressed = 0;
|
|
845
|
+
zip.forEach((_path, file) => {
|
|
846
|
+
if (!file.dir) totalUncompressed += file._data?.uncompressedSize || 0;
|
|
847
|
+
});
|
|
848
|
+
if (totalUncompressed > MAX_ZIP_SIZE) {
|
|
849
|
+
throw new Error(`Bundle exceeds 500MB uncompressed: ${totalUncompressed}`);
|
|
850
|
+
}
|
|
851
|
+
if (totalUncompressed / compressedSize > MAX_COMPRESSION_RATIO) {
|
|
852
|
+
throw new Error(`Compression ratio exceeds 100x: ${(totalUncompressed / compressedSize).toFixed(1)}x`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
770
855
|
function createZip() {
|
|
771
856
|
return new import_jszip.default();
|
|
772
857
|
}
|
|
@@ -787,37 +872,19 @@ async function extractFiles(zip, filter) {
|
|
|
787
872
|
}
|
|
788
873
|
|
|
789
874
|
// src/zip.ts
|
|
790
|
-
var MAX_ZIP_SIZE = 500 * 1024 * 1024;
|
|
791
|
-
var MAX_COMPRESSION_RATIO = 100;
|
|
792
875
|
async function readZip(filePath) {
|
|
793
876
|
const data = await (0, import_promises.readFile)(filePath);
|
|
794
877
|
return import_jszip2.default.loadAsync(data);
|
|
795
878
|
}
|
|
796
|
-
async function
|
|
879
|
+
async function validateZipBomb2(filePath) {
|
|
797
880
|
const data = await (0, import_promises.readFile)(filePath);
|
|
798
881
|
const zip = await import_jszip2.default.loadAsync(data);
|
|
799
|
-
|
|
800
|
-
zip.forEach((_relativePath, file) => {
|
|
801
|
-
if (!file.dir) {
|
|
802
|
-
totalUncompressed += file._data?.uncompressedSize || 0;
|
|
803
|
-
}
|
|
804
|
-
});
|
|
805
|
-
const compressedSize = data.length;
|
|
806
|
-
const ratio = totalUncompressed / compressedSize;
|
|
807
|
-
if (totalUncompressed > MAX_ZIP_SIZE) {
|
|
808
|
-
throw new Error(`ZIP uncompressed size exceeds 500MB limit: ${totalUncompressed}`);
|
|
809
|
-
}
|
|
810
|
-
if (ratio > MAX_COMPRESSION_RATIO) {
|
|
811
|
-
throw new Error(`ZIP compression ratio exceeds 100x limit: ${ratio.toFixed(1)}x`);
|
|
812
|
-
}
|
|
882
|
+
validateZipBomb(zip, data.length);
|
|
813
883
|
return true;
|
|
814
884
|
}
|
|
815
|
-
async function
|
|
885
|
+
async function validateMagicBytes2(filePath) {
|
|
816
886
|
const data = await (0, import_promises.readFile)(filePath);
|
|
817
|
-
|
|
818
|
-
if (!(magic[0] === 80 && magic[1] === 75 && magic[2] === 3 && magic[3] === 4)) {
|
|
819
|
-
throw new Error("File is not a valid ZIP (invalid magic bytes)");
|
|
820
|
-
}
|
|
887
|
+
validateMagicBytes(data);
|
|
821
888
|
return true;
|
|
822
889
|
}
|
|
823
890
|
async function writeZip(zip, outPath) {
|
|
@@ -909,7 +976,81 @@ async function resolveCommit(explicit) {
|
|
|
909
976
|
if (explicit) return explicit;
|
|
910
977
|
return gitRevParseShort() ?? void 0;
|
|
911
978
|
}
|
|
912
|
-
|
|
979
|
+
function isExternalPath(agentRoot, declaredPath) {
|
|
980
|
+
if ((0, import_path2.isAbsolute)(declaredPath)) return true;
|
|
981
|
+
const normRoot = (0, import_path2.normalize)((0, import_path2.resolve)(agentRoot));
|
|
982
|
+
const resolved = (0, import_path2.resolve)(normRoot, declaredPath);
|
|
983
|
+
return (0, import_path2.relative)(normRoot, resolved).startsWith("..");
|
|
984
|
+
}
|
|
985
|
+
async function discoverDescriptionFile(dir, explicit) {
|
|
986
|
+
if (explicit) {
|
|
987
|
+
try {
|
|
988
|
+
await (0, import_promises2.stat)((0, import_path2.join)(dir, explicit));
|
|
989
|
+
return explicit;
|
|
990
|
+
} catch {
|
|
991
|
+
throw new Error(`E003: Description file '${explicit}' not found in ${dir}`);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
const entries = await (0, import_promises2.readdir)(dir);
|
|
995
|
+
const descFiles = entries.filter((f) => f.endsWith(".description"));
|
|
996
|
+
if (descFiles.length === 0) {
|
|
997
|
+
throw new Error(`E003: No .description file found in ${dir}`);
|
|
998
|
+
}
|
|
999
|
+
if (descFiles.length > 1) {
|
|
1000
|
+
throw new Error(`E003: Multiple .description files found: ${descFiles.join(", ")} \u2014 use PackOptions.description to specify one`);
|
|
1001
|
+
}
|
|
1002
|
+
const name = descFiles[0];
|
|
1003
|
+
if (name !== "agent.description") {
|
|
1004
|
+
console.info(`[dot-agent] using description file: ${name}`);
|
|
1005
|
+
}
|
|
1006
|
+
return name;
|
|
1007
|
+
}
|
|
1008
|
+
async function consolidate(agentRoot, entryFile) {
|
|
1009
|
+
await initBehaviorParser();
|
|
1010
|
+
const normRoot = (0, import_path2.normalize)((0, import_path2.resolve)(agentRoot));
|
|
1011
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
1012
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1013
|
+
const order = [];
|
|
1014
|
+
const textCache = /* @__PURE__ */ new Map();
|
|
1015
|
+
async function dfs(relPath) {
|
|
1016
|
+
const absPath = (0, import_path2.resolve)(normRoot, relPath);
|
|
1017
|
+
if (visiting.has(absPath)) {
|
|
1018
|
+
throw new Error(`E013: Circular merge dependency involving '${relPath}'`);
|
|
1019
|
+
}
|
|
1020
|
+
if (visited.has(absPath)) return;
|
|
1021
|
+
visiting.add(absPath);
|
|
1022
|
+
let fileText;
|
|
1023
|
+
try {
|
|
1024
|
+
fileText = await (0, import_promises2.readFile)(absPath, "utf-8");
|
|
1025
|
+
} catch {
|
|
1026
|
+
throw new Error(`E012: Merge target not found: '${relPath}'`);
|
|
1027
|
+
}
|
|
1028
|
+
textCache.set(absPath, fileText);
|
|
1029
|
+
const result = parseBehaviorFile(fileText);
|
|
1030
|
+
const merges = result.ok?.merges ?? [];
|
|
1031
|
+
for (const mergePath of merges) {
|
|
1032
|
+
if ((0, import_path2.isAbsolute)(mergePath)) {
|
|
1033
|
+
throw new Error(`E014: Merge path '${mergePath}' in '${relPath}' is absolute \u2014 only paths within the agent root are allowed`);
|
|
1034
|
+
}
|
|
1035
|
+
const mergeAbs = (0, import_path2.resolve)((0, import_path2.dirname)(absPath), mergePath);
|
|
1036
|
+
const mergeRel = (0, import_path2.relative)(normRoot, mergeAbs);
|
|
1037
|
+
if (mergeRel.startsWith("..")) {
|
|
1038
|
+
throw new Error(`E014: Merge path '${mergePath}' in '${relPath}' escapes the agent root`);
|
|
1039
|
+
}
|
|
1040
|
+
await dfs(mergeRel);
|
|
1041
|
+
}
|
|
1042
|
+
visiting.delete(absPath);
|
|
1043
|
+
visited.add(absPath);
|
|
1044
|
+
order.push(relPath);
|
|
1045
|
+
}
|
|
1046
|
+
await dfs(entryFile);
|
|
1047
|
+
const texts = order.map((relPath) => textCache.get((0, import_path2.resolve)(normRoot, relPath)));
|
|
1048
|
+
return {
|
|
1049
|
+
mergedText: texts.join("\n"),
|
|
1050
|
+
mergeSources: order
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
async function collectFiles(dir, descriptionFile, mergedBehaviorText, mergeSources) {
|
|
913
1054
|
const files = /* @__PURE__ */ new Map();
|
|
914
1055
|
async function walk(subdir, prefix = "") {
|
|
915
1056
|
try {
|
|
@@ -928,13 +1069,16 @@ async function collectFiles(dir) {
|
|
|
928
1069
|
} catch {
|
|
929
1070
|
}
|
|
930
1071
|
}
|
|
931
|
-
files.set(
|
|
932
|
-
|
|
1072
|
+
files.set(descriptionFile, await (0, import_promises2.readFile)((0, import_path2.join)(dir, descriptionFile), "utf-8"));
|
|
1073
|
+
const flatBehaviorText = mergedBehaviorText.replace(/^\s*merge\s+"[^"]*"\s*$/gm, "");
|
|
1074
|
+
files.set("agent.behavior", flatBehaviorText);
|
|
1075
|
+
for (const relPath of mergeSources) {
|
|
1076
|
+
files.set(`behaviors/${relPath}`, await (0, import_promises2.readFile)((0, import_path2.join)(dir, relPath), "utf-8"));
|
|
1077
|
+
}
|
|
933
1078
|
try {
|
|
934
1079
|
files.set("SOUL.md", await (0, import_promises2.readFile)((0, import_path2.join)(dir, "SOUL.md"), "utf-8"));
|
|
935
1080
|
} catch {
|
|
936
1081
|
}
|
|
937
|
-
await walk((0, import_path2.join)(dir, "behaviors"), "behaviors");
|
|
938
1082
|
await walk((0, import_path2.join)(dir, "guides"), "guides");
|
|
939
1083
|
await walk((0, import_path2.join)(dir, "knowledge"), "knowledge");
|
|
940
1084
|
return files;
|
|
@@ -942,29 +1086,14 @@ async function collectFiles(dir) {
|
|
|
942
1086
|
async function pack(options = {}) {
|
|
943
1087
|
const dir = options.dir ?? process.cwd();
|
|
944
1088
|
const outPath = options.out ?? (0, import_path2.join)(dir, `${(0, import_path2.basename)(dir)}.agent`);
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
throw new Error("E003: File agent.description not found");
|
|
951
|
-
}
|
|
952
|
-
try {
|
|
953
|
-
behaviorText = await (0, import_promises2.readFile)((0, import_path2.join)(dir, "agent.behavior"), "utf-8");
|
|
954
|
-
} catch {
|
|
955
|
-
throw new Error("E007: File agent.behavior not found");
|
|
956
|
-
}
|
|
957
|
-
const [descriptionMessages, behaviorMessages] = await Promise.all([
|
|
958
|
-
lintDescription(descriptionText),
|
|
959
|
-
lintBehavior(behaviorText)
|
|
960
|
-
]);
|
|
961
|
-
const allMessages = [...descriptionMessages, ...behaviorMessages];
|
|
962
|
-
const errors = allMessages.filter((m) => m.severity === "error");
|
|
963
|
-
const warnings = allMessages.filter((m) => m.severity === "warning");
|
|
964
|
-
if (errors.length > 0) {
|
|
1089
|
+
const descriptionFileName = await discoverDescriptionFile(dir, options.description);
|
|
1090
|
+
const descriptionText = await (0, import_promises2.readFile)((0, import_path2.join)(dir, descriptionFileName), "utf-8");
|
|
1091
|
+
const descriptionMessages = await lintDescription(descriptionText, descriptionFileName);
|
|
1092
|
+
const descErrors = descriptionMessages.filter((m) => m.severity === "error");
|
|
1093
|
+
if (descErrors.length > 0) {
|
|
965
1094
|
throw new Error(
|
|
966
1095
|
`Lint failed:
|
|
967
|
-
${
|
|
1096
|
+
${descErrors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).join("\n")}`
|
|
968
1097
|
);
|
|
969
1098
|
}
|
|
970
1099
|
await initBehaviorParser();
|
|
@@ -974,9 +1103,26 @@ ${errors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).joi
|
|
|
974
1103
|
throw new Error(`E_DESC: ${firstError?.message ?? "parse failed"}`);
|
|
975
1104
|
}
|
|
976
1105
|
const df = descResult.ok;
|
|
1106
|
+
if (!df.behavior) {
|
|
1107
|
+
throw new Error(`E_DESC: '${descriptionFileName}' must declare a behavior file \u2014 add 'behavior "agent.behavior"' (or the name of your entry .behavior file)`);
|
|
1108
|
+
}
|
|
1109
|
+
if (isExternalPath(dir, df.behavior)) {
|
|
1110
|
+
throw new Error(`E014: behavior path '${df.behavior}' in '${descriptionFileName}' is absolute or escapes the agent root`);
|
|
1111
|
+
}
|
|
1112
|
+
const { mergedText, mergeSources } = await consolidate(dir, df.behavior);
|
|
1113
|
+
const behaviorMessages = await lintBehavior(mergedText, "agent.behavior", void 0, true);
|
|
1114
|
+
const allMessages = [...descriptionMessages, ...behaviorMessages];
|
|
1115
|
+
const errors = allMessages.filter((m) => m.severity === "error");
|
|
1116
|
+
const warnings = allMessages.filter((m) => m.severity === "warning");
|
|
1117
|
+
if (errors.length > 0) {
|
|
1118
|
+
throw new Error(
|
|
1119
|
+
`Lint failed:
|
|
1120
|
+
${errors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).join("\n")}`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
977
1123
|
const version = await resolveVersion(options.version);
|
|
978
1124
|
const commit = await resolveCommit(options.commit);
|
|
979
|
-
const allFiles = await collectFiles(dir);
|
|
1125
|
+
const allFiles = await collectFiles(dir, descriptionFileName, mergedText, mergeSources);
|
|
980
1126
|
const contentForHash = Array.from(allFiles.values()).join("");
|
|
981
1127
|
const sha256 = (0, import_crypto.createHash)("sha256").update(contentForHash).digest("hex");
|
|
982
1128
|
const namespace = df.agent.domain ?? "unknown";
|
|
@@ -996,7 +1142,7 @@ ${errors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).joi
|
|
|
996
1142
|
license: df.agent.license ?? "",
|
|
997
1143
|
persona: df.persona ?? "SOUL.md",
|
|
998
1144
|
purpose: "unknown",
|
|
999
|
-
compiler:
|
|
1145
|
+
compiler: `dot-agent/${COMPILER_VERSION}`,
|
|
1000
1146
|
commit,
|
|
1001
1147
|
capabilities: df.capabilities.map((c) => ({ id: c.name, description: c.description ?? "" })),
|
|
1002
1148
|
requires: df.requires,
|
|
@@ -1008,27 +1154,101 @@ ${errors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).joi
|
|
|
1008
1154
|
if (typesJson) {
|
|
1009
1155
|
agentFolder.file("types.json", typesJson);
|
|
1010
1156
|
}
|
|
1011
|
-
const gitkeepPaths = /* @__PURE__ */ new Set(["
|
|
1157
|
+
const gitkeepPaths = /* @__PURE__ */ new Set(["guides/.gitkeep", "knowledge/.gitkeep"]);
|
|
1012
1158
|
const filesJson = {
|
|
1013
|
-
description:
|
|
1159
|
+
description: descriptionFileName,
|
|
1014
1160
|
behavior: "agent.behavior",
|
|
1015
|
-
behaviors: Array.from(allFiles.keys()).filter((f) => f.startsWith("behaviors/")
|
|
1161
|
+
behaviors: Array.from(allFiles.keys()).filter((f) => f.startsWith("behaviors/")),
|
|
1016
1162
|
guides: Array.from(allFiles.keys()).filter((f) => f.startsWith("guides/") && !gitkeepPaths.has(f)),
|
|
1017
1163
|
knowledge: Array.from(allFiles.keys()).filter((f) => f.startsWith("knowledge/") && !gitkeepPaths.has(f))
|
|
1018
1164
|
};
|
|
1019
1165
|
agentFolder.file("files.json", JSON.stringify(filesJson, null, 2));
|
|
1020
1166
|
for (const [path, content] of allFiles) {
|
|
1021
|
-
|
|
1167
|
+
zip.file(path, content);
|
|
1022
1168
|
}
|
|
1023
1169
|
await writeZip(zip, outPath);
|
|
1024
1170
|
return { path: outPath, id, warnings };
|
|
1025
1171
|
}
|
|
1172
|
+
|
|
1173
|
+
// src/bundle.ts
|
|
1174
|
+
var import_promises3 = require("fs/promises");
|
|
1175
|
+
var import_path3 = require("path");
|
|
1176
|
+
var import_crypto2 = require("crypto");
|
|
1177
|
+
var GITKEEP = /* @__PURE__ */ new Set(["guides/.gitkeep", "knowledge/.gitkeep"]);
|
|
1178
|
+
async function bundleFromDir(dir) {
|
|
1179
|
+
const descriptionFileName = await discoverDescriptionFile(dir);
|
|
1180
|
+
const descriptionText = await (0, import_promises3.readFile)((0, import_path3.join)(dir, descriptionFileName), "utf-8");
|
|
1181
|
+
const descMessages = await lintDescription(descriptionText, descriptionFileName);
|
|
1182
|
+
const descErrors = descMessages.filter((m) => m.severity === "error");
|
|
1183
|
+
if (descErrors.length > 0) {
|
|
1184
|
+
throw new Error(
|
|
1185
|
+
`Lint failed:
|
|
1186
|
+
${descErrors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).join("\n")}`
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
await initBehaviorParser();
|
|
1190
|
+
const descResult = parseDescriptionFile(descriptionText);
|
|
1191
|
+
if (descResult.ok === null) {
|
|
1192
|
+
const firstError = descResult.diagnostics.find((d) => d.severity === "error");
|
|
1193
|
+
throw new Error(`E_DESC: ${firstError?.message ?? "parse failed"}`);
|
|
1194
|
+
}
|
|
1195
|
+
const df = descResult.ok;
|
|
1196
|
+
if (!df.behavior) {
|
|
1197
|
+
throw new Error(`E_DESC: '${descriptionFileName}' must declare a behavior file`);
|
|
1198
|
+
}
|
|
1199
|
+
const { mergedText, mergeSources } = await consolidate(dir, df.behavior);
|
|
1200
|
+
const behaviorMessages = await lintBehavior(mergedText, "agent.behavior", void 0, true);
|
|
1201
|
+
const allMessages = [...descMessages, ...behaviorMessages];
|
|
1202
|
+
const errors = allMessages.filter((m) => m.severity === "error");
|
|
1203
|
+
const warnings = allMessages.filter((m) => m.severity === "warning");
|
|
1204
|
+
if (errors.length > 0) {
|
|
1205
|
+
throw new Error(
|
|
1206
|
+
`Lint failed:
|
|
1207
|
+
${errors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).join("\n")}`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
for (const w of warnings) {
|
|
1211
|
+
process.stderr.write(`\x1B[33m\u26A0\x1B[0m ${w.file}:${w.line}:${w.col} ${w.code} ${w.message}
|
|
1212
|
+
`);
|
|
1213
|
+
}
|
|
1214
|
+
const allFiles = await collectFiles(dir, descriptionFileName, mergedText, mergeSources);
|
|
1215
|
+
const sha256 = (0, import_crypto2.createHash)("sha256").update(Array.from(allFiles.values()).join("")).digest("hex");
|
|
1216
|
+
const id = buildId({ namespace: df.agent.domain ?? "local", name: df.agent.name, version: "dev" });
|
|
1217
|
+
const aboutme = buildAboutme({
|
|
1218
|
+
id,
|
|
1219
|
+
name: df.agent.name,
|
|
1220
|
+
description: df.description ?? "",
|
|
1221
|
+
version: "dev",
|
|
1222
|
+
domain: df.agent.domain ?? "",
|
|
1223
|
+
license: df.agent.license ?? "",
|
|
1224
|
+
persona: df.persona ?? "SOUL.md",
|
|
1225
|
+
purpose: "development",
|
|
1226
|
+
compiler: `dot-agent/${COMPILER_VERSION}`,
|
|
1227
|
+
capabilities: df.capabilities.map((c) => ({ id: c.name, description: c.description ?? "" })),
|
|
1228
|
+
requires: df.requires,
|
|
1229
|
+
integrity: { sha256, files: ".agent/files.json" }
|
|
1230
|
+
});
|
|
1231
|
+
const files = {
|
|
1232
|
+
description: descriptionText,
|
|
1233
|
+
// Reuse collectFiles()'s 'agent.behavior' entry rather than the raw
|
|
1234
|
+
// mergedText — it has redundant `merge "..."` lines stripped (see
|
|
1235
|
+
// collectFiles() for why they're dead weight once flattened).
|
|
1236
|
+
behavior: allFiles.get("agent.behavior") ?? mergedText,
|
|
1237
|
+
soul: allFiles.get("SOUL.md"),
|
|
1238
|
+
guides: Array.from(allFiles.entries()).filter(([p]) => p.startsWith("guides/") && !GITKEEP.has(p)).map(([path, content]) => ({ path, content })),
|
|
1239
|
+
knowledge: Array.from(allFiles.entries()).filter(([p]) => p.startsWith("knowledge/") && !GITKEEP.has(p)).map(([path, content]) => ({ path, content })),
|
|
1240
|
+
behaviors: Array.from(allFiles.entries()).filter(([p]) => p.startsWith("behaviors/")).map(([path, content]) => ({ path, content }))
|
|
1241
|
+
};
|
|
1242
|
+
return { id, aboutme, files };
|
|
1243
|
+
}
|
|
1026
1244
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1027
1245
|
0 && (module.exports = {
|
|
1028
1246
|
aboutmeToJson,
|
|
1029
1247
|
buildAboutme,
|
|
1030
1248
|
buildId,
|
|
1249
|
+
bundleFromDir,
|
|
1031
1250
|
collectFiles,
|
|
1251
|
+
consolidate,
|
|
1032
1252
|
createLinter,
|
|
1033
1253
|
createZip,
|
|
1034
1254
|
extractDigest,
|