@0dai-dev/cli 4.3.5 → 4.3.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.
Files changed (79) hide show
  1. package/README.md +12 -11
  2. package/bin/0dai.js +214 -40
  3. package/lib/ai/manifest/mcp-exposure-contract.json +121 -0
  4. package/lib/ai/meta/manifest/mcp-tool-tiers.json +435 -0
  5. package/lib/ai/registry/mcp-catalog.json +98 -0
  6. package/lib/commands/auth.js +55 -1
  7. package/lib/commands/compliance.js +1 -1
  8. package/lib/commands/detect.js +10 -4
  9. package/lib/commands/doctor.js +545 -26
  10. package/lib/commands/experience.js +40 -5
  11. package/lib/commands/export.js +73 -0
  12. package/lib/commands/feedback.js +157 -15
  13. package/lib/commands/gh.js +26 -0
  14. package/lib/commands/graph.js +9 -4
  15. package/lib/commands/heatmap.js +1 -1
  16. package/lib/commands/init.js +222 -30
  17. package/lib/commands/mcp.js +129 -21
  18. package/lib/commands/models.js +138 -41
  19. package/lib/commands/provider.js +30 -59
  20. package/lib/commands/quota.js +1 -1
  21. package/lib/commands/receipt.js +1 -1
  22. package/lib/commands/run.js +18 -7
  23. package/lib/commands/runner.js +31 -1
  24. package/lib/commands/status.js +44 -11
  25. package/lib/commands/swarm.js +130 -12
  26. package/lib/commands/trust.js +286 -0
  27. package/lib/commands/update.js +184 -38
  28. package/lib/commands/usage.js +1 -1
  29. package/lib/commands/validate.js +32 -3
  30. package/lib/commands/vault.js +46 -9
  31. package/lib/python/__init__.py +0 -0
  32. package/lib/python/agent_quotas.py +525 -0
  33. package/lib/python/anomaly_alert.py +397 -0
  34. package/lib/python/anti_pattern_detector.py +799 -0
  35. package/lib/python/auth.py +443 -0
  36. package/lib/python/capi_profile_guard.py +477 -0
  37. package/lib/python/compliance_report.py +581 -0
  38. package/lib/python/drift_detector.py +388 -0
  39. package/lib/python/experience_pipeline.py +1130 -0
  40. package/lib/python/graph.py +19 -0
  41. package/lib/python/graph_core.py +293 -0
  42. package/lib/python/graph_io.py +179 -0
  43. package/lib/python/graph_legacy.py +2052 -0
  44. package/lib/python/graph_legacy_helpers.py +221 -0
  45. package/lib/python/graph_outcomes_core.py +85 -0
  46. package/lib/python/graph_queries.py +171 -0
  47. package/lib/python/graph_slice.py +198 -0
  48. package/lib/python/graph_slicer.py +576 -0
  49. package/lib/python/graph_slicer_cli.py +60 -0
  50. package/lib/python/graph_validation.py +64 -0
  51. package/lib/python/heatmap.py +934 -0
  52. package/lib/python/json_utils.py +193 -0
  53. package/lib/python/mcp_exposure_check.py +247 -0
  54. package/lib/python/model_router.py +1434 -0
  55. package/lib/python/project_manager.py +621 -0
  56. package/lib/python/provider_profiles.py +1618 -0
  57. package/lib/python/provider_registry.py +1211 -0
  58. package/lib/python/provider_registry_cli.py +125 -0
  59. package/lib/python/receipt_png.py +727 -0
  60. package/lib/python/structural_memory.py +325 -0
  61. package/lib/python/swarm_cost.py +177 -0
  62. package/lib/python/usage_ledger.py +569 -0
  63. package/lib/scripts/mcp_tier_config.py +240 -0
  64. package/lib/shared.js +97 -14
  65. package/lib/tui/index.mjs +35174 -0
  66. package/lib/utils/activation_telemetry.js +230 -11
  67. package/lib/utils/constants.js +7 -1
  68. package/lib/utils/export-bundler.js +285 -0
  69. package/lib/utils/identity.js +198 -1
  70. package/lib/utils/mcp-auth.js +81 -15
  71. package/lib/utils/plan.js +1 -1
  72. package/lib/vault/index.js +19 -3
  73. package/lib/vault/storage.js +21 -2
  74. package/lib/wizard.js +5 -2
  75. package/package.json +9 -3
  76. package/scripts/build-python-bundle.js +106 -0
  77. package/scripts/build-tui.js +14 -1
  78. package/scripts/harvest_experience.py +523 -0
  79. package/scripts/postinstall.js +15 -9
