@vendian/cli 0.0.5 → 0.0.7

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/README.md CHANGED
@@ -6,6 +6,8 @@ Command-line tools for signing in to Vendian and running local agent workflows.
6
6
  npm install -g @vendian/cli
7
7
  vendian
8
8
  vendian login
9
+ vendian init --output-dir ./agents
10
+ vendian create "My Agent" --output-dir ./agents
9
11
  vendian cloud local serve --agents-dir ./agents
10
12
  ```
11
13
 
@@ -16,6 +18,11 @@ vendian
16
18
  vendian login
17
19
  vendian doctor
18
20
  vendian update
21
+ vendian init --output-dir ./agents
22
+ vendian create "My Agent" --output-dir ./agents
23
+ vendian validate ./agents/my-agent --runtime
24
+ vendian test ./agents/my-agent --dry-run --mock-credentials
25
+ vendian models
19
26
  vendian cloud local serve --agents-dir ./agents
20
27
  ```
21
28
 
@@ -32,6 +39,13 @@ managed runtime without changing your agents.
32
39
  After login, regular `vendian cloud ...` commands are forwarded to the managed
33
40
  runtime installed by the CLI.
34
41
 
42
+ Agent authoring commands are also forwarded to the managed Python SDK CLI.
43
+ `vendian init` writes the current SDK-owned authoring docs into `.vendian-docs/`,
44
+ and `vendian create` scaffolds agents from the SDK templates. Updating the
45
+ managed runtime refreshes these docs and templates with the Python SDK. The CLI
46
+ remembers workspaces initialized with `vendian init` and refreshes their
47
+ `.vendian-docs/` automatically after `vendian update` or a managed auto-update.
48
+
35
49
  ## Local Data
36
50
 
37
51
  Vendian stores its managed runtime and CLI state under your user profile:
package/bin/vendian.cjs CHANGED
File without changes
package/cli-wrapper.cjs CHANGED
@@ -52,15 +52,21 @@ var import_node_path = __toESM(require("node:path"), 1);
52
52
  function pathApi(platform) {
53
53
  return platform === "win32" ? import_node_path.default.win32 : import_node_path.default.posix;
54
54
  }
55
+ function homeDir(env, platform) {
56
+ if (platform === "win32") {
57
+ return env.USERPROFILE || import_node_os.default.homedir();
58
+ }
59
+ return env.HOME || import_node_os.default.homedir();
60
+ }
55
61
  function vendianHome(env = process.env, platform = process.platform) {
56
62
  if (env.VENDIAN_CLI_HOME) {
57
63
  return pathApi(platform).resolve(env.VENDIAN_CLI_HOME);
58
64
  }
59
65
  if (platform === "win32") {
60
- const root = env.LOCALAPPDATA || import_node_path.default.join(import_node_os.default.homedir(), "AppData", "Local");
66
+ const root = env.LOCALAPPDATA || import_node_path.default.win32.join(homeDir(env, platform), "AppData", "Local");
61
67
  return import_node_path.default.win32.join(root, "Vendian", "cli");
62
68
  }
63
- return import_node_path.default.posix.join(import_node_os.default.homedir(), ".vendian", "cli");
69
+ return import_node_path.default.posix.join(homeDir(env, platform), ".vendian", "cli");
64
70
  }
65
71
  function configPath(env = process.env, platform = process.platform) {
66
72
  if (env.VENDIAN_CLI_CONFIG) {
@@ -239,12 +245,10 @@ function run(command, args, options = {}) {
239
245
  ...options
240
246
  });
241
247
  }
