@aarmos/cli 0.2.1 → 0.3.0
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/index.js +196 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command10 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -55,6 +55,62 @@ var GITIGNORE_TEMPLATE = `# Aarmos local state
|
|
|
55
55
|
.aarmos/keys/
|
|
56
56
|
.aarmos/avar/
|
|
57
57
|
`;
|
|
58
|
+
var TEMPLATES = {
|
|
59
|
+
hello: {
|
|
60
|
+
description: "Minimal \u2014 policy + a single echo playbook. Great first run.",
|
|
61
|
+
files: {
|
|
62
|
+
"playbooks/hello.md": '# hello\n\nOne-step playbook. Ask the agent to echo a greeting.\n\n## steps\n\n1. Say: "hello from aarmos"\n',
|
|
63
|
+
"policy.aarmos.toml": `# hello template \u2014 deny-by-default, no external tools.
|
|
64
|
+
[defaults]
|
|
65
|
+
gates.destructive = "deny"
|
|
66
|
+
rate.per_minute = 30
|
|
67
|
+
`
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
mcp: {
|
|
71
|
+
description: "MCP starter \u2014 one MCP adapter wired with a read-only scope.",
|
|
72
|
+
files: {
|
|
73
|
+
"policy.aarmos.toml": `# mcp template \u2014 one MCP adapter, read-only scope.
|
|
74
|
+
[defaults]
|
|
75
|
+
gates.destructive = "confirm"
|
|
76
|
+
rate.per_minute = 60
|
|
77
|
+
|
|
78
|
+
[[adapters.mcp]]
|
|
79
|
+
name = "example"
|
|
80
|
+
url = "https://mcp.example.com/server"
|
|
81
|
+
scopes = ["read:*"]
|
|
82
|
+
`,
|
|
83
|
+
"playbooks/mcp-probe.md": "# mcp-probe\n\nList tools exposed by the configured MCP server and print their schemas. Read-only.\n"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
policy: {
|
|
87
|
+
description: "ASP starter \u2014 YAML policy-as-code with a golden test.",
|
|
88
|
+
files: {
|
|
89
|
+
"asp.yaml": `# ASP \u2014 Agent Security Policy (draft)
|
|
90
|
+
version: asp/1
|
|
91
|
+
defaults:
|
|
92
|
+
gates:
|
|
93
|
+
destructive: confirm
|
|
94
|
+
rate:
|
|
95
|
+
per_minute: 60
|
|
96
|
+
rules:
|
|
97
|
+
- id: deny-shell
|
|
98
|
+
when:
|
|
99
|
+
tool: "shell.*"
|
|
100
|
+
decision: deny
|
|
101
|
+
reason: "Shell tools are not permitted in this workspace."
|
|
102
|
+
`,
|
|
103
|
+
"policies/tests/deny-shell.yaml": `# golden test \u2014 shell.* must be denied
|
|
104
|
+
input:
|
|
105
|
+
tool: shell.exec
|
|
106
|
+
args: { cmd: "ls" }
|
|
107
|
+
expect:
|
|
108
|
+
decision: deny
|
|
109
|
+
source: workspace-policy
|
|
110
|
+
`
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
58
114
|
var IMPORT_ALLOWLIST = [
|
|
59
115
|
"policy.aarmos.toml",
|
|
60
116
|
"avar.config.json",
|
|
@@ -66,6 +122,9 @@ var IMPORT_ALLOWLIST = [
|
|
|
66
122
|
];
|
|
67
123
|
function initCommand() {
|
|
68
124
|
return new Command("init").description("Scaffold policy.aarmos.toml, avar.config.json, and local signing key").option("--force", "Overwrite existing files", false).option(
|
|
125
|
+
"--template <name>",
|
|
126
|
+
`Embedded starter template: ${Object.keys(TEMPLATES).join(" | ")}. Writes template files before defaults; existing files are preserved unless --force.`
|
|
127
|
+
).option(
|
|
69
128
|
"--from <repo>",
|
|
70
129
|
"Seed policies/playbooks from a GitHub repo (owner/repo[#ref][/subdir]) or tarball URL. Only Aarmos config files are copied; source code and keys are never imported."
|
|
71
130
|
).action(async (opts) => {
|
|
@@ -74,6 +133,21 @@ function initCommand() {
|
|
|
74
133
|
const chainDir = join(cwd, ".aarmos", "avar");
|
|
75
134
|
mkdirSync(keyDir, { recursive: true });
|
|
76
135
|
mkdirSync(chainDir, { recursive: true });
|
|
136
|
+
if (opts.template) {
|
|
137
|
+
const name = opts.template;
|
|
138
|
+
const tpl = TEMPLATES[name];
|
|
139
|
+
if (!tpl) {
|
|
140
|
+
console.error(
|
|
141
|
+
`\u2717 unknown template "${opts.template}". Known: ${Object.keys(TEMPLATES).join(", ")}`
|
|
142
|
+
);
|
|
143
|
+
process.exitCode = 1;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
console.log(`\u25B8 template: ${name} \u2014 ${tpl.description}`);
|
|
147
|
+
for (const [rel, contents] of Object.entries(tpl.files)) {
|
|
148
|
+
writeIfMissing(join(cwd, rel), contents, opts.force);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
77
151
|
if (opts.from) {
|
|
78
152
|
await seedFromRepo(opts.from, cwd, opts.force);
|
|
79
153
|
}
|
|
@@ -1409,8 +1483,127 @@ AARMOS_POLICY_PRIVATE_KEY (keep secret):
|
|
|
1409
1483
|
return cmd;
|
|
1410
1484
|
}
|
|
1411
1485
|
|
|
1486
|
+
// src/commands/backup.ts
|
|
1487
|
+
import { Command as Command9 } from "commander";
|
|
1488
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
1489
|
+
function verifyBundle2(bytes) {
|
|
1490
|
+
let parsed;
|
|
1491
|
+
try {
|
|
1492
|
+
parsed = JSON.parse(bytes.toString("utf8"));
|
|
1493
|
+
} catch (e) {
|
|
1494
|
+
return {
|
|
1495
|
+
ok: false,
|
|
1496
|
+
code: "BUNDLE_INVALID_JSON",
|
|
1497
|
+
error: `invalid JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1501
|
+
return { ok: false, code: "BUNDLE_INVALID_SHAPE", error: "bundle must be a JSON object" };
|
|
1502
|
+
}
|
|
1503
|
+
if (parsed.kind !== "aarmos-ledger-bundle") {
|
|
1504
|
+
return {
|
|
1505
|
+
ok: false,
|
|
1506
|
+
code: "BUNDLE_WRONG_KIND",
|
|
1507
|
+
error: `expected kind "aarmos-ledger-bundle", got ${JSON.stringify(parsed.kind)}`
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
if (parsed.version !== 1) {
|
|
1511
|
+
return {
|
|
1512
|
+
ok: false,
|
|
1513
|
+
code: "BUNDLE_UNSUPPORTED_VERSION",
|
|
1514
|
+
error: `unsupported bundle version ${parsed.version}`
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
if (!parsed.workspaceId || typeof parsed.workspaceId !== "string") {
|
|
1518
|
+
return { ok: false, code: "BUNDLE_MISSING_WORKSPACE", error: "workspaceId missing" };
|
|
1519
|
+
}
|
|
1520
|
+
if (!parsed.exportedAt || Number.isNaN(Date.parse(parsed.exportedAt))) {
|
|
1521
|
+
return { ok: false, code: "BUNDLE_BAD_TIMESTAMP", error: "exportedAt is not a valid ISO date" };
|
|
1522
|
+
}
|
|
1523
|
+
const settings = Array.isArray(parsed.settings) ? parsed.settings : null;
|
|
1524
|
+
const traces = Array.isArray(parsed.traces) ? parsed.traces : null;
|
|
1525
|
+
if (!settings || !traces) {
|
|
1526
|
+
return {
|
|
1527
|
+
ok: false,
|
|
1528
|
+
code: "BUNDLE_MISSING_ARRAYS",
|
|
1529
|
+
error: "settings and traces must both be arrays"
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
const warnings = [];
|
|
1533
|
+
const declared = parsed.counts ?? {};
|
|
1534
|
+
if (typeof declared.settings === "number" && declared.settings !== settings.length) {
|
|
1535
|
+
warnings.push(
|
|
1536
|
+
`counts.settings=${declared.settings} but bundle contains ${settings.length} entries`
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
if (typeof declared.traces === "number" && declared.traces !== traces.length) {
|
|
1540
|
+
warnings.push(
|
|
1541
|
+
`counts.traces=${declared.traces} but bundle contains ${traces.length} entries`
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
if (settings.length === 0 && traces.length === 0) {
|
|
1545
|
+
warnings.push("bundle is empty \u2014 nothing to restore from");
|
|
1546
|
+
}
|
|
1547
|
+
return {
|
|
1548
|
+
ok: true,
|
|
1549
|
+
workspaceId: parsed.workspaceId,
|
|
1550
|
+
exportedAt: parsed.exportedAt,
|
|
1551
|
+
counts: { settings: settings.length, traces: traces.length },
|
|
1552
|
+
warnings
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
function runBackupVerify(opts) {
|
|
1556
|
+
const { file, json = false, quiet = false } = opts;
|
|
1557
|
+
if (!existsSync9(file)) {
|
|
1558
|
+
if (json) {
|
|
1559
|
+
process.stdout.write(
|
|
1560
|
+
JSON.stringify({ ok: false, code: "FILE_NOT_FOUND", error: `not found: ${file}` }) + "\n"
|
|
1561
|
+
);
|
|
1562
|
+
} else if (!quiet) {
|
|
1563
|
+
process.stderr.write(`\u2717 file not found: ${file}
|
|
1564
|
+
`);
|
|
1565
|
+
}
|
|
1566
|
+
return 2;
|
|
1567
|
+
}
|
|
1568
|
+
const result = verifyBundle2(readFileSync9(file));
|
|
1569
|
+
if (json) {
|
|
1570
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
1571
|
+
return result.ok ? 0 : 1;
|
|
1572
|
+
}
|
|
1573
|
+
if (!result.ok) {
|
|
1574
|
+
if (!quiet) process.stderr.write(`\u2717 [${result.code}] ${result.error}
|
|
1575
|
+
`);
|
|
1576
|
+
return 1;
|
|
1577
|
+
}
|
|
1578
|
+
if (!quiet) {
|
|
1579
|
+
process.stdout.write(`\u2713 ledger bundle valid
|
|
1580
|
+
`);
|
|
1581
|
+
process.stdout.write(` workspace: ${result.workspaceId}
|
|
1582
|
+
`);
|
|
1583
|
+
process.stdout.write(` exported: ${result.exportedAt}
|
|
1584
|
+
`);
|
|
1585
|
+
process.stdout.write(` settings: ${result.counts.settings}
|
|
1586
|
+
`);
|
|
1587
|
+
process.stdout.write(` traces: ${result.counts.traces}
|
|
1588
|
+
`);
|
|
1589
|
+
for (const w of result.warnings) process.stdout.write(` ! ${w}
|
|
1590
|
+
`);
|
|
1591
|
+
process.stdout.write("\nverified locally \u2014 no Aarmos service was contacted.\n");
|
|
1592
|
+
}
|
|
1593
|
+
return 0;
|
|
1594
|
+
}
|
|
1595
|
+
function backupCommand() {
|
|
1596
|
+
const cmd = new Command9("backup").description(
|
|
1597
|
+
"Inspect and verify Aarmos ledger backup bundles exported from the app (Settings \u2192 Ledger backup)."
|
|
1598
|
+
);
|
|
1599
|
+
cmd.command("verify").description("Verify a ledger bundle JSON offline (schema + counts).").argument("<bundle>", "Path to aarmos-ledger-*.json exported from the app").option("--json", "Emit machine-readable JSON on stdout").option("--quiet", "Suppress non-error output; exit code carries the verdict").action((bundle, opts) => {
|
|
1600
|
+
process.exit(runBackupVerify({ file: bundle, json: opts.json, quiet: opts.quiet }));
|
|
1601
|
+
});
|
|
1602
|
+
return cmd;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1412
1605
|
// src/index.ts
|
|
1413
|
-
var program = new
|
|
1606
|
+
var program = new Command10();
|
|
1414
1607
|
program.name("aarmos").description(
|
|
1415
1608
|
"Run your agent locally, across any protocol, with a signed receipt \u2014 in 60 seconds."
|
|
1416
1609
|
).version("0.1.0");
|
|
@@ -1422,6 +1615,7 @@ program.addCommand(verifyCommand());
|
|
|
1422
1615
|
program.addCommand(lintCommand());
|
|
1423
1616
|
program.addCommand(testCommand());
|
|
1424
1617
|
program.addCommand(policyCommand());
|
|
1618
|
+
program.addCommand(backupCommand());
|
|
1425
1619
|
program.parseAsync(process.argv).catch((err) => {
|
|
1426
1620
|
console.error(err instanceof Error ? err.message : String(err));
|
|
1427
1621
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aarmos/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Aarmos CLI — run any agent locally, across any protocol (MCP, OpenAPI, deep-link), with a signed AVAR receipt on every execution. Sovereign runtime, universal tool gateway, verifiable governance.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aarmos",
|