@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/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, unlink, readFile, readdir, stat } from 'fs/promises';
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 stat2 = await promises.stat(candidate);
110
- if (stat2.isDirectory()) roots.push(candidate);
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(homedir2(), ".local", "bin");
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 homedir2() {
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(`defineExtension (AIP-40): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
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(`specFromExtension (AIP-40): extension '${extension.slug}' extends '${extension.extends}' but no parent spec was provided in opts.parent`);
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(`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype (extends: none) and MUST declare path_convention`);
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(Object.keys(extension.add_fields?.properties ?? {}));
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
- extensionOnly[key] = value;
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(pathTemplate, handle, slugName);
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(`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minLength (${override.minLength}) > maxLength (${override.maxLength})`);
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(`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minimum (${override.minimum}) > maximum (${override.maximum})`);
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(`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: enum must be an array`);
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(`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype \u2014 supply your own parser or extend a public AIP for parser inheritance`);
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 = sha256(bearerToken);
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 sha256(input2) {
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 resolvePath4(path) {
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 = resolvePath4(path);
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 = resolvePath4(path);
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 = resolvePath4(path);
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 stat2 = await promises.stat(workspace);
5949
- if (!stat2.isDirectory()) {
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
  `