@@ -1,10 +1,123 @@
1
1
  "use strict";
2
2
  const shared = require("../shared");
3
- const { log, SUPPORTED_CLIS } = shared;
3
+ const { log, SUPPORTED_CLIS, cliDisplayName } = shared;
4
+
5
+ function installCommand(cli) {
6
+ if (cli.pkgType === "npm" && cli.pkg) return `npm install -g ${cli.pkg}@latest`;
7
+ if (cli.pkgType === "pip" && cli.pkg) return `pip install --upgrade ${cli.pkg}`;
8
+ return cli.install || "";
9
+ }
10
+
11
+ function latestCommand(cli) {
12
+ if (cli.pkgType === "npm" && cli.pkg) return `npm view ${cli.pkg} version`;
13
+ if (cli.pkgType === "pip" && cli.pkg) return `pip index versions ${cli.pkg}`;
14
+ return "";
15
+ }
16
+
17
+ function parseVersion(out) {
18
+ const m = String(out || "").match(/(\d+\.\d+\.\d+)/);
19
+ return m ? m[1] : null;
20
+ }
21
+
22
+ function readInstalledVersion(cli, execFileSync) {
23
+ try {
24
+ const out = execFileSync(cli.bin, ["--version"], { timeout: 8000 }).toString().trim();
25
+ return { installed: true, version: parseVersion(out), raw: out };
26
+ } catch {
27
+ return { installed: false, version: null, raw: "" };
28
+ }
29
+ }
30
+
31
+ function readLatestVersion(cli, execFileSync) {
32
+ if (cli.pkgType === "npm" && cli.pkg) {
33
+ try {
34
+ const latest = execFileSync("npm", ["view", cli.pkg, "version"], { timeout: 5000 })
35
+ .toString()
36
+ .trim();
37
+ return { latest, error: null };
38
+ } catch (error) {
39
+ return { latest: null, error };
40
+ }
41
+ }
42
+ if (cli.pkgType === "pip" && cli.pkg) {
43
+ try {
44
+ const out = execFileSync("pip", ["index", "versions", cli.pkg], { timeout: 8000, encoding: "utf8" });
45
+ const m = String(out).match(/LATEST:\s*(\d+\.\d+\.\d+)/i) || String(out).match(/(\d+\.\d+\.\d+)/);
46
+ return { latest: m ? m[1] : null, error: null };
47
+ } catch (error) {
48
+ return { latest: null, error };
49
+ }
50
+ }
51
+ return { latest: null, error: null };
52
+ }
53
+
54
+ function verifyUpdatedVersion(cli, expected, execFileSync) {
55
+ const after = readInstalledVersion(cli, execFileSync);
56
+ if (!after.installed) {
57
+ return {
58
+ ok: false,
59
+ version: null,
60
+ error: `post-update verification failed: ${cli.bin} is not available after install`,
61
+ };
62
+ }
63
+ if (after.version === expected) {
64
+ return { ok: true, version: after.version, error: null };
65
+ }
66
+ const actual = after.version
67
+ ? after.version
68
+ : (after.raw ? `unparseable output ${JSON.stringify(after.raw)}` : "unknown");
69
+ return {
70
+ ok: false,
71
+ version: after.version,
72
+ error: `post-update verification failed: ${cli.name} is still ${actual} (expected ${expected})`,
73
+ };
74
+ }
75
+
76
+ function updateEntry(cli, current, latest, status, extra = {}) {
77
+ return {
78
+ name: cli.name,
79
+ bin: cli.bin,
80
+ current: current || "",
81
+ latest: latest || "",
82
+ status,
83
+ ...(extra.error ? { error: extra.error } : {}),
84
+ ...(extra.next ? { next: extra.next } : {}),
85
+ };
86
+ }
87
+
88
+ function formatEntry(entry) {
89
+ const current = entry.current ? String(entry.current) : "";
90
+ const latest = entry.latest ? String(entry.latest) : "";
91
+ const name = cliDisplayName(entry);
92
+ const version = latest && current && latest !== current
93
+ ? `${current} -> ${latest}`
94
+ : (current || latest || "unknown");
95
+ return `${name} ${version}`;
96
+ }
97
+
98
+ function printSummary(summary) {
99
+ log("update summary");
100
+ for (const key of ["updated", "would_update", "failed", "unchanged"]) {
101
+ const entries = summary[key] || [];
102
+ if (!entries.length) continue;
103
+ console.log(` ${key.replace("_", " ")}: ${entries.map(formatEntry).join(", ")}`);
104
+ for (const entry of entries) {
105
+ const name = cliDisplayName(entry);
106
+ if (entry.next) console.log(` next ${name}: ${entry.next}`);
107
+ if (entry.error) console.log(` error ${name}: ${entry.error}`);
108
+ }
109
+ }
110
+ const nextEntries = ["would_update", "failed"]
111
+ .flatMap((key) => summary[key] || []);
112
+ if (nextEntries.some((entry) => entry.next && entry.next.startsWith("npm install "))) {
113
+ console.log(" note: global npm may need sudo/admin");
114
+ }
115
+ }
4
116
 
