@topolo/cli 0.13.2 → 0.13.4
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/agent-packs/README.md +81 -3
- package/agent-packs/coverage_changed.py +122 -0
- package/agent-packs/src/topolo_agent_pack/approvals.py +134 -0
- package/agent-packs/src/topolo_agent_pack/cli.py +49 -5
- package/agent-packs/src/topolo_agent_pack/execution.py +27 -9
- package/agent-packs/src/topolo_agent_pack/live.py +338 -5
- package/agent-packs/src/topolo_agent_pack/permissions.py +167 -0
- package/agent-packs/src/topolo_agent_pack/policy.py +11 -6
- package/agent-packs/src/topolo_agent_pack/topolo.py +264 -12
- package/dist/{claude-pack-backend-IDQQX3DG.js → claude-pack-backend-5DWMIDRA.js} +442 -10
- package/dist/index.js +48 -14
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/commands/agent-packs.ts
|
|
4
4
|
import { spawnSync } from "child_process";
|
|
5
|
-
import { existsSync } from "fs";
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
6
6
|
import { dirname, resolve } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import { homedir } from "os";
|
|
@@ -10,7 +10,7 @@ function registerAgentPacksCommand(program2) {
|
|
|
10
10
|
const packs = program2.command("agent-packs").description("Install and operate Topolo Agent Packs in supported local hosts").option("--store <path>", "Portable pack library (default ~/AgentPacks)");
|
|
11
11
|
packs.command("configure").description("Qualify and configure an adopted Claude roster through its live native backend").argument("<pack>").option("--harness <name>", "Host adapter", "claude").requiredOption("--policy <path>", "Reviewed execution policy JSON").requiredOption("--endpoint <url>", "Explicit loopback main-process inspector endpoint, enabled separately with authorization").requiredOption("--pid <pid>", "Expected Claude main-process PID").option("--apply", "Apply native groups, pins, models, effort and permission modes; otherwise preflight only").action(async (pack, options) => {
|
|
12
12
|
if (options.harness !== "claude") throw new Error("Native configure is currently qualified only for Claude Desktop.");
|
|
13
|
-
const { configureClaudePack } = await import("./claude-pack-backend-
|
|
13
|
+
const { configureClaudePack } = await import("./claude-pack-backend-5DWMIDRA.js");
|
|
14
14
|
const result = await configureClaudePack({
|
|
15
15
|
store: packs.opts().store ?? resolve(homedir(), "AgentPacks"),
|
|
16
16
|
pack,
|
|
@@ -34,6 +34,8 @@ function registerAgentPacksCommand(program2) {
|
|
|
34
34
|
"recover",
|
|
35
35
|
"doctor",
|
|
36
36
|
"policy",
|
|
37
|
+
"permissions",
|
|
38
|
+
"approvals",
|
|
37
39
|
"remote-recover",
|
|
38
40
|
"connect",
|
|
39
41
|
"remote-context",
|
|
@@ -44,12 +46,38 @@ function registerAgentPacksCommand(program2) {
|
|
|
44
46
|
for (const name of commands) {
|
|
45
47
|
const command = packs.command(name).description(name === "add" ? "Add an installed or bundled pack, group, or agent" : `Run the pack ${name} operation`);
|
|
46
48
|
if (name === "switch" || name === "recover") command.option("--policy <path>", "Reviewed policy for an adopted Claude roster").option("--endpoint <url>", "Explicit authorized Claude main-process inspector endpoint").option("--pid <pid>", "Expected Claude main-process PID").option("--apply", "Apply a native switch; recovery is always read-only");
|
|
49
|
+
if (name === "approvals") command.option("--intake <id>", "Exact owning native intake identity").option("--fingerprint <hash>", "Exact pending native request fingerprint").option("--response <path>", "Recorded one-request response JSON").option("--human-decision-recorded", "Assert that the exact human decision is recorded in the intake");
|
|
47
50
|
command.option("--harness <name>", "Local host adapter (codex, claude Desktop Code sessions)", "codex").option("--adapter-help", "Show every option supported by the selected adapter").argument("[arguments...]").allowUnknownOption().allowExcessArguments().addHelpText("after", name === "add" ? "\nExamples:\n topolo agent-packs add founder\n topolo agent-packs add founder --group specialists\n topolo agent-packs add founder --agent strategy\n" : "\nRun topolo agent-packs <command> --adapter-help for all adapter options. Cloud commands require explicit --environment and --context-key.\n").action(async (args, options) => {
|
|
48
51
|
if (!["codex", "claude"].includes(options.harness)) throw new Error(`The ${options.harness} pack adapter is not available. Supported adapters: codex, claude.`);
|
|
52
|
+
if (name === "approvals" && options.harness === "claude" && !options.adapterHelp) {
|
|
53
|
+
if (!["list", "details", "respond"].includes(args[0] ?? "") || args.length > 2)
|
|
54
|
+
throw new Error("Approvals requires list, details KEY, or respond KEY.");
|
|
55
|
+
if (args[0] !== "list" && (!args[1] || !options.intake || !options.fingerprint))
|
|
56
|
+
throw new Error("Exact request key, owning intake and fingerprint required.");
|
|
57
|
+
let decision;
|
|
58
|
+
if (args[0] === "respond") {
|
|
59
|
+
if (!options.response || !options.humanDecisionRecorded) throw new Error("Recorded human response required.");
|
|
60
|
+
const response = JSON.parse(readFileSync(options.response, "utf8"));
|
|
61
|
+
if (Object.keys(response).join(",") !== "decision" || !["once", "deny"].includes(response.decision))
|
|
62
|
+
throw new Error("Only a one-request once/deny response is supported.");
|
|
63
|
+
decision = response.decision;
|
|
64
|
+
}
|
|
65
|
+
const { operateClaudeApprovals } = await import("./claude-pack-backend-5DWMIDRA.js");
|
|
66
|
+
console.log(JSON.stringify(await operateClaudeApprovals({
|
|
67
|
+
store: packs.opts().store ?? resolve(homedir(), "AgentPacks"),
|
|
68
|
+
kind: args[0],
|
|
69
|
+
key: args[1],
|
|
70
|
+
intake: options.intake,
|
|
71
|
+
fingerprint: options.fingerprint,
|
|
72
|
+
decision,
|
|
73
|
+
humanDecisionRecorded: options.humanDecisionRecorded
|
|
74
|
+
}), null, 2));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
49
77
|
if (options.policy || options.endpoint || options.pid || options.apply) {
|
|
50
78
|
if (options.harness !== "claude" || !options.policy || !options.endpoint || !options.pid || args.length !== 1)
|
|
51
79
|
throw new Error("Native switch/recovery requires one Claude pack, --policy, --endpoint and --pid.");
|
|
52
|
-
const { configureClaudePack } = await import("./claude-pack-backend-
|
|
80
|
+
const { configureClaudePack } = await import("./claude-pack-backend-5DWMIDRA.js");
|
|
53
81
|
const result2 = await configureClaudePack({
|
|
54
82
|
store: packs.opts().store ?? resolve(homedir(), "AgentPacks"),
|
|
55
83
|
pack: args[0],
|
|
@@ -67,6 +95,12 @@ function registerAgentPacksCommand(program2) {
|
|
|
67
95
|
if (!adapter) throw new Error("This CLI installation is missing its bundled Topolo Agent Pack adapter. Reinstall @topolo/cli.");
|
|
68
96
|
const store = packs.opts().store;
|
|
69
97
|
const forwarded = (options.adapterHelp ? ["--help"] : args).map((value) => value === "--group" ? "--set" : value);
|
|
98
|
+
if (name === "approvals" && !options.adapterHelp) {
|
|
99
|
+
if (options.intake) forwarded.push("--intake", options.intake);
|
|
100
|
+
if (options.fingerprint) forwarded.push("--fingerprint", options.fingerprint);
|
|
101
|
+
if (options.response) forwarded.push("--response", options.response);
|
|
102
|
+
if (options.humanDecisionRecorded) forwarded.push("--human-decision-recorded");
|
|
103
|
+
}
|
|
70
104
|
const result = spawnSync("python3", [
|
|
71
105
|
adapter,
|
|
72
106
|
...store ? ["--store", store] : [],
|
|
@@ -3702,7 +3736,7 @@ async function pollUntilApproved(params) {
|
|
|
3702
3736
|
|
|
3703
3737
|
// node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
3704
3738
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, lstat, readdir as readdir2, rename as rename2, link } from "fs/promises";
|
|
3705
|
-
import { existsSync as existsSync2, lstatSync, readdirSync, readFileSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, rmdirSync } from "fs";
|
|
3739
|
+
import { existsSync as existsSync2, lstatSync, readdirSync, readFileSync as readFileSync2, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, rmdirSync } from "fs";
|
|
3706
3740
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3707
3741
|
import { homedir as homedir2, platform } from "os";
|
|
3708
3742
|
import { dirname as dirname2, join as join3 } from "path";
|
|
@@ -4622,7 +4656,7 @@ async function withConfigWriteLock(operation) {
|
|
|
4622
4656
|
assertDirectorySync();
|
|
4623
4657
|
const entries = readdirSync(lockPath);
|
|
4624
4658
|
const entry = lstatSync(markerPath);
|
|
4625
|
-
if (entries.length !== 1 || entries[0] !== markerName || !entry.isFile() || entry.isSymbolicLink() ||
|
|
4659
|
+
if (entries.length !== 1 || entries[0] !== markerName || !entry.isFile() || entry.isSymbolicLink() || readFileSync2(markerPath, "utf8") !== marker) throw new ConfigOwnershipError();
|
|
4626
4660
|
};
|
|
4627
4661
|
const persist = async (update) => {
|
|
4628
4662
|
const path = configPath();
|
|
@@ -4633,7 +4667,7 @@ async function withConfigWriteLock(operation) {
|
|
|
4633
4667
|
assertOwnedSync();
|
|
4634
4668
|
const entry = lstatSync(path);
|
|
4635
4669
|
if (!entry.isFile() || entry.isSymbolicLink()) throw new ConfigOwnershipError();
|
|
4636
|
-
const before =
|
|
4670
|
+
const before = readFileSync2(path, "utf8");
|
|
4637
4671
|
const updated = update(JSON.parse(before));
|
|
4638
4672
|
const fd = openSync(temporary, "wx", 384);
|
|
4639
4673
|
temporaryOwned = true;
|
|
@@ -4644,7 +4678,7 @@ async function withConfigWriteLock(operation) {
|
|
|
4644
4678
|
closeSync(fd);
|
|
4645
4679
|
}
|
|
4646
4680
|
assertOwnedSync();
|
|
4647
|
-
if (
|
|
4681
|
+
if (readFileSync2(path, "utf8") !== before) throw new ConfigOwnershipError();
|
|
4648
4682
|
renameSync(temporary, path);
|
|
4649
4683
|
temporaryOwned = false;
|
|
4650
4684
|
} finally {
|
|
@@ -4661,7 +4695,7 @@ async function withConfigWriteLock(operation) {
|
|
|
4661
4695
|
await rename2(markerPath, captured);
|
|
4662
4696
|
assertDirectorySync();
|
|
4663
4697
|
const entry = lstatSync(captured);
|
|
4664
|
-
if (!entry.isFile() || entry.isSymbolicLink() ||
|
|
4698
|
+
if (!entry.isFile() || entry.isSymbolicLink() || readFileSync2(captured, "utf8") !== marker || readdirSync(lockPath).length) throw new ConfigOwnershipError();
|
|
4665
4699
|
unlinkSync(captured);
|
|
4666
4700
|
rmdirSync(quarantine);
|
|
4667
4701
|
assertDirectorySync();
|
|
@@ -4985,7 +5019,7 @@ function normalizeError(err) {
|
|
|
4985
5019
|
}
|
|
4986
5020
|
|
|
4987
5021
|
// src/version.ts
|
|
4988
|
-
import { readFileSync as
|
|
5022
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
4989
5023
|
import { dirname as dirname4, join as join4 } from "path";
|
|
4990
5024
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4991
5025
|
function readCliVersion() {
|
|
@@ -4997,7 +5031,7 @@ function readCliVersion() {
|
|
|
4997
5031
|
];
|
|
4998
5032
|
for (const path of candidates) {
|
|
4999
5033
|
try {
|
|
5000
|
-
const pkg = JSON.parse(
|
|
5034
|
+
const pkg = JSON.parse(readFileSync3(path, "utf8"));
|
|
5001
5035
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
5002
5036
|
} catch {
|
|
5003
5037
|
}
|
|
@@ -5333,19 +5367,19 @@ function formatPretty(info) {
|
|
|
5333
5367
|
|
|
5334
5368
|
// src/commands/actions.ts
|
|
5335
5369
|
import { spawn as spawn2 } from "child_process";
|
|
5336
|
-
import { readFileSync as
|
|
5370
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
5337
5371
|
import { platform as platform3 } from "os";
|
|
5338
5372
|
import { basename, extname } from "path";
|
|
5339
5373
|
|
|
5340
5374
|
// src/json-input.ts
|
|
5341
|
-
import { readFileSync as
|
|
5375
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
5342
5376
|
function parseJsonInput(value) {
|
|
5343
5377
|
let source = value;
|
|
5344
5378
|
if (value.startsWith("@")) {
|
|
5345
5379
|
const path = value.slice(1);
|
|
5346
5380
|
if (!path) throw new Error("--data @file path must not be empty");
|
|
5347
5381
|
try {
|
|
5348
|
-
source =
|
|
5382
|
+
source = readFileSync4(path, "utf8");
|
|
5349
5383
|
} catch (err) {
|
|
5350
5384
|
throw new Error(`Could not read --data file ${path}: ${err.message}`);
|
|
5351
5385
|
}
|
|
@@ -5542,7 +5576,7 @@ Approval required. ${opened ? "Opening" : "Open"} ${approval.verificationUrl}` :
|
|
|
5542
5576
|
const resource = parseResourceOption(opts.resourceType, opts.resourceId);
|
|
5543
5577
|
const result = await topolo.client.uploadAction(
|
|
5544
5578
|
action,
|
|
5545
|
-
|
|
5579
|
+
readFileSync5(opts.file),
|
|
5546
5580
|
parseJsonObject(opts.data || "{}"),
|
|
5547
5581
|
{
|
|
5548
5582
|
fileName: basename(opts.file),
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
3
|
"packageManager": "pnpm@10.17.1",
|
|
4
|
-
"version": "0.13.
|
|
5
|
-
"topoloPublishedVersion": "0.13.
|
|
4
|
+
"version": "0.13.4",
|
|
5
|
+
"topoloPublishedVersion": "0.13.4",
|
|
6
6
|
"description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"author": "Topolo",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test:watch": "vitest",
|
|
28
28
|
"coverage": "pnpm run coverage:node && pnpm run coverage:adapter",
|
|
29
29
|
"coverage:node": "vitest run --coverage",
|
|
30
|
-
"coverage:adapter": "python3 -m venv node_modules/.cache/agent-pack-test && node_modules/.cache/agent-pack-test/bin/python -m pip install --disable-pip-version-check --quiet --timeout 15 --retries 0 -r agent-packs/requirements-test.txt && PYTHONPATH=agent-packs/src COVERAGE_FILE=node_modules/.cache/agent-pack-test/.coverage node_modules/.cache/agent-pack-test/bin/python -m coverage run --branch --source=topolo_agent_pack.topolo,topolo_agent_pack.claude,topolo_agent_pack.policy -m unittest discover -s agent-packs/tests && COVERAGE_FILE=node_modules/.cache/agent-pack-test/.coverage node_modules/.cache/agent-pack-test/bin/python -m coverage report --fail-under=100"
|
|
30
|
+
"coverage:adapter": "python3 -m venv node_modules/.cache/agent-pack-test && node_modules/.cache/agent-pack-test/bin/python -m pip install --disable-pip-version-check --quiet --timeout 15 --retries 0 -r agent-packs/requirements-test.txt && PYTHONPATH=agent-packs/src:agent-packs COVERAGE_FILE=node_modules/.cache/agent-pack-test/.coverage node_modules/.cache/agent-pack-test/bin/python -m coverage run --branch --source=topolo_agent_pack.topolo,topolo_agent_pack.claude,topolo_agent_pack.policy,topolo_agent_pack.permissions,topolo_agent_pack.approvals,topolo_agent_pack.execution,topolo_agent_pack.live,topolo_agent_pack.cli,coverage_changed -m unittest discover -s agent-packs/tests && COVERAGE_FILE=node_modules/.cache/agent-pack-test/.coverage node_modules/.cache/agent-pack-test/bin/python -m coverage report && COVERAGE_FILE=node_modules/.cache/agent-pack-test/.coverage node_modules/.cache/agent-pack-test/bin/python -m coverage report --include='*/topolo_agent_pack/topolo.py,*/topolo_agent_pack/claude.py,*/topolo_agent_pack/policy.py,*/topolo_agent_pack/permissions.py,*/topolo_agent_pack/approvals.py,*/topolo_agent_pack/execution.py,*/agent-packs/coverage_changed.py' --fail-under=100 && COVERAGE_FILE=node_modules/.cache/agent-pack-test/.coverage node_modules/.cache/agent-pack-test/bin/python -m coverage json -o node_modules/.cache/agent-pack-test/coverage.json && node_modules/.cache/agent-pack-test/bin/python agent-packs/coverage_changed.py"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.0.0",
|