@corvio/admin-cli 0.1.0-beta.7 → 0.1.0-beta.8

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.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  writeResult,
25
25
  } from "./core.js";
26
26
 
27
- const VERSION = "0.1.0-beta.7";
27
+ const VERSION = "0.1.0-beta.8";
28
28
  const DEFAULT_API_BASE = "https://api.corvio.ai/v1";
29
29
  const USER_AGENT = `corvio-admin-cli/${VERSION}`;
30
30
  const GLOBAL_OPTIONS = Object.freeze(["api-base", "json", "no-input", "help", "version"]);
package/dist/core.js CHANGED
@@ -97,6 +97,8 @@ export function parseArgv(argv) {
97
97
  "force",
98
98
  "once",
99
99
  "include-revoked",
100
+ "recover-credential",
101
+ "no-listen",
100
102
  "start-new-chain",
101
103
  "help",
102
104
  "version",
@@ -556,6 +558,14 @@ export function adminCredentialPath(env = process.env) {
556
558
  return join(configRoot(env), "admin-credentials.json");
557
559
  }
558
560
 
561
+ export function agentCredentialPath(env = process.env) {
562
+ return join(configRoot(env), "agent-credentials.json");
563
+ }
564
+
565
+ export function conversationRoutingPath(env = process.env) {
566
+ return join(configRoot(env), "conversation-routing.json");
567
+ }
568
+
559
569
  export async function readWorkspaceCredential(env = process.env) {
560
570
  const environmentKey = String(env.CORVIO_API_KEY || "").trim();
561
571
  if (environmentKey) {
@@ -588,21 +598,72 @@ export async function readWorkspaceCredential(env = process.env) {
588
598
  return { key, source: "device_link", profile: profileName, metadata };
589
599
  }
590
600
 
591
- export function readAgentCredential(env = process.env) {
601
+ export async function readAgentCredential(env = process.env, { required = true } = {}) {
592
602
  const key = String(env.CORVIO_AGENT_API_KEY || "").trim();
593
- if (!key) {
603
+ if (key) {
604
+ if (!key.startsWith("cvg_")) {
605
+ throw new CliError("CORVIO_AGENT_API_KEY must be an Agent cvg_ key.", {
606
+ code: "invalid_agent_key",
607
+ exitCode: EXIT.AUTH,
608
+ });
609
+ }
610
+ return { key, source: "environment", profile: null, metadata: null };
611
+ }
612
+ const path = agentCredentialPath(env);
613
+ let parsed;
614
+ try {
615
+ parsed = JSON.parse(await readFile(path, "utf8"));
616
+ } catch (cause) {
617
+ if (cause?.code === "ENOENT" && !required) return null;
618
+ if (cause?.code === "ENOENT") {
619
+ throw new CliError(
620
+ "No Agent credential found. Run `corvio agents connect` or set CORVIO_AGENT_API_KEY.",
621
+ { code: "agent_auth_required", exitCode: EXIT.AUTH },
622
+ );
623
+ }
624
+ throw new CliError("Stored Agent credentials are unreadable.", {
625
+ code: "agent_credential_store_invalid",
626
+ exitCode: EXIT.AUTH,
627
+ });
628
+ }
629
+ const storedKey = String(parsed?.api_key || "").trim();
630
+ if (!storedKey.startsWith("cvg_")) {
594
631
  throw new CliError(
595
- "No Agent credential found. Set CORVIO_AGENT_API_KEY to the cvg_ secret returned when the Agent was created.",
596
- { code: "agent_auth_required", exitCode: EXIT.AUTH },
632
+ "Stored Agent credentials are invalid; run `corvio agents connect --recover-credential`.",
633
+ { code: "agent_credential_store_invalid", exitCode: EXIT.AUTH },
597
634
  );
598
635
  }
599
- if (!key.startsWith("cvg_")) {
600
- throw new CliError("CORVIO_AGENT_API_KEY must be an Agent cvg_ key.", {
601
- code: "invalid_agent_key",
636
+ return { key: storedKey, source: "secure_store", profile: "default", metadata: parsed };
637
+ }
638
+
639
+ export async function storeAgentCredential(payload, env = process.env) {
640
+ const secret = String(payload?.secret || "").trim();
641
+ if (!secret.startsWith("cvg_")) {
642
+ throw new CliError("Agent connection did not return a valid cvg_ credential.", {
643
+ code: "agent_credential_invalid",
602
644
  exitCode: EXIT.AUTH,
603
645
  });
604
646
  }
605
- return { key, source: "environment", profile: null, metadata: null };
647
+ const path = agentCredentialPath(env);
648
+ const root = dirname(path);
649
+ await mkdir(root, { recursive: true, mode: 0o700 });
650
+ await chmod(root, 0o700).catch(() => {});
651
+ const document = {
652
+ version: 1,
653
+ api_key: secret,
654
+ agent_id: String(payload.agent_id || ""),
655
+ display_name: String(payload.display_name || ""),
656
+ provider: String(payload.provider || "custom"),
657
+ display_prefix: String(payload.display_prefix || "cvg"),
658
+ created_at: payload.created_at || new Date().toISOString(),
659
+ source: "agent_connect",
660
+ };
661
+ const temporary = `${path}.${randomUUID()}.tmp`;
662
+ await writeFile(temporary, `${JSON.stringify(document, null, 2)}\n`, { mode: 0o600, flag: "wx" });
663
+ await chmod(temporary, 0o600);
664
+ await rename(temporary, path);
665
+ await chmod(path, 0o600);
666
+ return { path, profile: "default", metadata: document };
606
667
  }
607
668
 
608
669
  export async function storeWorkspaceCredential(payload, env = process.env) {
@@ -642,6 +703,7 @@ export async function readWorkspaceContext(env = process.env) {
642
703
  workspaceId,
643
704
  workspaceName: String(parsed.active_workspace_name || "").trim() || null,
644
705
  source: "stored",
706
+ routingPreference: parsed.routing_preference || null,
645
707
  };
646
708
  } catch (cause) {
647
709
  if (cause?.code === "ENOENT") return null;
@@ -652,7 +714,7 @@ export async function readWorkspaceContext(env = process.env) {
652
714
  }
653
715
  }
654
716
 
655
- export async function storeWorkspaceContext({ workspaceId, workspaceName = null }, env = process.env) {
717
+ export async function storeWorkspaceContext({ workspaceId, workspaceName = null, routingPreference = null }, env = process.env) {
656
718
  const normalizedId = String(workspaceId || "").trim();
657
719
  if (!normalizedId) throw new CliError("Workspace ID is required.", { code: "workspace_selection_required" });
658
720
  const path = workspaceContextPath(env);
@@ -663,6 +725,13 @@ export async function storeWorkspaceContext({ workspaceId, workspaceName = null
663
725
  version: 1,
664
726
  active_workspace_id: normalizedId,
665
727
  active_workspace_name: String(workspaceName || "").trim() || null,
728
+ routing_preference: routingPreference || {
729
+ workspace_id: normalizedId,
730
+ provenance: "user_confirmed_cli",
731
+ confidence: 1,
732
+ scope: "global",
733
+ expires_at: null,
734
+ },
666
735
  updated_at: new Date().toISOString(),
667
736
  };
668
737
  const temporary = `${path}.${randomUUID()}.tmp`;
@@ -673,6 +742,75 @@ export async function storeWorkspaceContext({ workspaceId, workspaceName = null
673
742
  return { path, workspaceId: normalizedId };
674
743
  }
675
744
 
745
+ export async function readConversationRoutingReceipt(conversationId, env = process.env) {
746
+ const normalizedId = String(conversationId || "").trim();
747
+ if (!normalizedId) return null;
748
+ const path = conversationRoutingPath(env);
749
+ try {
750
+ const parsed = JSON.parse(await readFile(path, "utf8"));
751
+ const receipt = parsed?.receipts?.[normalizedId];
752
+ const workspaceId = String(receipt?.workspace_id || "").trim();
753
+ if (!workspaceId) return null;
754
+ return {
755
+ conversationId: normalizedId,
756
+ workspaceId,
757
+ agentId: String(receipt.agent_id || "").trim() || null,
758
+ apiBase: String(receipt.api_base || "").trim() || null,
759
+ recordedAt: String(receipt.recorded_at || "").trim() || null,
760
+ };
761
+ } catch (cause) {
762
+ if (cause?.code === "ENOENT") return null;
763
+ throw new CliError("Stored Corvio conversation routing receipts are unreadable.", {
764
+ code: "conversation_routing_store_invalid",
765
+ exitCode: EXIT.AUTH,
766
+ });
767
+ }
768
+ }
769
+
770
+ export async function storeConversationRoutingReceipt({ conversationId, workspaceId, agentId = null, apiBase = null }, env = process.env) {
771
+ const normalizedConversationId = String(conversationId || "").trim();
772
+ const normalizedWorkspaceId = String(workspaceId || "").trim();
773
+ if (!normalizedConversationId || !normalizedWorkspaceId) {
774
+ throw new CliError("Conversation and Workspace IDs are required for routing continuity.", {
775
+ code: "conversation_routing_receipt_invalid",
776
+ });
777
+ }
778
+ const path = conversationRoutingPath(env);
779
+ const root = dirname(path);
780
+ await mkdir(root, { recursive: true, mode: 0o700 });
781
+ await chmod(root, 0o700).catch(() => {});
782
+ let existing = {};
783
+ try {
784
+ const parsed = JSON.parse(await readFile(path, "utf8"));
785
+ if (parsed?.receipts && typeof parsed.receipts === "object" && !Array.isArray(parsed.receipts)) {
786
+ existing = parsed.receipts;
787
+ }
788
+ } catch (cause) {
789
+ if (cause?.code !== "ENOENT") {
790
+ throw new CliError("Stored Corvio conversation routing receipts are unreadable.", {
791
+ code: "conversation_routing_store_invalid",
792
+ exitCode: EXIT.AUTH,
793
+ });
794
+ }
795
+ }
796
+ const ordered = Object.entries({
797
+ ...existing,
798
+ [normalizedConversationId]: {
799
+ workspace_id: normalizedWorkspaceId,
800
+ agent_id: String(agentId || "").trim() || null,
801
+ api_base: String(apiBase || "").trim() || null,
802
+ recorded_at: new Date().toISOString(),
803
+ },
804
+ }).sort((left, right) => String(right[1]?.recorded_at || "").localeCompare(String(left[1]?.recorded_at || "")));
805
+ const document = { version: 1, receipts: Object.fromEntries(ordered.slice(0, 500)) };
806
+ const temporary = `${path}.${randomUUID()}.tmp`;
807
+ await writeFile(temporary, `${JSON.stringify(document, null, 2)}\n`, { mode: 0o600, flag: "wx" });
808
+ await chmod(temporary, 0o600);
809
+ await rename(temporary, path);
810
+ await chmod(path, 0o600);
811
+ return { path, conversationId: normalizedConversationId, workspaceId: normalizedWorkspaceId };
812
+ }
813
+
676
814
  export async function clearWorkspaceContext(env = process.env) {
677
815
  const path = workspaceContextPath(env);
678
816
  await rm(path, { force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corvio/admin-cli",
3
- "version": "0.1.0-beta.7",
3
+ "version": "0.1.0-beta.8",
4
4
  "description": "Official read-only Corvio platform administrator CLI.",
5
5
  "type": "module",
6
6
  "bin": {