@ouro.bot/cli 0.1.0-alpha.464 → 0.1.0-alpha.466

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
@@ -180,6 +180,9 @@ ouro vault unlock --agent <name>
180
180
  ouro vault status --agent <name>
181
181
  ouro vault config set --agent <name> --key teams.clientSecret
182
182
  ouro vault config status --agent <name> --scope all
183
+ ouro vault item set --agent <name> --item <path> --secret-field <field>
184
+ ouro vault item status --agent <name> --item <path>
185
+ ouro vault ops porkbun set --agent <name> --account <account>
183
186
  ouro connect --agent <name>
184
187
  ouro connect providers --agent <name>
185
188
  ouro connect perplexity --agent <name>
@@ -207,6 +210,8 @@ ouro mcp-serve --agent <name> # start MCP server on stdin/stdout (us
207
210
  ouro hook <event> --agent <name> # fire a lifecycle hook (SessionStart, Stop, PostToolUse)
208
211
  ```
209
212
 
213
+ The generic secret primitive is a vault item / credential in the owning agent vault: stable item name/path, hidden secret material, optional public fields, notes, timestamps/provenance, and no assumed use. `ouro connect` is for harness-managed workflows; workflow bindings reference ordinary vault items when they need secret material.
214
+
210
215
  ## Setting Up On Another Machine
211
216
 
212
217
  To clone an existing agent onto a new machine (macOS, Linux, or Windows via WSL2), see **[docs/cross-machine-setup.md](docs/cross-machine-setup.md)**. The short version is bundle plus vault: `npx ouro.bot@latest`, open the home deck, choose clone, enter the bundle's git remote URL, unlock the agent vault, refresh/verify credentials, and start with `ouro up`.
package/changelog.json CHANGED
@@ -1,6 +1,22 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.466",
6
+ "changes": [
7
+ "`ouro vault item set/status/list` is now the generic human-facing path for storing ordinary agent-owned vault items with hidden secret fields, optional public fields, freeform notes, metadata-only status/list output, and no assumed provider semantics.",
8
+ "`ouro vault ops porkbun` remains as a deprecated compatibility alias over ordinary vault items, while docs and help teach vault item -> managed workflow -> non-secret binding instead of treating Porkbun, DNS, or ops credentials as separate credential species.",
9
+ "Vault item guardrails now keep harness-managed provider/runtime items on `ouro auth`, `ouro connect`, and `ouro vault config`, and coverage locks no-secret logging, notes-as-orientation, reserved item errors, compatibility behavior, and 100% branch coverage for the new surface."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.465",
14
+ "changes": [
15
+ "`ouro vault ops porkbun set` now stores account-scoped Porkbun API credentials in the owning agent vault through hidden prompts, outside the connect bay and runtime/config.",
16
+ "Porkbun ops credentials are named by their true authority boundary at `ops/registrars/porkbun/accounts/<account>`, so multiple Porkbun accounts can coexist and domain allowlists remain separate.",
17
+ "Auth/provider docs now clarify that `ouro connect` is only for harness-managed capabilities, while registrar and deployment credentials belong in explicit `ops/...` vault items."
18
+ ]
19
+ },
4
20
  {
5
21
  "version": "0.1.0-alpha.464",
6
22
  "changes": [
@@ -103,6 +103,7 @@ const provider_ping_1 = require("../provider-ping");
103
103
  const agent_discovery_1 = require("./agent-discovery");
104
104
  const connect_bay_1 = require("./connect-bay");
105
105
  const runtime_capability_check_1 = require("../runtime-capability-check");
106
+ const vault_items_1 = require("./vault-items");
106
107
  // ── ensureDaemonRunning ──
107
108
  const DEFAULT_DAEMON_STARTUP_TIMEOUT_MS = 60_000;
108
109
  const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
@@ -325,6 +326,9 @@ function agentResolutionFailureMode(command) {
325
326
  case "vault.status":
326
327
  case "vault.config.set":
327
328
  case "vault.config.status":
329
+ case "vault.item.set":
330
+ case "vault.item.status":
331
+ case "vault.item.list":
328
332
  case "connect":
329
333
  case "account.ensure":
330
334
  case "mail.import-mbox":
@@ -2009,6 +2013,170 @@ async function executeVaultConfigStatus(command, deps) {
2009
2013
  deps.writeStdout(message);
2010
2014
  return message;
2011
2015
  }
2016
+ function parseVaultItemPublicFields(fields) {
2017
+ const parsed = {};
2018
+ for (const field of fields ?? []) {
2019
+ const separator = field.indexOf("=");
2020
+ const key = (0, vault_items_1.normalizeVaultItemFieldName)(field.slice(0, separator));
2021
+ parsed[key] = field.slice(separator + 1);
2022
+ }
2023
+ return parsed;
2024
+ }
2025
+ function uniqueVaultItemSecretFields(command) {
2026
+ const fields = command.template ? (0, vault_items_1.vaultItemTemplateSecretFields)(command.template) : [];
2027
+ for (const field of command.secretFields ?? []) {
2028
+ if (!fields.includes(field))
2029
+ fields.push(field);
2030
+ }
2031
+ return fields;
2032
+ }
2033
+ function vaultItemCompatibilityNotice(command) {
2034
+ if (command.compatibilityAlias !== vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS)
2035
+ return [];
2036
+ return ["deprecated compatibility alias: use ouro vault item set --template porkbun-api"];
2037
+ }
2038
+ function freeformVaultItemReservedMessage(itemName) {
2039
+ if (itemName.startsWith("providers/")) {
2040
+ return `Vault item "${itemName}" is reserved for harness-managed workflows. Use ouro auth or ouro connect for provider credentials.`;
2041
+ }
2042
+ if (itemName === "runtime/config" || /^runtime\/machines\/[^/]+\/config$/.test(itemName)) {
2043
+ return `Vault item "${itemName}" is reserved for harness-managed workflows. Use ouro connect or ouro vault config for runtime and sense configuration.`;
2044
+ }
2045
+ return undefined;
2046
+ }
2047
+ function assertFreeformVaultItemWritable(itemName) {
2048
+ const reservedMessage = freeformVaultItemReservedMessage(itemName);
2049
+ if (reservedMessage)
2050
+ throw new Error(reservedMessage);
2051
+ }
2052
+ function porkbunAccountForCompatibility(command) {
2053
+ if (command.compatibilityAlias !== vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS)
2054
+ return undefined;
2055
+ return (0, vault_items_1.normalizePorkbunOpsAccount)((0, vault_items_1.porkbunOpsAccountFromItemName)(command.item));
2056
+ }
2057
+ const PORKBUN_OPS_PROMPT_LABELS = {
2058
+ apiKey: (account) => `Porkbun API key for ${account}: `,
2059
+ secretApiKey: (account) => `Porkbun Secret API key for ${account}: `,
2060
+ };
2061
+ const PORKBUN_OPS_VALIDATION_LABELS = {
2062
+ apiKey: "Porkbun API key",
2063
+ secretApiKey: "Porkbun Secret API key",
2064
+ };
2065
+ function vaultItemTemplatePromptLabel(command, field, account) {
2066
+ if (command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS) {
2067
+ return PORKBUN_OPS_PROMPT_LABELS[field](account);
2068
+ }
2069
+ return `Secret field ${field} for ${command.item}: `;
2070
+ }
2071
+ function vaultItemSecretValidationLabel(command, field) {
2072
+ if (command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS) {
2073
+ return PORKBUN_OPS_VALIDATION_LABELS[field];
2074
+ }
2075
+ return `Secret field ${field}`;
2076
+ }
2077
+ async function executeVaultItemSet(command, deps) {
2078
+ if (command.agent === "SerpentGuide") {
2079
+ throw new Error("SerpentGuide has no persistent credential vault. Store vault items in the owning agent vault.");
2080
+ }
2081
+ if (!command.compatibilityAlias)
2082
+ assertFreeformVaultItemWritable(command.item);
2083
+ const account = porkbunAccountForCompatibility(command);
2084
+ const promptSecret = requirePromptSecret(deps, command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS ? "Porkbun ops credential entry" : "Vault item secret entry");
2085
+ const secretFieldNames = uniqueVaultItemSecretFields(command);
2086
+ const publicFields = parseVaultItemPublicFields(command.publicFields);
2087
+ if (account && !publicFields.account)
2088
+ publicFields.account = account;
2089
+ const secretFields = {};
2090
+ for (const field of secretFieldNames) {
2091
+ const normalized = (0, vault_items_1.normalizeVaultItemFieldName)(field);
2092
+ const value = await promptSecret(vaultItemTemplatePromptLabel(command, normalized, account));
2093
+ secretFields[normalized] = (0, vault_items_1.requireVaultItemSecret)(value, vaultItemSecretValidationLabel(command, normalized));
2094
+ }
2095
+ const payload = {
2096
+ schemaVersion: 1,
2097
+ updatedAt: providerCliNow(deps).toISOString(),
2098
+ publicFields,
2099
+ secretFields,
2100
+ };
2101
+ const progress = createHumanCommandProgress(deps, command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS ? "vault ops porkbun" : "vault item set");
2102
+ try {
2103
+ await runCommandProgressPhase(progress, command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS ? "storing ordinary vault item" : "storing vault item", async () => {
2104
+ const store = (0, credential_access_1.getCredentialStore)(command.agent);
2105
+ await store.store(command.item, {
2106
+ ...(account ? { username: account } : {}),
2107
+ password: JSON.stringify(payload),
2108
+ ...(command.note !== undefined
2109
+ ? { notes: command.note }
2110
+ : command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS
2111
+ ? { notes: "Ordinary vault item written by a deprecated Porkbun compatibility alias. Notes are for human/agent orientation only; workflow bindings live outside this item." }
2112
+ : {}),
2113
+ });
2114
+ return command.item;
2115
+ }, () => "secret stored");
2116
+ }
2117
+ finally {
2118
+ progress.end();
2119
+ }
2120
+ const message = [
2121
+ ...vaultItemCompatibilityNotice(command),
2122
+ `stored ordinary vault item for ${command.agent}`,
2123
+ `item: vault:${command.agent}:${command.item}`,
2124
+ ...(account ? [`account: ${account}`] : []),
2125
+ `public fields: ${Object.keys(publicFields).length === 0 ? "none" : Object.keys(publicFields).sort().join(", ")}`,
2126
+ `secret fields: ${Object.keys(secretFields).sort().join(", ")}`,
2127
+ `notes: ${command.note !== undefined || command.compatibilityAlias === vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS ? "present" : "absent"}`,
2128
+ "secret values were not printed",
2129
+ ].join("\n");
2130
+ deps.writeStdout(message);
2131
+ return message;
2132
+ }
2133
+ async function executeVaultItemStatus(command, deps) {
2134
+ if (command.agent === "SerpentGuide") {
2135
+ const message = "SerpentGuide has no persistent credential vault. Vault items belong in the owning agent vault.";
2136
+ deps.writeStdout(message);
2137
+ return message;
2138
+ }
2139
+ const store = (0, credential_access_1.getCredentialStore)(command.agent);
2140
+ const account = porkbunAccountForCompatibility(command);
2141
+ const meta = await store.get(command.item);
2142
+ const lines = [
2143
+ ...vaultItemCompatibilityNotice(command),
2144
+ `agent: ${command.agent}`,
2145
+ `${command.compatibilityAlias ? "ordinary vault item" : "item"}: vault:${command.agent}:${command.item}`,
2146
+ `status: ${meta ? "present" : "missing"}`,
2147
+ ];
2148
+ if (meta?.username)
2149
+ lines.push(account ? `account: ${meta.username}` : `username: ${meta.username}`);
2150
+ if (meta?.notes)
2151
+ lines.push("notes: present");
2152
+ lines.push("secret values were not printed");
2153
+ const message = lines.join("\n");
2154
+ deps.writeStdout(message);
2155
+ return message;
2156
+ }
2157
+ async function executeVaultItemList(command, deps) {
2158
+ if (command.agent === "SerpentGuide") {
2159
+ const message = "SerpentGuide has no persistent credential vault. Vault items belong in the owning agent vault.";
2160
+ deps.writeStdout(message);
2161
+ return message;
2162
+ }
2163
+ const store = (0, credential_access_1.getCredentialStore)(command.agent);
2164
+ const prefix = command.prefix;
2165
+ const items = (await store.list())
2166
+ .filter((item) => !prefix || item.domain.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`))
2167
+ .map((item) => item.domain)
2168
+ .sort();
2169
+ const lines = [
2170
+ ...vaultItemCompatibilityNotice(command),
2171
+ `agent: ${command.agent}`,
2172
+ ...(prefix ? [`prefix: ${prefix}`] : []),
2173
+ `items: ${items.length === 0 ? "none stored" : items.join(", ")}`,
2174
+ "secret values were not printed",
2175
+ ];
2176
+ const message = lines.join("\n");
2177
+ deps.writeStdout(message);
2178
+ return message;
2179
+ }
2012
2180
  function requirePromptSecret(deps, purpose) {
2013
2181
  if (deps.promptSecret)
2014
2182
  return deps.promptSecret;
@@ -5354,6 +5522,15 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
5354
5522
  if (command.kind === "vault.config.status") {
5355
5523
  return executeVaultConfigStatus(command, deps);
5356
5524
  }
5525
+ if (command.kind === "vault.item.set") {
5526
+ return executeVaultItemSet(command, deps);
5527
+ }
5528
+ if (command.kind === "vault.item.status") {
5529
+ return executeVaultItemStatus(command, deps);
5530
+ }
5531
+ if (command.kind === "vault.item.list") {
5532
+ return executeVaultItemList(command, deps);
5533
+ }
5357
5534
  // ── auth (local, no daemon socket needed) ──
5358
5535
  if (command.kind === "auth.run") {
5359
5536
  return executeAuthRun(command, deps);
@@ -212,9 +212,9 @@ exports.COMMAND_REGISTRY = {
212
212
  vault: {
213
213
  category: "Auth",
214
214
  description: "Create, replace, recover, unlock, inspect, and populate the agent credential vault",
215
- usage: "ouro vault <create|replace|recover|unlock|status|config> [--agent <name>]",
215
+ usage: "ouro vault <create|replace|recover|unlock|status|config|item|ops> [--agent <name>]",
216
216
  example: "ouro vault status",
217
- subcommands: ["create", "replace", "recover", "unlock", "status", "config set", "config status"],
217
+ subcommands: ["create", "replace", "recover", "unlock", "status", "config set", "config status", "vault item set", "vault item status", "vault item list", "vault ops porkbun set", "vault ops porkbun status"],
218
218
  },
219
219
  thoughts: {
220
220
  category: "Internal",
@@ -366,6 +366,31 @@ const SUBCOMMAND_HELP = {
366
366
  usage: "ouro vault config status [--agent <name>] [--scope agent|machine|all]",
367
367
  example: "ouro vault config status --scope all",
368
368
  },
369
+ "vault item set": {
370
+ description: "Store an ordinary vault item / credential with no assumed use. Prompts for hidden secret fields, stores optional public fields and notes, and secret values are not printed.",
371
+ usage: "ouro vault item set [--agent <name>] --item <path> (--secret-field <name>...|--template <template>) [--public-field <key=value>] [--note <text>]",
372
+ example: "ouro vault item set --agent slugger --item ops/porkbun/ari@mendelow.me --template porkbun-api",
373
+ },
374
+ "vault item status": {
375
+ description: "Show metadata for an ordinary vault item without printing secret values",
376
+ usage: "ouro vault item status [--agent <name>] --item <path>",
377
+ example: "ouro vault item status --agent slugger --item ops/porkbun/ari@mendelow.me",
378
+ },
379
+ "vault item list": {
380
+ description: "List ordinary vault item names and metadata without printing secret values",
381
+ usage: "ouro vault item list [--agent <name>] [--prefix <path-prefix>]",
382
+ example: "ouro vault item list --agent slugger --prefix ops/",
383
+ },
384
+ "vault ops porkbun set": {
385
+ description: "deprecated compatibility alias for `ouro vault item set --template porkbun-api`; stores an ordinary vault item, not a special credential kind",
386
+ usage: "ouro vault ops porkbun set [--agent <name>] --account <account>",
387
+ example: "ouro vault ops porkbun set --agent slugger --account ari@mendelow.me",
388
+ },
389
+ "vault ops porkbun status": {
390
+ description: "deprecated compatibility alias for checking the ordinary vault item used by the Porkbun API template",
391
+ usage: "ouro vault ops porkbun status [--agent <name>] [--account <account>]",
392
+ example: "ouro vault ops porkbun status --agent slugger --account ari@mendelow.me",
393
+ },
369
394
  };
370
395
  // ── Levenshtein distance ──
371
396
  function levenshteinDistance(a, b) {
@@ -16,6 +16,7 @@ exports.parseMcpServeCommand = parseMcpServeCommand;
16
16
  exports.parseOuroCommand = parseOuroCommand;
17
17
  const types_1 = require("../../mind/friends/types");
18
18
  const cli_help_1 = require("./cli-help");
19
+ const vault_items_1 = require("./vault-items");
19
20
  // ── Shared helpers ──
20
21
  function extractAgentFlag(args) {
21
22
  const idx = args.indexOf("--agent");
@@ -95,6 +96,11 @@ function usage() {
95
96
  " ouro vault status [--agent <name>] [--store auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file]",
96
97
  " ouro vault config set [--agent <name>] --key <path> [--value <value>] [--scope agent|machine]",
97
98
  " ouro vault config status [--agent <name>] [--scope agent|machine|all]",
99
+ " ouro vault item set [--agent <name>] --item <path> --secret-field <name> [--public-field <key=value>] [--note <text>]",
100
+ " ouro vault item status [--agent <name>] --item <path>",
101
+ " ouro vault item list [--agent <name>] [--prefix <path-prefix>]",
102
+ " ouro vault ops porkbun set [--agent <name>] --account <account>",
103
+ " ouro vault ops porkbun status [--agent <name>] [--account <account>]",
98
104
  " ouro chat <agent>",
99
105
  " ouro msg --to <agent> [--session <id>] [--task <ref>] <message>",
100
106
  " ouro poke <agent> --task <task-id>",
@@ -458,6 +464,10 @@ function parseVaultCommand(args) {
458
464
  const sub = args[0];
459
465
  if (sub === "config")
460
466
  return parseVaultConfigCommand(args.slice(1));
467
+ if (sub === "item")
468
+ return parseVaultItemCommand(args.slice(1));
469
+ if (sub === "ops")
470
+ return parseVaultOpsCommand(args.slice(1));
461
471
  const { agent, rest } = extractAgentFlag(args.slice(1));
462
472
  let email;
463
473
  let serverUrl;
@@ -544,6 +554,133 @@ function parseVaultCommand(args) {
544
554
  }
545
555
  return { kind: "vault.status", ...(agent ? { agent } : {}), ...(store ? { store } : {}) };
546
556
  }
557
+ function parseVaultItemCommand(args) {
558
+ const action = args[0];
559
+ if (action !== "set" && action !== "status" && action !== "list") {
560
+ throw new Error("Usage: ouro vault item set|status|list [--agent <name>] --item <path>");
561
+ }
562
+ const { agent, rest } = extractAgentFlag(args.slice(1));
563
+ let item;
564
+ let prefix;
565
+ let template;
566
+ let note;
567
+ const secretFields = [];
568
+ const publicFields = [];
569
+ for (let i = 0; i < rest.length; i += 1) {
570
+ const token = rest[i];
571
+ if (token === "--item") {
572
+ item = (0, vault_items_1.normalizeVaultItemName)(rest[i + 1]);
573
+ i += 1;
574
+ continue;
575
+ }
576
+ if (token === "--prefix") {
577
+ const value = rest[i + 1]?.trim() ?? "";
578
+ if (!value || /[\r\n\t]/.test(value) || value.startsWith("/")) {
579
+ throw new Error("Vault item prefix must be non-empty, relative, and free of control characters.");
580
+ }
581
+ prefix = value;
582
+ i += 1;
583
+ continue;
584
+ }
585
+ if (token === "--template") {
586
+ const value = rest[i + 1];
587
+ if (!(0, vault_items_1.isVaultItemTemplate)(value)) {
588
+ throw new Error("vault item --template must be porkbun-api");
589
+ }
590
+ template = value;
591
+ i += 1;
592
+ continue;
593
+ }
594
+ if (token === "--secret-field") {
595
+ secretFields.push((0, vault_items_1.normalizeVaultItemFieldName)(rest[i + 1]));
596
+ i += 1;
597
+ continue;
598
+ }
599
+ if (token === "--public-field") {
600
+ const value = rest[i + 1]?.trim() ?? "";
601
+ const separator = value.indexOf("=");
602
+ if (separator <= 0 || separator === value.length - 1) {
603
+ throw new Error("vault item --public-field must be key=value");
604
+ }
605
+ (0, vault_items_1.normalizeVaultItemFieldName)(value.slice(0, separator));
606
+ publicFields.push(value);
607
+ i += 1;
608
+ continue;
609
+ }
610
+ if (token === "--note") {
611
+ note = rest[i + 1] ?? "";
612
+ i += 1;
613
+ continue;
614
+ }
615
+ throw new Error(`Usage: ouro vault item ${action} [--agent <name>] --item <path>`);
616
+ }
617
+ if (action === "list") {
618
+ return { kind: "vault.item.list", ...(agent ? { agent } : {}), ...(prefix ? { prefix } : {}) };
619
+ }
620
+ if (!item)
621
+ throw new Error(`Usage: ouro vault item ${action} [--agent <name>] --item <path>`);
622
+ if (action === "status") {
623
+ return { kind: "vault.item.status", ...(agent ? { agent } : {}), item };
624
+ }
625
+ if (!template && secretFields.length === 0) {
626
+ throw new Error("ouro vault item set requires --secret-field or --template");
627
+ }
628
+ return {
629
+ kind: "vault.item.set",
630
+ ...(agent ? { agent } : {}),
631
+ item,
632
+ ...(template ? { template } : {}),
633
+ ...(secretFields.length > 0 ? { secretFields } : {}),
634
+ ...(publicFields.length > 0 ? { publicFields } : {}),
635
+ ...(note !== undefined ? { note } : {}),
636
+ };
637
+ }
638
+ function parseVaultOpsCommand(args) {
639
+ const provider = args[0];
640
+ const action = args[1];
641
+ if (provider !== "porkbun") {
642
+ throw new Error("Usage: ouro vault ops porkbun set|status [--agent <name>] [--account <account>]");
643
+ }
644
+ if (action !== "set" && action !== "status") {
645
+ throw new Error("Usage: ouro vault ops porkbun set|status [--agent <name>] [--account <account>]");
646
+ }
647
+ const { agent, rest } = extractAgentFlag(args.slice(2));
648
+ let account;
649
+ for (let i = 0; i < rest.length; i += 1) {
650
+ const token = rest[i];
651
+ if (token === "--account") {
652
+ account = (0, vault_items_1.normalizePorkbunOpsAccount)(rest[i + 1]);
653
+ i += 1;
654
+ continue;
655
+ }
656
+ throw new Error(`Usage: ouro vault ops porkbun ${action} [--agent <name>] [--account <account>]`);
657
+ }
658
+ if (action === "set") {
659
+ if (!account)
660
+ throw new Error("Usage: ouro vault ops porkbun set [--agent <name>] --account <account>");
661
+ return {
662
+ kind: "vault.item.set",
663
+ ...(agent ? { agent } : {}),
664
+ item: (0, vault_items_1.porkbunOpsCredentialItemName)(account),
665
+ template: "porkbun-api",
666
+ compatibilityAlias: vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS,
667
+ };
668
+ }
669
+ if (account) {
670
+ return {
671
+ kind: "vault.item.status",
672
+ ...(agent ? { agent } : {}),
673
+ item: (0, vault_items_1.porkbunOpsCredentialItemName)(account),
674
+ compatibilityAlias: vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS,
675
+ };
676
+ }
677
+ return {
678
+ kind: "vault.item.list",
679
+ ...(agent ? { agent } : {}),
680
+ prefix: vault_items_1.PORKBUN_OPS_CREDENTIAL_PREFIX,
681
+ compatibilityAlias: vault_items_1.PORKBUN_OPS_COMPATIBILITY_ALIAS,
682
+ };
683
+ }
547
684
  function parseVaultConfigCommand(args) {
548
685
  const sub = args[0];
549
686
  const { agent, rest } = extractAgentFlag(args.slice(1));
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PORKBUN_OPS_COMPATIBILITY_ALIAS = exports.PORKBUN_OPS_CREDENTIAL_PREFIX = void 0;
4
+ exports.isVaultItemTemplate = isVaultItemTemplate;
5
+ exports.normalizeVaultItemName = normalizeVaultItemName;
6
+ exports.normalizeVaultItemFieldName = normalizeVaultItemFieldName;
7
+ exports.vaultItemTemplateSecretFields = vaultItemTemplateSecretFields;
8
+ exports.normalizePorkbunOpsAccount = normalizePorkbunOpsAccount;
9
+ exports.porkbunOpsCredentialItemName = porkbunOpsCredentialItemName;
10
+ exports.porkbunOpsAccountFromItemName = porkbunOpsAccountFromItemName;
11
+ exports.requireVaultItemSecret = requireVaultItemSecret;
12
+ exports.PORKBUN_OPS_CREDENTIAL_PREFIX = "ops/registrars/porkbun/accounts";
13
+ exports.PORKBUN_OPS_COMPATIBILITY_ALIAS = "vault ops porkbun";
14
+ const VAULT_ITEM_NAME_FORBIDDEN = /[\r\n\t]/;
15
+ const VAULT_ITEM_FIELD_FORBIDDEN = /[\r\n\t=]/;
16
+ const PORKBUN_OPS_ACCOUNT_FORBIDDEN = /[\/\r\n\t]/;
17
+ function isVaultItemTemplate(value) {
18
+ return value === "porkbun-api";
19
+ }
20
+ function normalizeVaultItemName(item) {
21
+ const normalized = item?.trim() ?? "";
22
+ if (!normalized || VAULT_ITEM_NAME_FORBIDDEN.test(normalized) || normalized.startsWith("/") || normalized.endsWith("/")) {
23
+ throw new Error("Vault item name/path must be non-empty, relative, and free of control characters.");
24
+ }
25
+ return normalized;
26
+ }
27
+ function normalizeVaultItemFieldName(field) {
28
+ const normalized = field?.trim() ?? "";
29
+ if (!normalized || VAULT_ITEM_FIELD_FORBIDDEN.test(normalized)) {
30
+ throw new Error("Vault item field names must be non-empty and free of control characters or '='.");
31
+ }
32
+ return normalized;
33
+ }
34
+ function vaultItemTemplateSecretFields(_template) {
35
+ return ["apiKey", "secretApiKey"];
36
+ }
37
+ function normalizePorkbunOpsAccount(account) {
38
+ const normalized = account?.trim() ?? "";
39
+ if (!normalized || PORKBUN_OPS_ACCOUNT_FORBIDDEN.test(normalized)) {
40
+ throw new Error("Porkbun account must be a non-empty account label without slashes or control characters.");
41
+ }
42
+ return normalized;
43
+ }
44
+ function porkbunOpsCredentialItemName(account) {
45
+ return `${exports.PORKBUN_OPS_CREDENTIAL_PREFIX}/${normalizePorkbunOpsAccount(account)}`;
46
+ }
47
+ function porkbunOpsAccountFromItemName(itemName) {
48
+ const prefix = `${exports.PORKBUN_OPS_CREDENTIAL_PREFIX}/`;
49
+ return itemName.startsWith(prefix) ? itemName.slice(prefix.length) : undefined;
50
+ }
51
+ function requireVaultItemSecret(value, label) {
52
+ const trimmed = value.trim();
53
+ if (!trimmed)
54
+ throw new Error(`${label} cannot be blank`);
55
+ return trimmed;
56
+ }
@@ -59,10 +59,11 @@ const DISPATCH_EXEMPT_PATTERNS = [
59
59
  "repertoire/tools-config",
60
60
  "repertoire/tools-base",
61
61
  // CLI sub-modules: cli-exec.ts is the router with emitNervesEvent calls;
62
- // cli-parse, cli-render, and cli-help are pure functions/data with no side effects.
62
+ // cli-parse, cli-render, cli-help, and their small helpers are pure functions/data with no side effects.
63
63
  "daemon/cli-parse",
64
64
  "daemon/cli-render",
65
65
  "daemon/cli-help",
66
+ "daemon/vault-items",
66
67
  // Shared utility modules: pure helpers consumed by modules that own observability.
67
68
  "arc/json-store",
68
69
  "repertoire/api-client",
@@ -72,6 +72,11 @@ function optionalTrimmedText(value, fieldName) {
72
72
  const trimmed = value.trim();
73
73
  return trimmed.length > 0 ? trimmed : undefined;
74
74
  }
75
+ function resolveVaultItemArg(args, legacyFieldName = "domain") {
76
+ if (args.item !== undefined)
77
+ return requireTrimmedText(args.item, "item");
78
+ return requireTrimmedText(args[legacyFieldName], legacyFieldName);
79
+ }
75
80
  function parsePasswordLength(value) {
76
81
  if (value === undefined || value === null || value === "")
77
82
  return DEFAULT_PASSWORD_LENGTH;
@@ -127,13 +132,17 @@ exports.credentialToolDefinitions = [
127
132
  type: "function",
128
133
  function: {
129
134
  name: "credential_get",
130
- description: "Get credential metadata for a domain. Returns username, notes, and creation date. Never returns passwords — the credential gateway handles secret injection internally.",
135
+ description: "Get credential metadata for a vault item name/path. Returns username, notes, and creation date. Never returns passwords — the credential gateway handles secret injection internally.",
131
136
  parameters: {
132
137
  type: "object",
133
138
  properties: {
139
+ item: {
140
+ type: "string",
141
+ description: "Vault item name/path to look up (e.g. 'airbnb.com' or 'ops/porkbun/account')",
142
+ },
134
143
  domain: {
135
144
  type: "string",
136
- description: "Domain to look up (e.g. 'airbnb.com', 'api.openweathermap.org')",
145
+ description: "compatibility alias for item when the vault item name is a service domain",
137
146
  },
138
147
  },
139
148
  required: ["domain"],
@@ -141,17 +150,18 @@ exports.credentialToolDefinitions = [
141
150
  },
142
151
  },
143
152
  handler: async (args) => {
153
+ const itemName = resolveVaultItemArg(args);
144
154
  (0, runtime_1.emitNervesEvent)({
145
155
  component: "repertoire",
146
156
  event: "repertoire.credential_tool_call",
147
157
  message: "credential_get invoked",
148
- meta: { tool: "credential_get", domain: args.domain },
158
+ meta: { tool: "credential_get", domain: itemName, item: itemName },
149
159
  });
150
160
  try {
151
161
  const store = (0, credential_access_1.getCredentialStore)();
152
- const meta = await store.get(args.domain);
162
+ const meta = await store.get(itemName);
153
163
  if (!meta) {
154
- return `No credential found for "${args.domain}".`;
164
+ return `No credential found for "${itemName}".`;
155
165
  }
156
166
  return JSON.stringify(meta, null, 2);
157
167
  }
@@ -220,13 +230,17 @@ exports.credentialToolDefinitions = [
220
230
  type: "function",
221
231
  function: {
222
232
  name: "credential_store",
223
- description: "Store credentials the agent acquired or just used successfully during signup. Prefer credential_generate_password for new passwords, then call this tool once the site accepts the exact password. Stored passwords are never returned later — only metadata is visible.",
233
+ description: "Store credentials in a vault item name/path after the agent acquired or just used them successfully. Prefer credential_generate_password for new passwords, then call this tool once the site accepts the exact password. Stored passwords are never returned later — only metadata is visible.",
224
234
  parameters: {
225
235
  type: "object",
226
236
  properties: {
237
+ item: {
238
+ type: "string",
239
+ description: "Vault item name/path to store under; domains are examples, not the schema",
240
+ },
227
241
  domain: {
228
242
  type: "string",
229
- description: "Domain these credentials are for (e.g. 'airbnb.com')",
243
+ description: "compatibility alias for item when the vault item name is a service domain",
230
244
  },
231
245
  username: {
232
246
  type: "string",
@@ -238,7 +252,7 @@ exports.credentialToolDefinitions = [
238
252
  },
239
253
  notes: {
240
254
  type: "string",
241
- description: "Optional notes about this credential",
255
+ description: "Optional human/agent orientation notes about this credential; not parsed by code",
242
256
  },
243
257
  },
244
258
  required: ["domain", "username", "password"],
@@ -250,14 +264,15 @@ exports.credentialToolDefinitions = [
250
264
  let username = "";
251
265
  let password = "";
252
266
  let notes;
267
+ const itemNameForEvent = typeof args.item === "string" && args.item.trim() ? args.item.trim() : args.domain;
253
268
  (0, runtime_1.emitNervesEvent)({
254
269
  component: "repertoire",
255
270
  event: "repertoire.credential_tool_call",
256
271
  message: "credential_store invoked",
257
- meta: { tool: "credential_store", domain: args.domain },
272
+ meta: { tool: "credential_store", domain: itemNameForEvent, item: itemNameForEvent },
258
273
  });
259
274
  try {
260
- domain = requireTrimmedText(args.domain, "domain");
275
+ domain = resolveVaultItemArg(args);
261
276
  username = requireTrimmedText(args.username, "username");
262
277
  password = requireNonBlankSecret(args.password, "password");
263
278
  notes = optionalTrimmedText(args.notes, "notes");
@@ -281,13 +296,13 @@ exports.credentialToolDefinitions = [
281
296
  type: "function",
282
297
  function: {
283
298
  name: "credential_list",
284
- description: "List stored credential domains. Returns metadata only (domain, username, notes, creation date). Never returns passwords.",
299
+ description: "List stored vault items. Returns metadata only (item/domain name, username, notes, creation date). Never returns passwords.",
285
300
  parameters: {
286
301
  type: "object",
287
302
  properties: {
288
303
  search: {
289
304
  type: "string",
290
- description: "Optional search filter to match against domain names",
305
+ description: "Optional search filter to match against vault item names/paths",
291
306
  },
292
307
  },
293
308
  },
@@ -323,13 +338,17 @@ exports.credentialToolDefinitions = [
323
338
  type: "function",
324
339
  function: {
325
340
  name: "credential_delete",
326
- description: "Delete stored credentials for a domain.",
341
+ description: "Delete stored credentials for a vault item name/path.",
327
342
  parameters: {
328
343
  type: "object",
329
344
  properties: {
345
+ item: {
346
+ type: "string",
347
+ description: "Vault item name/path whose credentials should be deleted",
348
+ },
330
349
  domain: {
331
350
  type: "string",
332
- description: "Domain whose credentials should be deleted",
351
+ description: "compatibility alias for item when the vault item name is a service domain",
333
352
  },
334
353
  },
335
354
  required: ["domain"],
@@ -344,12 +363,13 @@ exports.credentialToolDefinitions = [
344
363
  meta: { tool: "credential_delete", domain: args.domain },
345
364
  });
346
365
  try {
366
+ const itemName = resolveVaultItemArg(args);
347
367
  const store = (0, credential_access_1.getCredentialStore)();
348
- const deleted = await store.delete(args.domain);
368
+ const deleted = await store.delete(itemName);
349
369
  if (deleted) {
350
- return `Credentials for "${args.domain}" deleted.`;
370
+ return `Credentials for "${itemName}" deleted.`;
351
371
  }
352
- return `No credential found for "${args.domain}".`;
372
+ return `No credential found for "${itemName}".`;
353
373
  }
354
374
  catch (err) {
355
375
  /* v8 ignore next -- defensive: store.delete wraps errors @preserve */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.464",
3
+ "version": "0.1.0-alpha.466",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",