@agentproto/cli 0.1.0-alpha.8 → 0.1.0-alpha.9
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/cli.mjs +1822 -253
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +365 -37
- package/dist/index.mjs.map +1 -1
- package/dist/registry/builtins.d.ts +14 -0
- package/dist/registry/builtins.mjs +329 -0
- package/dist/registry/builtins.mjs.map +1 -0
- package/dist/registry/manifest.d.ts +88 -0
- package/dist/registry/manifest.mjs +42 -0
- package/dist/registry/manifest.mjs.map +1 -0
- package/dist/registry/plugins.d.ts +23 -0
- package/dist/registry/plugins.mjs +201 -0
- package/dist/registry/plugins.mjs.map +1 -0
- package/dist/registry/runtime.d.ts +70 -0
- package/dist/registry/runtime.mjs +52 -0
- package/dist/registry/runtime.mjs.map +1 -0
- package/dist/util/credentials.d.ts +71 -0
- package/dist/util/credentials.mjs +88 -0
- package/dist/util/credentials.mjs.map +1 -0
- package/package.json +32 -4
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as childProc from 'child_process';
|
|
2
2
|
import { execFile, spawn } from 'child_process';
|
|
3
3
|
import { createHash, randomUUID, randomBytes } from 'crypto';
|
|
4
|
-
import { mkdtemp, writeFile, chmod, rm, mkdir,
|
|
4
|
+
import { mkdtemp, writeFile, chmod, rm, mkdir, readFile, unlink, readdir, stat } from 'fs/promises';
|
|
5
5
|
import { tmpdir, userInfo, hostname, homedir, platform } from 'os';
|
|
6
6
|
import { resolve, join, dirname, isAbsolute, normalize, relative, basename } from 'path';
|
|
7
7
|
import { promisify, parseArgs } from 'util';
|
|
@@ -106,8 +106,8 @@ async function collectAgentprotoNamespaceRoots(start) {
|
|
|
106
106
|
for (let depth = 0; depth < 16; depth++) {
|
|
107
107
|
for (const candidate of candidatesAt(cur)) {
|
|
108
108
|
try {
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
109
|
+
const stat3 = await promises.stat(candidate);
|
|
110
|
+
if (stat3.isDirectory()) roots.push(candidate);
|
|
111
111
|
} catch {
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -485,9 +485,315 @@ async function saveLedger(path, ledger) {
|
|
|
485
485
|
await chmod(path, 384).catch(() => {
|
|
486
486
|
});
|
|
487
487
|
}
|
|
488
|
+
async function runInstallProfile(slug, args) {
|
|
489
|
+
const { values } = parseArgs({
|
|
490
|
+
args: [...args],
|
|
491
|
+
allowPositionals: true,
|
|
492
|
+
strict: true,
|
|
493
|
+
options: {
|
|
494
|
+
force: { type: "boolean", short: "f" },
|
|
495
|
+
"dry-run": { type: "boolean" },
|
|
496
|
+
"skip-setup": { type: "boolean" },
|
|
497
|
+
cwd: { type: "string" },
|
|
498
|
+
package: { type: "string" }
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
if (!slug.startsWith("runtime-profile/")) {
|
|
502
|
+
process.stderr.write(
|
|
503
|
+
`agentproto install: expected slug starting with 'runtime-profile/', got '${slug}'.
|
|
504
|
+
`
|
|
505
|
+
);
|
|
506
|
+
return 2;
|
|
507
|
+
}
|
|
508
|
+
const profileName = slug.slice("runtime-profile/".length);
|
|
509
|
+
if (!/^[a-z][a-z0-9-]*$/.test(profileName)) {
|
|
510
|
+
process.stderr.write(
|
|
511
|
+
`agentproto install: invalid profile name '${profileName}'. Lower-kebab only.
|
|
512
|
+
`
|
|
513
|
+
);
|
|
514
|
+
return 2;
|
|
515
|
+
}
|
|
516
|
+
let packageName;
|
|
517
|
+
if (typeof values.package === "string" && values.package) {
|
|
518
|
+
packageName = values.package;
|
|
519
|
+
} else {
|
|
520
|
+
const aliased = await readProfileAlias(profileName);
|
|
521
|
+
packageName = aliased ?? `@agentproto/runtime-profile-${profileName}`;
|
|
522
|
+
}
|
|
523
|
+
let mod;
|
|
524
|
+
try {
|
|
525
|
+
mod = await import(packageName);
|
|
526
|
+
} catch (err) {
|
|
527
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
528
|
+
process.stderr.write(
|
|
529
|
+
`agentproto install: could not load profile package '${packageName}'. Install it first with: npm i -g ${packageName}
|
|
530
|
+
cause: ${cause}
|
|
531
|
+
`
|
|
532
|
+
);
|
|
533
|
+
return 1;
|
|
534
|
+
}
|
|
535
|
+
const filesDir = typeof mod.FILES_DIR === "string" ? mod.FILES_DIR : null;
|
|
536
|
+
const loadProfileManifest = mod.loadProfileManifest;
|
|
537
|
+
if (!filesDir || !loadProfileManifest) {
|
|
538
|
+
process.stderr.write(
|
|
539
|
+
`agentproto install: profile package '${packageName}' does not export FILES_DIR + loadProfileManifest.
|
|
540
|
+
`
|
|
541
|
+
);
|
|
542
|
+
return 1;
|
|
543
|
+
}
|
|
544
|
+
const manifest = await loadProfileManifest();
|
|
545
|
+
const cwd = values.cwd ? resolve(values.cwd) : process.cwd();
|
|
546
|
+
const dryRun = values["dry-run"] === true;
|
|
547
|
+
const force = values.force === true;
|
|
548
|
+
process.stdout.write(
|
|
549
|
+
`agentproto install ${slug} v${manifest.version} \u2192 ${cwd}
|
|
550
|
+
${manifest.description}
|
|
551
|
+
`
|
|
552
|
+
);
|
|
553
|
+
const ledgerPath = ledgerPathFor2(profileName);
|
|
554
|
+
const prevLedger = await loadLedger2(ledgerPath);
|
|
555
|
+
const newLedger = {
|
|
556
|
+
slug,
|
|
557
|
+
version: manifest.version,
|
|
558
|
+
installedAt: prevLedger?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
559
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
560
|
+
files: []
|
|
561
|
+
};
|
|
562
|
+
let applied = 0;
|
|
563
|
+
let skipped = 0;
|
|
564
|
+
let failed = 0;
|
|
565
|
+
for (const entry of manifest.files) {
|
|
566
|
+
const src = resolve(filesDir, entry.src);
|
|
567
|
+
const dest = resolve(cwd, entry.dest);
|
|
568
|
+
const prev = prevLedger?.files.find((f) => f.dest === entry.dest) ?? null;
|
|
569
|
+
try {
|
|
570
|
+
const srcBuf = await readFile(src);
|
|
571
|
+
const result = await applyStrategy({
|
|
572
|
+
strategy: entry.strategy,
|
|
573
|
+
srcBuf,
|
|
574
|
+
dest,
|
|
575
|
+
prev,
|
|
576
|
+
force,
|
|
577
|
+
dryRun
|
|
578
|
+
});
|
|
579
|
+
newLedger.files.push({
|
|
580
|
+
dest: entry.dest,
|
|
581
|
+
strategy: entry.strategy,
|
|
582
|
+
hashAfter: result.hashAfter,
|
|
583
|
+
appliedAt: result.wrote ? (/* @__PURE__ */ new Date()).toISOString() : prev?.appliedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
584
|
+
});
|
|
585
|
+
if (result.wrote) {
|
|
586
|
+
applied += 1;
|
|
587
|
+
process.stdout.write(` ${result.action.padEnd(10)} ${entry.dest}
|
|
588
|
+
`);
|
|
589
|
+
if (entry.executable && !dryRun) {
|
|
590
|
+
await chmod(dest, 493);
|
|
591
|
+
}
|
|
592
|
+
} else {
|
|
593
|
+
skipped += 1;
|
|
594
|
+
process.stdout.write(` ${result.action.padEnd(10)} ${entry.dest}
|
|
595
|
+
`);
|
|
596
|
+
}
|
|
597
|
+
} catch (err) {
|
|
598
|
+
failed += 1;
|
|
599
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
600
|
+
process.stderr.write(` FAIL ${entry.dest} \u2014 ${msg}
|
|
601
|
+
`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (!dryRun) {
|
|
605
|
+
await saveLedger2(ledgerPath, newLedger);
|
|
606
|
+
}
|
|
607
|
+
process.stdout.write(
|
|
608
|
+
`agentproto install ${slug}: ${applied} applied, ${skipped} skipped, ${failed} failed${dryRun ? " (dry-run)" : ""}
|
|
609
|
+
`
|
|
610
|
+
);
|
|
611
|
+
return failed === 0 ? 0 : 1;
|
|
612
|
+
}
|
|
613
|
+
async function applyStrategy(opts) {
|
|
614
|
+
const { strategy, srcBuf, dest, prev, force, dryRun } = opts;
|
|
615
|
+
const destExists = await fileExists(dest);
|
|
616
|
+
if (strategy === "preserve") {
|
|
617
|
+
if (destExists) {
|
|
618
|
+
const currentHash = await hashFile(dest);
|
|
619
|
+
return { wrote: false, action: "preserve", hashAfter: currentHash };
|
|
620
|
+
}
|
|
621
|
+
if (!dryRun) {
|
|
622
|
+
await ensureDir(dirname(dest));
|
|
623
|
+
await writeFile(dest, srcBuf);
|
|
624
|
+
}
|
|
625
|
+
return { wrote: true, action: "create", hashAfter: sha256(srcBuf) };
|
|
626
|
+
}
|
|
627
|
+
if (strategy === "overwrite") {
|
|
628
|
+
const srcHash = sha256(srcBuf);
|
|
629
|
+
if (destExists && !force) {
|
|
630
|
+
const destHash = await hashFile(dest);
|
|
631
|
+
if (destHash === srcHash) {
|
|
632
|
+
return { wrote: false, action: "unchanged", hashAfter: destHash };
|
|
633
|
+
}
|
|
634
|
+
if (prev && prev.hashAfter !== destHash) {
|
|
635
|
+
return {
|
|
636
|
+
wrote: false,
|
|
637
|
+
action: "user-edit",
|
|
638
|
+
hashAfter: destHash
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (!dryRun) {
|
|
643
|
+
await ensureDir(dirname(dest));
|
|
644
|
+
await writeFile(dest, srcBuf);
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
wrote: true,
|
|
648
|
+
action: destExists ? "update" : "create",
|
|
649
|
+
hashAfter: srcHash
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (strategy === "merge-json-deep") {
|
|
653
|
+
const srcJson = parseJsonOrThrow(srcBuf.toString("utf8"), "src");
|
|
654
|
+
let merged = srcJson;
|
|
655
|
+
if (destExists) {
|
|
656
|
+
const destRaw = await readFile(dest, "utf8");
|
|
657
|
+
const destJson = parseJsonOrThrow(destRaw, "dest");
|
|
658
|
+
merged = deepMerge(destJson, srcJson);
|
|
659
|
+
}
|
|
660
|
+
const out = `${JSON.stringify(merged, null, 2)}
|
|
661
|
+
`;
|
|
662
|
+
const outBuf = Buffer.from(out, "utf8");
|
|
663
|
+
const outHash = sha256(outBuf);
|
|
664
|
+
if (destExists) {
|
|
665
|
+
const destHash = await hashFile(dest);
|
|
666
|
+
if (destHash === outHash) {
|
|
667
|
+
return { wrote: false, action: "unchanged", hashAfter: destHash };
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (!dryRun) {
|
|
671
|
+
await ensureDir(dirname(dest));
|
|
672
|
+
await writeFile(dest, outBuf);
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
wrote: true,
|
|
676
|
+
action: destExists ? "merge" : "create",
|
|
677
|
+
hashAfter: outHash
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
if (strategy === "append") {
|
|
681
|
+
const srcText = srcBuf.toString("utf8");
|
|
682
|
+
if (destExists) {
|
|
683
|
+
const destText = await readFile(dest, "utf8");
|
|
684
|
+
if (destText.includes(srcText)) {
|
|
685
|
+
return {
|
|
686
|
+
wrote: false,
|
|
687
|
+
action: "unchanged",
|
|
688
|
+
hashAfter: sha256(Buffer.from(destText, "utf8"))
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
const out = `${destText.endsWith("\n") ? destText : `${destText}
|
|
692
|
+
`}${srcText}`;
|
|
693
|
+
const outBuf = Buffer.from(out, "utf8");
|
|
694
|
+
if (!dryRun) {
|
|
695
|
+
await ensureDir(dirname(dest));
|
|
696
|
+
await writeFile(dest, outBuf);
|
|
697
|
+
}
|
|
698
|
+
return { wrote: true, action: "append", hashAfter: sha256(outBuf) };
|
|
699
|
+
}
|
|
700
|
+
if (!dryRun) {
|
|
701
|
+
await ensureDir(dirname(dest));
|
|
702
|
+
await writeFile(dest, srcBuf);
|
|
703
|
+
}
|
|
704
|
+
return { wrote: true, action: "create", hashAfter: sha256(srcBuf) };
|
|
705
|
+
}
|
|
706
|
+
throw new Error(`Unknown strategy: ${strategy}`);
|
|
707
|
+
}
|
|
708
|
+
function deepMerge(target, source) {
|
|
709
|
+
if (target && source && typeof target === "object" && typeof source === "object" && !Array.isArray(target) && !Array.isArray(source)) {
|
|
710
|
+
const out = { ...target };
|
|
711
|
+
for (const [k, v] of Object.entries(source)) {
|
|
712
|
+
out[k] = deepMerge(target[k], v);
|
|
713
|
+
}
|
|
714
|
+
return out;
|
|
715
|
+
}
|
|
716
|
+
if (Array.isArray(target) && Array.isArray(source)) {
|
|
717
|
+
const seen = /* @__PURE__ */ new Set();
|
|
718
|
+
const out = [];
|
|
719
|
+
for (const item of [...target, ...source]) {
|
|
720
|
+
const key = JSON.stringify(item);
|
|
721
|
+
if (seen.has(key)) continue;
|
|
722
|
+
seen.add(key);
|
|
723
|
+
out.push(item);
|
|
724
|
+
}
|
|
725
|
+
return out;
|
|
726
|
+
}
|
|
727
|
+
return source ?? target;
|
|
728
|
+
}
|
|
729
|
+
function parseJsonOrThrow(raw, side) {
|
|
730
|
+
try {
|
|
731
|
+
return JSON.parse(raw);
|
|
732
|
+
} catch (err) {
|
|
733
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
734
|
+
throw new Error(`merge-json-deep ${side} not valid JSON: ${msg}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
function sha256(buf) {
|
|
738
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
739
|
+
}
|
|
740
|
+
async function hashFile(path) {
|
|
741
|
+
const buf = await readFile(path);
|
|
742
|
+
return sha256(buf);
|
|
743
|
+
}
|
|
744
|
+
async function fileExists(path) {
|
|
745
|
+
try {
|
|
746
|
+
await stat(path);
|
|
747
|
+
return true;
|
|
748
|
+
} catch {
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
async function ensureDir(dir) {
|
|
753
|
+
try {
|
|
754
|
+
await stat(dir);
|
|
755
|
+
} catch {
|
|
756
|
+
await mkdir(dir, { recursive: true });
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
function ledgerPathFor2(profileName) {
|
|
760
|
+
return join(homedir(), ".agentproto", "profiles", `${profileName}.json`);
|
|
761
|
+
}
|
|
762
|
+
async function loadLedger2(path) {
|
|
763
|
+
try {
|
|
764
|
+
const raw = await readFile(path, "utf8");
|
|
765
|
+
return JSON.parse(raw);
|
|
766
|
+
} catch {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function saveLedger2(path, ledger) {
|
|
771
|
+
await ensureDir(dirname(path));
|
|
772
|
+
await writeFile(path, `${JSON.stringify(ledger, null, 2)}
|
|
773
|
+
`);
|
|
774
|
+
}
|
|
775
|
+
async function readProfileAlias(profileName) {
|
|
776
|
+
const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
|
|
777
|
+
const path = join(base, "config.json");
|
|
778
|
+
try {
|
|
779
|
+
const raw = await readFile(path, "utf8");
|
|
780
|
+
const parsed = JSON.parse(raw);
|
|
781
|
+
const aliased = parsed.profileAliases?.[profileName];
|
|
782
|
+
return typeof aliased === "string" ? aliased : null;
|
|
783
|
+
} catch {
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
488
787
|
|
|
489
788
|
// src/commands/install.ts
|
|
490
789
|
async function runInstall(args) {
|
|
790
|
+
const slugPeek = args.find((a) => !a.startsWith("-"));
|
|
791
|
+
if (slugPeek?.startsWith("runtime-profile/")) {
|
|
792
|
+
return runInstallProfile(
|
|
793
|
+
slugPeek,
|
|
794
|
+
args.filter((a) => a !== slugPeek)
|
|
795
|
+
);
|
|
796
|
+
}
|
|
491
797
|
const { values, positionals } = parseArgs({
|
|
492
798
|
args: [...args],
|
|
493
799
|
allowPositionals: true,
|
|
@@ -780,7 +1086,7 @@ async function runDownloadInstaller(opts) {
|
|
|
780
1086
|
}
|
|
781
1087
|
if (extractCode !== 0) return extractCode;
|
|
782
1088
|
const srcBin = join(extractDir, opts.extractBin);
|
|
783
|
-
const binDir = process.env["AGENTPROTO_BIN_DIR"] ?? join(
|
|
1089
|
+
const binDir = process.env["AGENTPROTO_BIN_DIR"] ?? join(homedir3(), ".local", "bin");
|
|
784
1090
|
await mkdir(binDir, { recursive: true });
|
|
785
1091
|
const destBin = join(binDir, opts.extractBin.split("/").pop());
|
|
786
1092
|
const cpCode = await spawnInherit("cp", ["-p", srcBin, destBin]);
|
|
@@ -806,7 +1112,7 @@ async function runDownloadInstaller(opts) {
|
|
|
806
1112
|
});
|
|
807
1113
|
}
|
|
808
1114
|
}
|
|
809
|
-
function
|
|
1115
|
+
function homedir3() {
|
|
810
1116
|
return process.env["HOME"] ?? process.env["USERPROFILE"] ?? "/";
|
|
811
1117
|
}
|
|
812
1118
|
function spawnInherit(cmd, argv) {
|
|
@@ -1329,28 +1635,51 @@ createDoctype({
|
|
|
1329
1635
|
validate(def) {
|
|
1330
1636
|
const result = extensionFrontmatterSchema.safeParse(def);
|
|
1331
1637
|
if (!result.success) {
|
|
1332
|
-
throw new Error(
|
|
1638
|
+
throw new Error(
|
|
1639
|
+
`defineExtension (AIP-40): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
1640
|
+
);
|
|
1333
1641
|
}
|
|
1334
1642
|
},
|
|
1335
1643
|
build(def) {
|
|
1336
1644
|
return { ...def };
|
|
1337
1645
|
}
|
|
1338
1646
|
});
|
|
1647
|
+
function parseExtensionManifest(source) {
|
|
1648
|
+
const parsed = matter(source);
|
|
1649
|
+
if (Object.keys(parsed.data).length === 0) {
|
|
1650
|
+
throw new Error("parseExtensionManifest: missing or empty frontmatter");
|
|
1651
|
+
}
|
|
1652
|
+
const result = extensionFrontmatterSchema.safeParse(parsed.data);
|
|
1653
|
+
if (!result.success) {
|
|
1654
|
+
throw new Error(
|
|
1655
|
+
`parseExtensionManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1658
|
+
return { frontmatter: result.data, body: parsed.content };
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// ../extension/dist/index.mjs
|
|
1339
1662
|
function specFromExtension(extension, opts = {}) {
|
|
1340
1663
|
const { parent } = opts;
|
|
1341
1664
|
const isRoot = extension.extends === "none";
|
|
1342
1665
|
if (!isRoot && !parent) {
|
|
1343
|
-
throw new Error(
|
|
1666
|
+
throw new Error(
|
|
1667
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' extends '${extension.extends}' but no parent spec was provided in opts.parent`
|
|
1668
|
+
);
|
|
1344
1669
|
}
|
|
1345
1670
|
if (isRoot && !extension.path_convention) {
|
|
1346
|
-
throw new Error(
|
|
1671
|
+
throw new Error(
|
|
1672
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype (extends: none) and MUST declare path_convention`
|
|
1673
|
+
);
|
|
1347
1674
|
}
|
|
1348
1675
|
if (parent && extension.tighten) {
|
|
1349
1676
|
verifyTightening(extension);
|
|
1350
1677
|
}
|
|
1351
1678
|
const slugName = extension.slug.split(":")[1] ?? extension.slug;
|
|
1352
1679
|
const pathTemplate = extension.path_convention ?? null;
|
|
1353
|
-
const extensionKeys = new Set(
|
|
1680
|
+
const extensionKeys = new Set(
|
|
1681
|
+
Object.keys(extension.add_fields?.properties ?? {})
|
|
1682
|
+
);
|
|
1354
1683
|
const define = (params) => {
|
|
1355
1684
|
const withDefaults = { ...params };
|
|
1356
1685
|
if (extension.defaults) {
|
|
@@ -1366,10 +1695,8 @@ function specFromExtension(extension, opts = {}) {
|
|
|
1366
1695
|
const parentOnly = {};
|
|
1367
1696
|
const extensionOnly = {};
|
|
1368
1697
|
for (const [key, value] of Object.entries(withDefaults)) {
|
|
1369
|
-
if (extensionKeys.has(key))
|
|
1370
|
-
|
|
1371
|
-
else
|
|
1372
|
-
parentOnly[key] = value;
|
|
1698
|
+
if (extensionKeys.has(key)) extensionOnly[key] = value;
|
|
1699
|
+
else parentOnly[key] = value;
|
|
1373
1700
|
}
|
|
1374
1701
|
const parentHandle = parent.define(parentOnly);
|
|
1375
1702
|
return Object.freeze({
|
|
@@ -1380,7 +1707,11 @@ function specFromExtension(extension, opts = {}) {
|
|
|
1380
1707
|
const parse = isRoot ? rootParse(extension) : parent.parse;
|
|
1381
1708
|
const pathOf = (handle) => {
|
|
1382
1709
|
if (pathTemplate) {
|
|
1383
|
-
return resolvePathTemplate(
|
|
1710
|
+
return resolvePathTemplate(
|
|
1711
|
+
pathTemplate,
|
|
1712
|
+
handle,
|
|
1713
|
+
slugName
|
|
1714
|
+
);
|
|
1384
1715
|
}
|
|
1385
1716
|
return parent.pathOf(handle);
|
|
1386
1717
|
};
|
|
@@ -1397,36 +1728,33 @@ function verifyTightening(extension, parent) {
|
|
|
1397
1728
|
const t = extension.tighten ?? {};
|
|
1398
1729
|
for (const [field, override] of Object.entries(t)) {
|
|
1399
1730
|
if (typeof override.minLength === "number" && typeof override.maxLength === "number" && override.minLength > override.maxLength) {
|
|
1400
|
-
throw new Error(
|
|
1731
|
+
throw new Error(
|
|
1732
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minLength (${override.minLength}) > maxLength (${override.maxLength})`
|
|
1733
|
+
);
|
|
1401
1734
|
}
|
|
1402
1735
|
if (typeof override.minimum === "number" && typeof override.maximum === "number" && override.minimum > override.maximum) {
|
|
1403
|
-
throw new Error(
|
|
1736
|
+
throw new Error(
|
|
1737
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minimum (${override.minimum}) > maximum (${override.maximum})`
|
|
1738
|
+
);
|
|
1404
1739
|
}
|
|
1405
1740
|
if (override.enum !== void 0 && !Array.isArray(override.enum)) {
|
|
1406
|
-
throw new Error(
|
|
1741
|
+
throw new Error(
|
|
1742
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: enum must be an array`
|
|
1743
|
+
);
|
|
1407
1744
|
}
|
|
1408
1745
|
}
|
|
1409
1746
|
}
|
|
1410
1747
|
function rootParse(extension) {
|
|
1411
1748
|
return (source) => {
|
|
1412
|
-
throw new Error(
|
|
1749
|
+
throw new Error(
|
|
1750
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype \u2014 supply your own parser or extend a public AIP for parser inheritance`
|
|
1751
|
+
);
|
|
1413
1752
|
};
|
|
1414
1753
|
}
|
|
1415
1754
|
function resolvePathTemplate(template, handle, doctypeSlug) {
|
|
1416
1755
|
const identity = typeof handle.id === "string" && handle.id || typeof handle.slug === "string" && handle.slug || typeof handle.name === "string" && handle.name || "unknown";
|
|
1417
1756
|
return template.replace(/<slug>/g, identity).replace(/<DOCTYPE>/g, doctypeSlug.toUpperCase());
|
|
1418
1757
|
}
|
|
1419
|
-
function parseExtensionManifest(source) {
|
|
1420
|
-
const parsed = matter(source);
|
|
1421
|
-
if (Object.keys(parsed.data).length === 0) {
|
|
1422
|
-
throw new Error("parseExtensionManifest: missing or empty frontmatter");
|
|
1423
|
-
}
|
|
1424
|
-
const result = extensionFrontmatterSchema.safeParse(parsed.data);
|
|
1425
|
-
if (!result.success) {
|
|
1426
|
-
throw new Error(`parseExtensionManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
|
|
1427
|
-
}
|
|
1428
|
-
return { frontmatter: result.data, body: parsed.content };
|
|
1429
|
-
}
|
|
1430
1758
|
|
|
1431
1759
|
// ../mcp-server/dist/index.mjs
|
|
1432
1760
|
async function createMcpServer(opts) {
|
|
@@ -5551,7 +5879,7 @@ var RemoteController = class {
|
|
|
5551
5879
|
let bearerTokenHash = "";
|
|
5552
5880
|
if (exposesGateway) {
|
|
5553
5881
|
bearerToken = randomBytes(32).toString("base64url");
|
|
5554
|
-
bearerTokenHash =
|
|
5882
|
+
bearerTokenHash = sha2562(bearerToken);
|
|
5555
5883
|
}
|
|
5556
5884
|
const state = {
|
|
5557
5885
|
provider: providerId,
|
|
@@ -5616,7 +5944,7 @@ function pickProvider(id) {
|
|
|
5616
5944
|
const _exhaustive = id;
|
|
5617
5945
|
throw new Error(`unknown remote provider: ${String(_exhaustive)}`);
|
|
5618
5946
|
}
|
|
5619
|
-
function
|
|
5947
|
+
function sha2562(input2) {
|
|
5620
5948
|
return createHash("sha256").update(input2).digest("hex");
|
|
5621
5949
|
}
|
|
5622
5950
|
function errToMessage(err) {
|
|
@@ -5709,7 +6037,7 @@ var WorkspacePathError = class extends Error {
|
|
|
5709
6037
|
};
|
|
5710
6038
|
function createWorkspaceFs(opts) {
|
|
5711
6039
|
const root = resolve(opts.workspace);
|
|
5712
|
-
function
|
|
6040
|
+
function resolvePath42(path) {
|
|
5713
6041
|
if (typeof path !== "string" || path.length === 0) {
|
|
5714
6042
|
throw new WorkspacePathError("path must be a non-empty string");
|
|
5715
6043
|
}
|
|
@@ -5729,18 +6057,18 @@ function createWorkspaceFs(opts) {
|
|
|
5729
6057
|
}
|
|
5730
6058
|
return {
|
|
5731
6059
|
async readFile(path) {
|
|
5732
|
-
const abs =
|
|
6060
|
+
const abs = resolvePath42(path);
|
|
5733
6061
|
const buf = await readFile(abs);
|
|
5734
6062
|
return buf.toString("utf8");
|
|
5735
6063
|
},
|
|
5736
6064
|
async writeFile(path, content) {
|
|
5737
|
-
const abs =
|
|
6065
|
+
const abs = resolvePath42(path);
|
|
5738
6066
|
await mkdir(dirname(abs), { recursive: true });
|
|
5739
6067
|
await writeFile(abs, content);
|
|
5740
6068
|
},
|
|
5741
6069
|
async exists(path) {
|
|
5742
6070
|
try {
|
|
5743
|
-
const abs =
|
|
6071
|
+
const abs = resolvePath42(path);
|
|
5744
6072
|
return existsSync(abs);
|
|
5745
6073
|
} catch {
|
|
5746
6074
|
return false;
|
|
@@ -5945,8 +6273,8 @@ async function runServe(args) {
|
|
|
5945
6273
|
values.workspace ?? cfgDaemon.workspace ?? process.cwd()
|
|
5946
6274
|
);
|
|
5947
6275
|
try {
|
|
5948
|
-
const
|
|
5949
|
-
if (!
|
|
6276
|
+
const stat3 = await promises.stat(workspace);
|
|
6277
|
+
if (!stat3.isDirectory()) {
|
|
5950
6278
|
process.stderr.write(
|
|
5951
6279
|
`agentproto serve: --workspace "${workspace}" is not a directory.
|
|
5952
6280
|
`
|