242
- function savePackageTokenSecret(token, config = {}, platform = process.platform) {
243
- if (!token) {
244
- return { ok: false };
245
- }
246
- if (platform === "darwin") {
247
- const result = run("security", [
248
+ function buildMacKeychainSaveCommand(token) {
249
+ return [
250
+ "security",
251
+ [
248
252
  "add-generic-password",
249
253
  "-a",
250
254
  PACKAGE_TOKEN_ACCOUNT,
@@ -253,7 +257,28 @@ function savePackageTokenSecret(token, config = {}, platform = process.platform)
253
257
  "-w",
254
258
  token,
255
259
  "-U"
256
- ]);
260
+ ]
261
+ ];
262
+ }
263
+ function buildMacKeychainLoadCommand() {
264
+ return [
265
+ "security",
266
+ [
267
+ "find-generic-password",
268
+ "-a",
269
+ PACKAGE_TOKEN_ACCOUNT,
270
+ "-s",
271
+ SERVICE,
272
+ "-w"
273
+ ]
274
+ ];
275
+ }
276
+ function savePackageTokenSecret(token, config = {}, platform = process.platform) {
277
+ if (!token) {
278
+ return { ok: false };
279
+ }
280
+ if (platform === "darwin") {
281
+ const result = run(...buildMacKeychainSaveCommand(token));
257
282
  return { ok: result.status === 0, provider: "macos-keychain" };
258
283
  }
259
284
  if (platform === "win32") {
@@ -286,14 +311,7 @@ function savePackageTokenSecret(token, config = {}, platform = process.platform)
286
311
  }
287
312
  function loadPackageTokenSecret(config = {}, platform = process.platform) {
288
313
  if (platform === "darwin") {
289
- const result = run("security", [
290
- "find-generic-password",
291
- "-a",
292
- PACKAGE_TOKEN_ACCOUNT,
293
- "-s",
294
- SERVICE,
295
- "-w"
296
- ]);
314
+ const result = run(...buildMacKeychainLoadCommand());
297
315
  return result.status === 0 ? result.stdout.trim() : "";
298
316
  }
299
317
  if (platform === "win32") {
@@ -417,45 +435,96 @@ function doctor({ env = process.env, platform = process.platform } = {}) {
417
435
  }
418
436
 
419
437
  // src/forward.js
438
+ var import_node_fs8 = __toESM(require("node:fs"), 1);
439
+
440
+ // src/docs.js
420
441
  var import_node_fs5 = __toESM(require("node:fs"), 1);
421
- var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
422
- async function forwardToPythonVendian(args, { env = process.env, platform = process.platform } = {}) {
423
- const venvPath = managedVenvPath(env, platform);
424
- const vendianPath = venvVendian(venvPath, platform);
425
- if (!import_node_fs5.default.existsSync(vendianPath)) {
426
- throw new Error("Vendian is not set up yet. Run `vendian login` first.");
427
- }
428
- maybeAutoUpdateManagedEnv({ env, platform, venvPath });
429
- const code = await spawnForward(vendianPath, args, { env });
430
- process.exitCode = code;
442
+ var import_node_path4 = __toESM(require("node:path"), 1);
443
+ var DOC_WORKSPACES_KEY = "agentDocWorkspaces";
444
+ function pathApi2(platform) {
445
+ return platform === "win32" ? import_node_path4.default.win32 : import_node_path4.default.posix;
431
446
  }
432
- function maybeAutoUpdateManagedEnv({ env = process.env, platform = process.platform, venvPath = managedVenvPath(env, platform) } = {}) {
433
- if (env.VENDIAN_SKIP_AUTO_UPDATE === "1") {
434
- return false;
447
+ function commandWritesAgentDocs(args = []) {
448
+ return args[0] === "init";
449
+ }
450
+ function initOutputDir(args = []) {
451
+ for (let index = 1; index < args.length; index += 1) {
452
+ const arg = args[index];
453
+ if (arg === "--output-dir" || arg === "-o") {
454
+ return args[index + 1] || ".";
455
+ }
456
+ if (arg.startsWith("--output-dir=")) {
457
+ return arg.slice("--output-dir=".length) || ".";
458
+ }
435
459
  }
460
+ return ".";
461
+ }
462
+ function resolveWorkspacePath(outputDir, { cwd = process.cwd(), platform = process.platform } = {}) {
463
+ const api = pathApi2(platform);
464
+ const raw = outputDir || ".";
465
+ return api.isAbsolute(raw) ? api.normalize(raw) : api.resolve(cwd, raw);
466
+ }
467
+ function recordAgentDocsWorkspace(outputDir, {
468
+ env = process.env,
469
+ platform = process.platform,
470
+ cwd = process.cwd(),
471
+ now = /* @__PURE__ */ new Date()
472
+ } = {}) {
473
+ const workspacePath = resolveWorkspacePath(outputDir, { cwd, platform });
436
474
  const config = loadConfig(env, platform);
437
- const lastUpdate = Date.parse(config.lastManagedUpdateAt || "");
438
- if (Number.isFinite(lastUpdate) && Date.now() - lastUpdate < AUTO_UPDATE_INTERVAL_MS) {
439
- return false;
440
- }
441
- const registry = registryConfig(config, env);
442
- if (!registry.token) {
475
+ const existing = Array.isArray(config[DOC_WORKSPACES_KEY]) ? config[DOC_WORKSPACES_KEY] : [];
476
+ const workspaces = [workspacePath, ...existing.filter((entry) => entry !== workspacePath)];
477
+ const next = {
478
+ ...config,
479
+ [DOC_WORKSPACES_KEY]: workspaces,
480
+ lastAgentDocsInitAt: now.toISOString()
481
+ };
482
+ saveConfig(next, env, platform);
483
+ return next;
484
+ }
485
+ function recordForwardedDocsCommand(args, code, options = {}) {
486
+ if (code !== 0 || !commandWritesAgentDocs(args)) {
443
487
  return false;
444
488
  }
445
- const pythonPath = venvPython(venvPath, platform);
446
- if (!import_node_fs5.default.existsSync(pythonPath)) {
447
- return false;
489
+ recordAgentDocsWorkspace(initOutputDir(args), options);
490
+ return true;
491
+ }
492
+ function refreshAgentDocsWorkspaces({
493
+ config,
494
+ venvPath,
495
+ env = process.env,
496
+ platform = process.platform,
497
+ run: run2 = runInherit,
498
+ now = /* @__PURE__ */ new Date()
499
+ } = {}) {
500
+ const currentConfig = config || loadConfig(env, platform);
501
+ const workspaces = Array.isArray(currentConfig[DOC_WORKSPACES_KEY]) ? [...new Set(currentConfig[DOC_WORKSPACES_KEY].filter((entry) => typeof entry === "string" && entry.trim()))] : [];
502
+ if (workspaces.length === 0) {
503
+ saveConfig(currentConfig, env, platform);
504
+ return { config: currentConfig, refreshed: 0, failed: 0 };
448
505
  }
449
- try {
450
- console.error("[vendian] Checking managed CLI/runtime updates...");
451
- installVendianPackages({ pythonPath, venvPath, config, env });
452
- saveConfig({ ...config, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() }, env, platform);
453
- return true;
454
- } catch (error) {
455
- const message = error && typeof error.message === "string" ? error.message : String(error);
456
- console.error(`[vendian] Update check failed; continuing with installed CLI. ${message}`);
457
- return false;
506
+ const vendianPath = venvVendian(venvPath, platform);
507
+ let refreshed = 0;
508
+ let failed = 0;
509
+ for (const workspace of workspaces) {
510
+ if (!import_node_fs5.default.existsSync(workspace)) {
511
+ continue;
512
+ }
513
+ const status = run2(vendianPath, ["init", "--output-dir", workspace, "--yes"]);
514
+ if (status === 0) {
515
+ refreshed += 1;
516
+ } else {
517
+ failed += 1;
518
+ console.error(`[vendian] Could not refresh SDK docs in ${workspace}`);
519
+ }
458
520
  }
521
+ const next = {
522
+ ...currentConfig,
523
+ [DOC_WORKSPACES_KEY]: workspaces,
524
+ lastAgentDocsRefreshAt: now.toISOString()
525
+ };
526
+ saveConfig(next, env, platform);
527
+ return { config: next, refreshed, failed };
459
528
  }
460
529
 
461
530
  // src/setup.js
@@ -465,7 +534,7 @@ var import_node_fs7 = __toESM(require("node:fs"), 1);
465
534
  var import_node_crypto = __toESM(require("node:crypto"), 1);
466
535
  var import_node_http = __toESM(require("node:http"), 1);
467
536
  var import_node_fs6 = __toESM(require("node:fs"), 1);
468
- var import_node_path4 = __toESM(require("node:path"), 1);
537
+ var import_node_path5 = __toESM(require("node:path"), 1);
469
538
  var import_node_child_process3 = require("node:child_process");
470
539
  var DEFAULT_OAUTH_REDIRECT_PORT = 8765;
471
540
  function resolveApiUrl({ backend, apiUrl, env = process.env } = {}) {
@@ -509,6 +578,15 @@ async function loginWithVendianOAuth({ backend, apiUrl, noBrowser = false, env =
509
578
  await callback.close();
510
579
  }
511
580
  }
581
+ async function fetchPackageCredentials({ apiUrl, accessToken }) {
582
+ if (!apiUrl || !accessToken) {
583
+ return void 0;
584
+ }
585
+ const payload = await getJson(`${String(apiUrl).replace(/\/$/, "")}/api/v1/cli/package-credentials`, {
586
+ Authorization: `Bearer ${accessToken}`
587
+ });
588
+ return payload.packageCredentials;
589
+ }
512
590
  function loadCloudConfig(env = process.env, platform = process.platform) {
513
591
  const file = cloudConfigPath(env, platform);
514
592
  try {
@@ -533,6 +611,21 @@ function cloudAuthStatus({ backend, apiUrl, env = process.env, platform = proces
533
611
  profiles
534
612
  };
535
613
  }
614
+ function activeCloudAuthStatus({ env = process.env, platform = process.platform } = {}) {
615
+ const config = loadCloudConfig(env, platform);
616
+ const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
617
+ const apiUrl = typeof config.active_api_url === "string" ? config.active_api_url : void 0;
618
+ const profile = apiUrl ? profiles[apiUrl] : void 0;
619
+ return {
620
+ apiUrl,
621
+ activeApiUrl: apiUrl,
622
+ profile: profile && typeof profile === "object" ? profile : void 0,
623
+ authenticated: Boolean(profile?.access_token),
624
+ email: typeof profile?.email === "string" ? profile.email : void 0,
625
+ expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
626
+ profiles
627
+ };
628
+ }
536
629
  async function getOAuthConfig(apiUrl, redirectUri) {
537
630
  const url = new URL(`${apiUrl}/api/v1/cli/auth/oauth/config`);
538
631
  url.searchParams.set("redirectUri", redirectUri);
@@ -564,8 +657,8 @@ async function exchangeCodeForClerkToken(config, code, codeVerifier, redirectUri
564
657
  }
565
658
  return String(token);
566
659
  }
567
- async function getJson(url) {
568
- const response = await fetch(url, { headers: { Accept: "application/json" } });
660
+ async function getJson(url, headers = {}) {
661
+ const response = await fetch(url, { headers: { Accept: "application/json", ...headers } });
569
662
  return decodeResponse(response);
570
663
  }
571
664
  async function postJson(url, body) {
@@ -655,24 +748,30 @@ function openBrowser(url) {
655
748
  return;
656
749
  }
657
750
  if (platform === "darwin") {
658
- (0, import_node_child_process3.spawnSync)("open", [url], { stdio: "ignore", shell: false });
751
+ (0, import_node_child_process3.spawnSync)(...buildMacOpenCommand(url), { stdio: "ignore", shell: false });
659
752
  return;
660
753
  }
661
- (0, import_node_child_process3.spawnSync)("xdg-open", [url], { stdio: "ignore", shell: false });
754
+ (0, import_node_child_process3.spawnSync)(...buildLinuxOpenCommand(url), { stdio: "ignore", shell: false });
662
755
  }
663
756
  function buildWindowsOpenCommand(url) {
664
757
  return ["rundll32.exe", ["url.dll,FileProtocolHandler", String(url)]];
665
758
  }
759
+ function buildMacOpenCommand(url) {
760
+ return ["open", [String(url)]];
761
+ }
762
+ function buildLinuxOpenCommand(url) {
763
+ return ["xdg-open", [String(url)]];
764
+ }
666
765
  function cloudConfigPath(env = process.env, platform = process.platform) {
667
766
  if (env.VENDIAN_CLOUD_CONFIG) {
668
- return import_node_path4.default.resolve(env.VENDIAN_CLOUD_CONFIG);
767
+ return import_node_path5.default.resolve(env.VENDIAN_CLOUD_CONFIG);
669
768
  }
670
769
  if (platform === "win32") {
671
- const root2 = env.APPDATA || import_node_path4.default.join(process.env.USERPROFILE || "", "AppData", "Roaming");
672
- return import_node_path4.default.win32.join(root2, "Vendian", "cloud-auth.json");
770
+ const root2 = env.APPDATA || import_node_path5.default.join(process.env.USERPROFILE || "", "AppData", "Roaming");
771
+ return import_node_path5.default.win32.join(root2, "Vendian", "cloud-auth.json");
673
772
  }
674
- const root = env.XDG_CONFIG_HOME || import_node_path4.default.join(process.env.HOME || "", ".config");
675
- return import_node_path4.default.posix.join(root, "vendian", "cloud-auth.json");
773
+ const root = env.XDG_CONFIG_HOME || import_node_path5.default.join(process.env.HOME || "", ".config");
774
+ return import_node_path5.default.posix.join(root, "vendian", "cloud-auth.json");
676
775
  }
677
776
  function saveCloudToken(token, { env = process.env, platform = process.platform } = {}) {
678
777
  const file = cloudConfigPath(env, platform);
@@ -694,7 +793,7 @@ function saveCloudToken(token, { env = process.env, platform = process.platform
694
793
  scopes: token.scopes,
695
794
  tooling_eligible: token.toolingEligible
696
795
  };
697
- import_node_fs6.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
796
+ import_node_fs6.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true });
698
797
  import_node_fs6.default.writeFileSync(file, `${JSON.stringify(raw, null, 2)}
699
798
  `, { encoding: "utf8", mode: 384 });
700
799
  try {
@@ -739,24 +838,12 @@ async function setup({
739
838
  throw new Error("Vendian login succeeded, but the backend did not return package credentials. Configure CLI_PACKAGE_REGISTRY_TOKEN on the backend.");
740
839
  }
741
840
  if (packageCredentials) {
742
- next.gitlabHost = packageCredentials.gitlabHost || next.gitlabHost;
743
- next.gitlabUsername = packageCredentials.username || next.gitlabUsername;
744
- next.sdkPublicProjectId = packageCredentials.sdkProjectId || next.sdkPublicProjectId;
745
- next.sdkRuntimeProjectId = packageCredentials.runtimeProjectId || next.sdkRuntimeProjectId;
746
- next.vendianAgentsVersion = packageCredentials.vendianAgentsVersion || next.vendianAgentsVersion;
747
- next.vendianAgentsRuntimeVersion = packageCredentials.vendianAgentsRuntimeVersion || next.vendianAgentsRuntimeVersion;
748
- const secret = savePackageTokenSecret(packageCredentials.token, next, platform);
749
- if (secret.ok) {
750
- Object.assign(next, secret.config || {});
751
- delete next.gitlabToken;
752
- console.log("Package access saved.");
753
- } else {
754
- next.gitlabToken = packageCredentials.token;
755
- console.log("Package access saved in the local Vendian CLI config file.");
756
- }
841
+ savePackageCredentials(next, packageCredentials, { platform });
757
842
  }
758
843
  } else if (auth.authenticated) {
759
844
  console.log(`Cloud authentication already saved for ${auth.apiUrl}${auth.email ? ` (${auth.email})` : ""}.`);
845
+ const refreshed = await refreshPackageAccessFromCloudAuth({ config: next, auth, env, platform });
846
+ Object.assign(next, refreshed.config);
760
847
  } else if (registry.token) {
761
848
  if (registry.tokenSource !== "secret-store") {
762
849
  next.gitlabToken = registry.token;
@@ -777,7 +864,8 @@ async function setup({
777
864
  console.log(`Python: ${pythonVersion(pythonPath) || pythonPath}`);
778
865
  saveConfig(next, env, platform);
779
866
  installVendianPackages({ pythonPath, venvPath, config: next, env, platform });
780
- saveConfig({ ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() }, env, platform);
867
+ const updated = { ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
868
+ refreshAgentDocsWorkspaces({ config: updated, venvPath, env, platform });
781
869
  if (!verifyVendianImports(pythonPath)) {
782
870
  throw new Error("Vendian packages installed, but import verification failed.");
783
871
  }
@@ -790,6 +878,57 @@ async function setup({
790
878
  console.log(line);
791
879
  }
792
880
  }
881
+ function savePackageCredentials(config, packageCredentials, { platform = process.platform } = {}) {
882
+ if (!packageCredentials?.token) {
883
+ return false;
884
+ }
885
+ config.gitlabHost = packageCredentials.gitlabHost || config.gitlabHost;
886
+ config.gitlabUsername = packageCredentials.username || config.gitlabUsername;
887
+ config.sdkPublicProjectId = packageCredentials.sdkProjectId || config.sdkPublicProjectId;
888
+ config.sdkRuntimeProjectId = packageCredentials.runtimeProjectId || config.sdkRuntimeProjectId;
889
+ config.vendianAgentsVersion = packageCredentials.vendianAgentsVersion || config.vendianAgentsVersion;
890
+ config.vendianAgentsRuntimeVersion = packageCredentials.vendianAgentsRuntimeVersion || config.vendianAgentsRuntimeVersion;
891
+ const secret = savePackageTokenSecret(packageCredentials.token, config, platform);
892
+ if (secret.ok) {
893
+ Object.assign(config, secret.config || {});
894
+ delete config.gitlabToken;
895
+ console.log("Package access saved.");
896
+ } else {
897
+ config.gitlabToken = packageCredentials.token;
898
+ console.log("Package access saved in the local Vendian CLI config file.");
899
+ }
900
+ return true;
901
+ }
902
+ async function refreshPackageAccessFromCloudAuth({
903
+ config,
904
+ auth,
905
+ env = process.env,
906
+ platform = process.platform,
907
+ save = true
908
+ } = {}) {
909
+ const currentConfig = config || loadConfig(env, platform);
910
+ const registry = registryConfig(currentConfig, env, platform);
911
+ if (registry.token) {
912
+ return { config: currentConfig, refreshed: false };
913
+ }
914
+ const status = auth || activeCloudAuthStatus({ env, platform });
915
+ if (!status.authenticated || !status.apiUrl || !status.profile?.access_token) {
916
+ return { config: currentConfig, refreshed: false };
917
+ }
918
+ const packageCredentials = await fetchPackageCredentials({
919
+ apiUrl: status.apiUrl,
920
+ accessToken: status.profile.access_token
921
+ });
922
+ if (!packageCredentials?.token) {
923
+ return { config: currentConfig, refreshed: false };
924
+ }
925
+ const next = { ...currentConfig };
926
+ const stored = savePackageCredentials(next, packageCredentials, { platform });
927
+ if (stored && save) {
928
+ saveConfig(next, env, platform);
929
+ }
930
+ return { config: next, refreshed: stored };
931
+ }
793
932
  function setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl } = {}) {
794
933
  const lines = ["Vendian login complete."];
795
934
  if (cloudAuthApiUrl) {
@@ -811,13 +950,98 @@ function cloudAuthLoginCommand({ backend, apiUrl } = {}) {
811
950
  return "vendian cloud auth login";
812
951
  }
813
952
 
953
+ // src/forward.js
954
+ var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
955
+ async function forwardToPythonVendian(args, { env = process.env, platform = process.platform } = {}) {
956
+ const venvPath = managedVenvPath(env, platform);
957
+ const vendianPath = venvVendian(venvPath, platform);
958
+ if (!import_node_fs8.default.existsSync(vendianPath)) {
959
+ throw new Error("Vendian is not set up yet. Run `vendian login` first.");
960
+ }
961
+ const loadedConfig = loadConfig(env, platform);
962
+ const requiresPackageAccess = commandNeedsPackageAccess(args);
963
+ const refreshed = requiresPackageAccess ? await refreshPackageAccessFromCloudAuth({ config: loadedConfig, env, platform }) : { config: loadedConfig };
964
+ maybeAutoUpdateManagedEnv({ env, platform, venvPath });
965
+ const config = refreshed.config;
966
+ const registry = registryConfig(config, env, platform);
967
+ if (requiresPackageAccess && !registry.token) {
968
+ throw new Error("Package access is missing. Run `vendian login` or `vendian update` before starting local agents.");
969
+ }
970
+ const code = await spawnForward(vendianPath, args, {
971
+ env: {
972
+ ...env,
973
+ ...packageIndexEnv(config, env, platform)
974
+ }
975
+ });
976
+ recordForwardedDocsCommand(args, code, { env, platform });
977
+ process.exitCode = code;
978
+ }
979
+ function commandNeedsPackageAccess(args = []) {
980
+ return args[0] === "cloud" && args[1] === "local" && ["serve", "run"].includes(args[2]);
981
+ }
982
+ function packageIndexEnv(config = {}, env = process.env, platform = process.platform) {
983
+ const registry = registryConfig(config, env, platform);
984
+ const indexes = buildIndexUrls(registry);
985
+ if (!indexes) {
986
+ return {};
987
+ }
988
+ const extraIndexUrls = joinIndexUrls([
989
+ indexes.runtimeIndexUrl,
990
+ "https://pypi.org/simple",
991
+ env.VENDIAN_PIP_EXTRA_INDEX_URL
992
+ ]);
993
+ const uvExtraIndexUrls = joinIndexUrls([
994
+ indexes.runtimeIndexUrl,
995
+ "https://pypi.org/simple",
996
+ env.VENDIAN_UV_EXTRA_INDEX_URL
997
+ ]);
998
+ return {
999
+ VENDIAN_PIP_INDEX_URL: indexes.sdkIndexUrl,
1000
+ VENDIAN_PIP_EXTRA_INDEX_URL: extraIndexUrls,
1001
+ VENDIAN_UV_INDEX_URL: indexes.sdkIndexUrl,
1002
+ VENDIAN_UV_EXTRA_INDEX_URL: uvExtraIndexUrls
1003
+ };
1004
+ }
1005
+ function joinIndexUrls(urls) {
1006
+ return urls.flatMap((value) => String(value || "").split(/\s+/)).map((value) => value.trim()).filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(" ");
1007
+ }
1008
+ function maybeAutoUpdateManagedEnv({ env = process.env, platform = process.platform, venvPath = managedVenvPath(env, platform) } = {}) {
1009
+ if (env.VENDIAN_SKIP_AUTO_UPDATE === "1") {
1010
+ return false;
1011
+ }
1012
+ const config = loadConfig(env, platform);
1013
+ const lastUpdate = Date.parse(config.lastManagedUpdateAt || "");
1014
+ if (Number.isFinite(lastUpdate) && Date.now() - lastUpdate < AUTO_UPDATE_INTERVAL_MS) {
1015
+ return false;
1016
+ }
1017
+ const registry = registryConfig(config, env);
1018
+ if (!registry.token) {
1019
+ return false;
1020
+ }
1021
+ const pythonPath = venvPython(venvPath, platform);
1022
+ if (!import_node_fs8.default.existsSync(pythonPath)) {
1023
+ return false;
1024
+ }
1025
+ try {
1026
+ console.error("[vendian] Checking managed CLI/runtime updates...");
1027
+ installVendianPackages({ pythonPath, venvPath, config, env });
1028
+ const next = { ...config, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
1029
+ refreshAgentDocsWorkspaces({ config: next, venvPath, env, platform });
1030
+ return true;
1031
+ } catch (error) {
1032
+ const message = error && typeof error.message === "string" ? error.message : String(error);
1033
+ console.error(`[vendian] Update check failed; continuing with installed CLI. ${message}`);
1034
+ return false;
1035
+ }
1036
+ }
1037
+
814
1038
  // src/tui.js
815
- var import_node_fs8 = __toESM(require("node:fs"), 1);
1039
+ var import_node_fs9 = __toESM(require("node:fs"), 1);
816
1040
  var import_node_readline = __toESM(require("node:readline"), 1);
817
1041
  var import_promises = __toESM(require("node:readline/promises"), 1);
818
1042
 
819
1043
  // src/version.js
820
- var CLI_VERSION = true ? "0.0.5" : process.env.npm_package_version || "0.0.0-dev";
1044
+ var CLI_VERSION = true ? "0.0.7" : process.env.npm_package_version || "0.0.0-dev";
821
1045
 
822
1046
  // src/tui.js
823
1047
  var RESET = "\x1B[0m";
@@ -839,6 +1063,8 @@ var ENDPOINTS = [
839
1063
  ];
840
1064
  var ACTIONS = [
841
1065
  { id: "connect", label: "Connect / switch endpoint", detail: "Sign in to local, dev, staging, production, or a custom API" },
1066
+ { id: "init", label: "Initialize agent docs", detail: "Write SDK-owned authoring docs into a workspace" },
1067
+ { id: "create", label: "Create agent", detail: "Scaffold a new agent through the Python SDK CLI" },
842
1068
  { id: "serve", label: "Start local agent server", detail: "Run agents from ./agents through the managed runtime" },
843
1069
  { id: "doctor", label: "Run doctor", detail: "Check Python, runtime, package access, and local paths" },
844
1070
  { id: "update", label: "Update managed runtime", detail: "Refresh the Vendian runtime packages" },
@@ -923,7 +1149,7 @@ function runtimeSummary({ env = process.env, platform = process.platform, now =
923
1149
  const config = loadConfig(env, platform);
924
1150
  const venvPath = managedVenvPath(env, platform);
925
1151
  const vendianPath = venvVendian(venvPath, platform);
926
- const installed = import_node_fs8.default.existsSync(vendianPath);
1152
+ const installed = import_node_fs9.default.existsSync(vendianPath);
927
1153
  const lastUpdate = Date.parse(config.lastManagedUpdateAt || "");
928
1154
  const stale = !Number.isFinite(lastUpdate) || now - lastUpdate > 24 * 60 * 60 * 1e3;
929
1155
  return {
@@ -937,8 +1163,36 @@ async function runAction(action, { env, platform, input, output }) {
937
1163
  await connectEndpoint({ env, platform, input, output });
938
1164
  return false;
939
1165
  }
1166
+ if (action === "init") {
1167
+ const outputDir = await prompt(input, output, "Workspace directory", ".");
1168
+ output.write(`${CLEAR}${SHOW_CURSOR}`);
1169
+ output.write(`[vendian] Initializing agent docs in ${outputDir || "."}
1170
+
1171
+ `);
1172
+ await forwardToPythonVendian(["init", "--output-dir", outputDir || "."], { env, platform });
1173
+ return true;
1174
+ }
1175
+ if (action === "create") {
1176
+ const name = await prompt(input, output, "Agent name");
1177
+ if (!name) {
1178
+ output.write("Agent name is required.\n");
1179
+ await prompt(input, output, "Press Enter to return");
1180
+ return false;
1181
+ }
1182
+ const outputDir = await prompt(input, output, "Output directory", "./agents");
1183
+ output.write(`${CLEAR}${SHOW_CURSOR}`);
1184
+ output.write(`[vendian] Creating agent ${name} in ${outputDir || "./agents"}
1185
+
1186
+ `);
1187
+ await forwardToPythonVendian(["create", name, "--output-dir", outputDir || "./agents"], { env, platform });
1188
+ return true;
1189
+ }
940
1190
  if (action === "serve") {
941
1191
  const agentsDir = await prompt(input, output, "Agents directory", "./agents");
1192
+ output.write(`${CLEAR}${SHOW_CURSOR}`);
1193
+ output.write(`[vendian] Starting local agent server for ${agentsDir || "./agents"}
1194
+
1195
+ `);
942
1196
  await forwardToPythonVendian(["cloud", "local", "serve", "--agents-dir", agentsDir || "./agents"], { env, platform });
943
1197
  return true;
944
1198
  }
@@ -1001,6 +1255,11 @@ function helpText() {
1001
1255
  "Common commands:",
1002
1256
  " vendian",
1003
1257
  " vendian login",
1258
+ " vendian init --output-dir ./agents",
1259
+ ' vendian create "My Agent" --output-dir ./agents',
1260
+ " vendian validate ./agents/my-agent --runtime",
1261
+ " vendian test ./agents/my-agent --dry-run --mock-credentials",
1262
+ " vendian models",
1004
1263
  " vendian login --backend staging",
1005
1264
  " vendian login --api-url http://localhost:3000",
1006
1265
  " vendian cloud local serve --agents-dir ./agents",
@@ -1065,10 +1324,14 @@ Usage:
1065
1324
  vendian setup Alias for vendian login
1066
1325
  vendian doctor Check local bootstrap health
1067
1326
  vendian update Update the managed Vendian CLI/runtime
1068
- vendian <command> Run a Vendian cloud command
1327
+ vendian init Write current SDK agent docs into a workspace
1328
+ vendian create "My Agent" Scaffold a new agent from SDK templates
1329
+ vendian <command> Run a managed Python SDK/cloud command
1069
1330
 
1070
1331
  Examples:
1071
1332
  vendian login
1333
+ vendian init --output-dir ./agents
1334
+ vendian create "Google Drive Processor" --output-dir ./agents
1072
1335
  vendian login --backend staging
1073
1336
  vendian cloud local serve --agents-dir ./agents
1074
1337
 
@@ -1135,7 +1398,20 @@ function parseSetupOptions(args) {
1135
1398
  return options;
1136
1399
  }
1137
1400
 
1401
+ // src/terminal-title.js
1402
+ function setTerminalTitle(title, { output = process.stdout, processRef = process } = {}) {
1403
+ const safeTitle = String(title).replace(/[\x00-\x1f\x7f]/g, " ").trim();
1404
+ if (!safeTitle) {
1405
+ return;
1406
+ }
1407
+ processRef.title = safeTitle;
1408
+ if (output?.isTTY && typeof output.write === "function") {
1409
+ output.write(`\x1B]0;${safeTitle}\x07`);
1410
+ }
1411
+ }
1412
+
1138
1413
  // src/entry.js
1414
+ setTerminalTitle("Vendian CLI");
1139
1415
  main(process.argv.slice(2)).catch((error) => {
1140
1416
  const message = error && typeof error.message === "string" ? error.message : String(error);
1141
1417
  console.error(`[vendian] ${message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vendian/cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Public Vendian CLI bootstrapper and launcher",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -21,7 +21,8 @@
21
21
  "scripts": {
22
22
  "build": "node scripts/build-package.mjs",
23
23
  "prepack": "npm run build",
24
- "prepublishOnly": "npm test",
24
+ "prepublishOnly": "npm run release:sync-version && npm test",
25
+ "release:sync-version": "node scripts/sync-release-version.mjs",
25
26
  "test": "node --test"
26
27
  },
27
28
  "engines": {