@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.
- package/README.md +12 -11
- package/bin/0dai.js +214 -40
- package/lib/ai/manifest/mcp-exposure-contract.json +121 -0
- package/lib/ai/meta/manifest/mcp-tool-tiers.json +435 -0
- package/lib/ai/registry/mcp-catalog.json +98 -0
- package/lib/commands/auth.js +55 -1
- package/lib/commands/compliance.js +1 -1
- package/lib/commands/detect.js +10 -4
- package/lib/commands/doctor.js +545 -26
- package/lib/commands/experience.js +40 -5
- package/lib/commands/export.js +73 -0
- package/lib/commands/feedback.js +157 -15
- package/lib/commands/gh.js +26 -0
- package/lib/commands/graph.js +9 -4
- package/lib/commands/heatmap.js +1 -1
- package/lib/commands/init.js +222 -30
- package/lib/commands/mcp.js +129 -21
- package/lib/commands/models.js +138 -41
- package/lib/commands/provider.js +30 -59
- package/lib/commands/quota.js +1 -1
- package/lib/commands/receipt.js +1 -1
- package/lib/commands/run.js +18 -7
- package/lib/commands/runner.js +31 -1
- package/lib/commands/status.js +44 -11
- package/lib/commands/swarm.js +130 -12
- package/lib/commands/trust.js +286 -0
- package/lib/commands/update.js +184 -38
- package/lib/commands/usage.js +1 -1
- package/lib/commands/validate.js +32 -3
- package/lib/commands/vault.js +46 -9
- package/lib/python/__init__.py +0 -0
- package/lib/python/agent_quotas.py +525 -0
- package/lib/python/anomaly_alert.py +397 -0
- package/lib/python/anti_pattern_detector.py +799 -0
- package/lib/python/auth.py +443 -0
- package/lib/python/capi_profile_guard.py +477 -0
- package/lib/python/compliance_report.py +581 -0
- package/lib/python/drift_detector.py +388 -0
- package/lib/python/experience_pipeline.py +1130 -0
- package/lib/python/graph.py +19 -0
- package/lib/python/graph_core.py +293 -0
- package/lib/python/graph_io.py +179 -0
- package/lib/python/graph_legacy.py +2052 -0
- package/lib/python/graph_legacy_helpers.py +221 -0
- package/lib/python/graph_outcomes_core.py +85 -0
- package/lib/python/graph_queries.py +171 -0
- package/lib/python/graph_slice.py +198 -0
- package/lib/python/graph_slicer.py +576 -0
- package/lib/python/graph_slicer_cli.py +60 -0
- package/lib/python/graph_validation.py +64 -0
- package/lib/python/heatmap.py +934 -0
- package/lib/python/json_utils.py +193 -0
- package/lib/python/mcp_exposure_check.py +247 -0
- package/lib/python/model_router.py +1434 -0
- package/lib/python/project_manager.py +621 -0
- package/lib/python/provider_profiles.py +1618 -0
- package/lib/python/provider_registry.py +1211 -0
- package/lib/python/provider_registry_cli.py +125 -0
- package/lib/python/receipt_png.py +727 -0
- package/lib/python/structural_memory.py +325 -0
- package/lib/python/swarm_cost.py +177 -0
- package/lib/python/usage_ledger.py +569 -0
- package/lib/scripts/mcp_tier_config.py +240 -0
- package/lib/shared.js +97 -14
- package/lib/tui/index.mjs +35174 -0
- package/lib/utils/activation_telemetry.js +230 -11
- package/lib/utils/constants.js +7 -1
- package/lib/utils/export-bundler.js +285 -0
- package/lib/utils/identity.js +198 -1
- package/lib/utils/mcp-auth.js +81 -15
- package/lib/utils/plan.js +1 -1
- package/lib/vault/index.js +19 -3
- package/lib/vault/storage.js +21 -2
- package/lib/wizard.js +5 -2
- package/package.json +9 -3
- package/scripts/build-python-bundle.js +106 -0
- package/scripts/build-tui.js +14 -1
- package/scripts/harvest_experience.py +523 -0
- package/scripts/postinstall.js +15 -9
package/lib/commands/update.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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(` ${
|
|
44
|
-
if (dryRun) {
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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 = {
|
|
210
|
+
module.exports = {
|
|
211
|
+
cmdUpdate,
|
|
212
|
+
installCommand,
|
|
213
|
+
latestCommand,
|
|
214
|
+
formatEntry,
|
|
215
|
+
};
|
package/lib/commands/usage.js
CHANGED
|
@@ -67,7 +67,7 @@ function cmdUsage(target, rawArgs = []) {
|
|
|
67
67
|
process.exit(1);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const script =
|
|
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}`);
|
package/lib/commands/validate.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
55
|
-
const
|
|
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
|
}
|
package/lib/commands/vault.js
CHANGED
|
@@ -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
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
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
|
-
|
|
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
|