5
117
  function cmdUpdate(args) {
6
118
  const { execFileSync: _ef3, execSync } = require("child_process");
7
119
  const dryRun = args.includes("--dry-run");
120
+ const wantJson = args.includes("--json");
8
121
 
9
122
  // 0dai self-update entry, then all supported agent CLIs.
10
123
  const CLIS = [
@@ -12,58 +125,91 @@ function cmdUpdate(args) {
12
125
  ...SUPPORTED_CLIS,
13
126
  ];
14
127
 
15
- let updated = 0;
128
+ const summary = {
129
+ dry_run: dryRun,
130
+ updated: [],
131
+ would_update: [],
132
+ failed: [],
133
+ unchanged: [],
134
+ skipped: [],
135
+ };
16
136
  for (const cli of CLIS) {
17
- let installed = false, ver = null;
18
- try {
19
- const out = _ef3(cli.bin, ["--version"], { timeout: 8000 }).toString().trim();
20
- installed = true;
21
- const m = out.match(/(\d+\.\d+\.\d+)/);
22
- if (m) ver = m[1];
23
- } catch {}
24
-
25
- if (!installed) continue;
26
-
27
- let latest = null;
28
- if (cli.pkgType === "npm" && cli.pkg) {
29
- try { latest = _ef3("npm", ["view", cli.pkg, "version"], { timeout: 5000 }).toString().trim(); } catch {}
30
- } else if (cli.pkgType === "pip" && cli.pkg) {
31
- try {
32
- const out = _ef3("pip", ["index", "versions", cli.pkg], { timeout: 8000, encoding: "utf8" });
33
- const m = out.match(/LATEST:\s*(\d+\.\d+\.\d+)/i) || out.match(/(\d+\.\d+\.\d+)/);
34
- if (m) latest = m[1];
35
- } catch {}
137
+ const label = cliDisplayName(cli);
138
+ const installedInfo = readInstalledVersion(cli, _ef3);
139
+ const ver = installedInfo.version;
140
+
141
+ if (!installedInfo.installed) {
142
+ summary.skipped.push(updateEntry(cli, "", "", "not_installed"));
143
+ continue;
144
+ }
145
+
146
+ const latestInfo = readLatestVersion(cli, _ef3);
147
+ const latest = latestInfo.latest;
148
+ const latestError = latestInfo.error;
149
+
150
+ if (latestError) {
151
+ const error = `latest check failed: ${String(latestError && latestError.message ? latestError.message : latestError).split("\n")[0]}`;
152
+ summary.failed.push(updateEntry(cli, ver, latest, "latest_unknown", { error, next: latestCommand(cli) }));
153
+ if (!wantJson) log(`failed to check ${label}: ${error}`);
154
+ continue;
36
155
  }
37
156
 
38
157
  if (!latest || latest === ver) {
39
- console.log(` ${cli.name} ${ver || ""} — up to date`);
158
+ summary.unchanged.push(updateEntry(cli, ver, latest, "unchanged"));
159
+ if (!wantJson) console.log(` ${label} ${ver || ""} — up to date`);
40
160
  continue;
41
161
  }
42
162
 
43
- console.log(` ${cli.name} ${ver} → ${latest}`);
44
- if (dryRun) { updated++; continue; }
163
+ if (!wantJson) console.log(` ${label} ${ver} → ${latest}`);
164
+ if (dryRun) {
165
+ summary.would_update.push(updateEntry(cli, ver, latest, "would_update", { next: installCommand(cli) }));
166
+ continue;
167
+ }
45
168
 
46
169
  try {
47
- if (cli.pkgType === "npm") {
48
- log(`updating ${cli.name}...`);
49
- execSync(`npm install -g ${cli.pkg}@latest`, { timeout: 60000, stdio: "pipe" });
50
- log(`${cli.name} updated to ${latest}`);
51
- updated++;
52
- } else if (cli.pkgType === "pip") {
53
- log(`updating ${cli.name}...`);
54
- execSync(`pip install --upgrade ${cli.pkg}`, { timeout: 60000, stdio: "pipe" });
55
- log(`${cli.name} updated to ${latest}`);
56
- updated++;
170
+ if (cli.pkgType === "npm" || cli.pkgType === "pip") {
171
+ const next = installCommand(cli);
172
+ if (!wantJson) log(`updating ${label}...`);
173
+ execSync(next, { timeout: 60000, stdio: "pipe" });
174
+ const verified = verifyUpdatedVersion(cli, latest, _ef3);
175
+ if (!verified.ok) {
176
+ summary.failed.push(updateEntry(cli, ver, latest, "verify_failed", { error: verified.error, next }));
177
+ if (!wantJson) log(`failed to verify ${label}: ${verified.error}`);
178
+ continue;
179
+ }
180
+ if (!wantJson) log(`${label} updated to ${latest}`);
181
+ summary.updated.push(updateEntry(cli, ver, latest, "updated"));
182
+ } else {
183
+ summary.skipped.push(updateEntry(cli, ver, latest, "unsupported"));
57
184
  }
58
185
  } catch (e) {
59
- log(`failed to update ${cli.name}: ${e.message.split("\n")[0]}`);
186
+ const error = String(e && e.message ? e.message : e).split("\n")[0];
187
+ summary.failed.push(updateEntry(cli, ver, latest, "failed", { error, next: installCommand(cli) }));
188
+ if (!wantJson) log(`failed to update ${label}: ${error}`);
60
189
  }
61
190
  }
62
- if (updated) {
63
- log(`${dryRun ? "would update" : "updated"} ${updated} CLI(s)`);
191
+
192
+ summary.counts = {
193
+ updated: summary.updated.length,
194
+ would_update: summary.would_update.length,
195
+ failed: summary.failed.length,
196
+ unchanged: summary.unchanged.length,
197
+ skipped: summary.skipped.length,
198
+ };
199
+
200
+ if (wantJson) {
201
+ console.log(JSON.stringify(summary, null, 2));
202
+ } else if (summary.updated.length || summary.would_update.length || summary.failed.length) {
203
+ printSummary(summary);
64
204
  } else {
65
205
  log("all CLIs are up to date");
66
206
  }
207
+ return summary;
67
208
  }
68
209
 
69
- module.exports = { cmdUpdate };
210
+ module.exports = {
211
+ cmdUpdate,
212
+ installCommand,
213
+ latestCommand,
214
+ formatEntry,
215
+ };
@@ -67,7 +67,7 @@ function cmdUsage(target, rawArgs = []) {
67
67
  process.exit(1);
68
68
  }
69
69
 
70
- const script = findRepoScript(target, "usage_ledger.py");
70
+ const script = shared.resolvePythonScript(target, "usage_ledger.py");
71
71
  if (!script) {
72
72
  log("usage ledger unavailable");
73
73
  console.log(` ${D}Expected scripts/usage_ledger.py in this project${R}`);
@@ -19,11 +19,17 @@ function cmdValidate(target) {
19
19
  "ai/manifest/project.yaml", "ai/manifest/discovery.json",
20
20
  "ai/manifest/applied-lock.json", "ai/manifest/environment.yaml",
21
21
  "ai/manifest/commands.yaml",
22
+ "ai/manifest/current_state.json", "ai/manifest/current_task.json",
22
23
  ];
23
24
 
24
25
  let agents = [];
26
+ let localMode = false;
25
27
  try {
26
- agents = JSON.parse(fs.readFileSync(path.join(ai, "manifest", "discovery.json"), "utf8")).selected_agents || [];
28
+ const discovery = JSON.parse(fs.readFileSync(path.join(ai, "manifest", "discovery.json"), "utf8"));
29
+ agents = discovery.selected_agents || [];
30
+ // A wizard/`0dai init --local` layer is generated offline and never writes
31
+ // the cloud-only files below. Treat it as a valid "local" install mode.
32
+ localMode = discovery.wizard === true || discovery.mode === "local";
27
33
  } catch {}
28
34
 
29
35
  const agentFiles = Object.fromEntries(
@@ -35,6 +41,18 @@ function cmdValidate(target) {
35
41
  for (const f of agentFiles[agent] || []) required.push(f);
36
42
  }
37
43
 
44
+ // Files that only `0dai sync` (cloud mode) can produce. On a local layer these
45
+ // are optional — their absence is informational, not a validation failure.
46
+ const CLOUD_ONLY = new Set([
47
+ "ai/VERSION_SCHEMA",
48
+ "ai/manifest/applied-lock.json",
49
+ "ai/manifest/environment.yaml",
50
+ "ai/manifest/commands.yaml",
51
+ ".claude/settings.json", ".claude/CLAUDE.md", ".mcp.json",
52
+ ".codex/config.toml", "opencode.json",
53
+ ...Object.values(agentFiles).flat(),
54
+ ]);
55
+
38
56
  const FIX_HINTS = {
39
57
  "ai/VERSION": "run: 0dai init",
40
58
  "ai/VERSION_SCHEMA": "run: 0dai sync",
@@ -43,6 +61,8 @@ function cmdValidate(target) {
43
61
  "ai/manifest/applied-lock.json": "run: 0dai sync",
44
62
  "ai/manifest/environment.yaml": "run: 0dai sync",
45
63
  "ai/manifest/commands.yaml": "run: 0dai sync",
64
+ "ai/manifest/current_state.json": "run: 0dai sync",
65
+ "ai/manifest/current_task.json": "run: 0dai sync",
46
66
  "AGENTS.md": "run: 0dai sync",
47
67
  ".claude/settings.json": "run: 0dai sync",
48
68
  ".claude/CLAUDE.md": "run: 0dai sync",
@@ -51,10 +71,16 @@ function cmdValidate(target) {
51
71
  "opencode.json": "install opencode, then: 0dai sync",
52
72
  };
53
73
 
54
- const present = required.filter(f => fs.existsSync(path.join(target, f)));
55
- const missing = required.filter(f => !fs.existsSync(path.join(target, f)));
74
+ const exists = (f) => fs.existsSync(path.join(target, f));
75
+ const present = required.filter(exists);
76
+ // On a local layer, absent cloud-only files are optional, not missing.
77
+ const missing = required.filter(f => !exists(f) && !(localMode && CLOUD_ONLY.has(f)));
78
+ const optional = localMode ? required.filter(f => !exists(f) && CLOUD_ONLY.has(f)) : [];
56
79
 
57
80
  for (const f of present) console.log(` ${G}✓${R2} ${f}`);
81
+ for (const f of optional) {
82
+ console.log(` ${D2}○ ${f} — optional in local mode (run: 0dai sync to enable cloud features)${R2}`);
83
+ }
58
84
  for (const f of missing) {
59
85
  const hint = FIX_HINTS[f] || "run: 0dai sync";
60
86
  console.log(` ${E}✗${R2} ${f} ${D2}— ${hint}${R2}`);
@@ -63,6 +89,9 @@ function cmdValidate(target) {
63
89
  if (missing.length) {
64
90
  console.log(`\n${E}${missing.length} missing${R2} / ${present.length + missing.length} total`);
65
91
  process.exitCode = 1;
92
+ } else if (localMode) {
93
+ const optNote = optional.length ? ` ${D2}(${optional.length} cloud-only file(s) skipped)${R2}` : "";
94
+ log(`${G}validate ok${R2} — healthy local install, ${present.length} required files present${optNote}`);
66
95
  } else {
67
96
  log(`${G}validate ok${R2} — all ${present.length} required files present`);
68
97
  }
@@ -24,10 +24,16 @@ function hasFlag(args, name) {
24
24
  return args.includes(name);
25
25
  }
26
26
 
27
+ function _hasFlagBeforeTerminator(args, name) {
28
+ const stop = args.indexOf("--");
29
+ const scan = stop >= 0 ? args.slice(0, stop) : args;
30
+ return scan.includes(name);
31
+ }
32
+
27
33
  function printUsage() {
28
34
  console.log("Usage: 0dai vault <init|add|get|list|inject|rotate> [--json]");
29
35
  console.log(" Phase 1+2a ships `init`, `add`, `get`. `list`/`inject`/`rotate` gated on operator P2b approval.");
30
- console.log(" add: 0dai vault add <scope> <name> <value> (or value via --stdin)");
36
+ console.log(" add: 0dai vault add <scope> <name> <value> (or value via --stdin; use -- before values starting with --)");
31
37
  console.log(" get: 0dai vault get <scope> <name>");
32
38
  console.log(" Runbook: docs/runbooks/0dai-vault.md");
33
39
  }
@@ -84,12 +90,39 @@ function _readStdinSync() {
84
90
  }
85
91
  }
86
92
 
87
- function cmdVaultAdd(args) {
88
- const asJson = hasFlag(args, "--json");
89
- const useStdin = hasFlag(args, "--stdin");
90
- // positional args after stripping flags
91
- const positional = args.filter((a) => !a.startsWith("--"));
93
+ function _normalizeStdinPayload(payload) {
94
+ return String(payload || "").replace(/\r?\n$/, "");
95
+ }
96
+
97
+ function _positionalArgs(args, knownFlags = new Set()) {
98
+ const out = [];
99
+ for (let i = 0; i < args.length; i += 1) {
100
+ const arg = args[i];
101
+ if (arg === "--") {
102
+ out.push(...args.slice(i + 1));
103
+ break;
104
+ }
105
+ if (knownFlags.has(arg)) continue;
106
+ out.push(arg);
107
+ }
108
+ return out;
109
+ }
110
+
111
+ function _parseAddArgs(args) {
112
+ const flags = new Set(["--json", "--stdin"]);
113
+ const positional = _positionalArgs(args, flags);
92
114
  const [scope, name, value] = positional;
115
+ return {
116
+ asJson: _hasFlagBeforeTerminator(args, "--json"),
117
+ useStdin: _hasFlagBeforeTerminator(args, "--stdin"),
118
+ scope,
119
+ name,
120
+ value,
121
+ };
122
+ }
123
+
124
+ function cmdVaultAdd(args) {
125
+ const { asJson, useStdin, scope, name, value } = _parseAddArgs(args);
93
126
  if (!scope || !name) {
94
127
  log("usage: 0dai vault add <scope> <name> <value>|--stdin");
95
128
  process.exitCode = 1;
@@ -97,7 +130,7 @@ function cmdVaultAdd(args) {
97
130
  }
98
131
  let payload = value;
99
132
  if (useStdin) {
100
- payload = _readStdinSync();
133
+ payload = _normalizeStdinPayload(_readStdinSync());
101
134
  if (!payload) {
102
135
  log("vault add --stdin: no input on stdin");
103
136
  process.exitCode = 1;
@@ -142,7 +175,7 @@ function cmdVaultAdd(args) {
142
175
 
143
176
  function cmdVaultGet(args) {
144
177
  const asJson = hasFlag(args, "--json");
145
- const positional = args.filter((a) => !a.startsWith("--"));
178
+ const positional = _positionalArgs(args, new Set(["--json"]));
146
179
  const [scope, name] = positional;
147
180
  if (!scope || !name) {
148
181
  log("usage: 0dai vault get <scope> <name>");
@@ -209,7 +242,9 @@ function cmdVaultDeferred(sub, args) {
209
242
 
210
243
  function cmdVault(_target, sub, args) {
211
244
  const command = sub && !sub.startsWith("-") ? sub : "";
212
- const forwarded = command === sub ? args.slice(2) : args.slice(1);
245
+ // args is already args.slice(2) from bin/0dai.js (the tail after "vault <sub>"),
246
+ // so forwarded is the remaining flags/positionals unchanged.
247
+ const forwarded = command ? args : args.slice(1);
213
248
 
214
249
  if (!command || command === "help" || command === "-h" || command === "--help") {
215
250
  printUsage();
@@ -243,4 +278,6 @@ function cmdVault(_target, sub, args) {
243
278
 
244
279
  module.exports = {
245
280
  cmdVault,
281
+ _normalizeStdinPayload,
282
+ _parseAddArgs,
246
283
  };
File without changes