@atbash/cli 0.5.15-dev.8 → 0.5.15
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 +0 -77
- package/dist/bin/atbash.js +1 -7
- package/dist/bin/atbash.js.map +1 -1
- package/dist/commands/connect.d.ts +0 -64
- package/dist/commands/connect.js +56 -107
- package/dist/commands/connect.js.map +1 -1
- package/package.json +3 -3
- package/dist/commands/mcp-cmd.d.ts +0 -13
- package/dist/commands/mcp-cmd.js +0 -216
- package/dist/commands/mcp-cmd.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -359
- package/dist/commands/setup.js +0 -1763
- package/dist/commands/setup.js.map +0 -1
- package/dist/shared/atbash-targets.d.ts +0 -49
- package/dist/shared/atbash-targets.js +0 -63
- package/dist/shared/atbash-targets.js.map +0 -1
package/dist/commands/mcp-cmd.js
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.resolveLauncherKey = resolveLauncherKey;
|
|
37
|
-
exports.registerMcpCommand = registerMcpCommand;
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
const child_process_1 = require("child_process");
|
|
40
|
-
const sdk_1 = require("@atbash/sdk");
|
|
41
|
-
/**
|
|
42
|
-
* `atbash mcp` — run the Atbash MCP server without putting a private key in a
|
|
43
|
-
* config file.
|
|
44
|
-
*
|
|
45
|
-
* THE PROBLEM THIS SOLVES
|
|
46
|
-
*
|
|
47
|
-
* `@atbash/mcp` reads its agent identity from `ATBASH_AGENT_PRIVKEY` and has no
|
|
48
|
-
* key-file fallback (verified against the published 0.1.3). The documented wiring
|
|
49
|
-
* therefore asks the operator to paste a raw private key into their MCP client's
|
|
50
|
-
* own config:
|
|
51
|
-
*
|
|
52
|
-
* { "mcpServers": { "atbash": { "command": "npx", "args": ["-y", "@atbash/mcp"],
|
|
53
|
-
* "env": { "ATBASH_AGENT_PRIVKEY": "<the key>" } } } }
|
|
54
|
-
*
|
|
55
|
-
* That file is `claude_desktop_config.json` and its equivalents — files people
|
|
56
|
-
* sync between machines, paste into issues, and screenshot when asking for help.
|
|
57
|
-
* It made "wire up MCP for me" impossible to automate honestly: the automation's
|
|
58
|
-
* whole job would have been to plant a secret somewhere worse than where it
|
|
59
|
-
* already was.
|
|
60
|
-
*
|
|
61
|
-
* This command is the indirection that removes the problem. The client spawns
|
|
62
|
-
* `atbash mcp`, which reads the key from the 0600 file Atbash already manages and
|
|
63
|
-
* passes it to the server through the CHILD PROCESS ENVIRONMENT ONLY. Nothing
|
|
64
|
-
* secret is ever written to a config file, so the client entry is:
|
|
65
|
-
*
|
|
66
|
-
* { "mcpServers": { "atbash": { "command": "npx", "args": ["-y", "@atbash/cli", "mcp"] } } }
|
|
67
|
-
*
|
|
68
|
-
* — which contains no credential at all, and is safe for `atbash setup` to write.
|
|
69
|
-
*
|
|
70
|
-
* STDOUT IS THE PROTOCOL. MCP speaks JSON-RPC over stdio, so a single stray
|
|
71
|
-
* `console.log` here corrupts the stream and the client reports a broken server.
|
|
72
|
-
* Every diagnostic in this file goes to stderr. Do not change that.
|
|
73
|
-
*/
|
|
74
|
-
/**
|
|
75
|
-
* The server this launcher runs, and the SDK it is run WITH.
|
|
76
|
-
*
|
|
77
|
-
* ⚠️ BOTH PINS ARE LOAD-BEARING. DO NOT LOOSEN THEM.
|
|
78
|
-
*
|
|
79
|
-
* `@atbash/mcp` declares `@atbash/sdk: ^0.3.19` and imports `getOrgTierInfo` as a
|
|
80
|
-
* named export. `@atbash/sdk@0.3.24` REMOVED that export, so `^0.3.19` now
|
|
81
|
-
* resolves to 0.3.25 and the server dies at import time:
|
|
82
|
-
*
|
|
83
|
-
* SyntaxError: The requested module '@atbash/sdk' does not provide an
|
|
84
|
-
* export named 'getOrgTierInfo'
|
|
85
|
-
*
|
|
86
|
-
* That breaks EVERY published `@atbash/mcp` (0.1.0 through 0.1.3) — a plain
|
|
87
|
-
* `npm install @atbash/mcp` cannot start at all, which is why the documented
|
|
88
|
-
* hand-wiring produces a client reporting a failed server. Naming an SDK version
|
|
89
|
-
* from before the removal is what makes it run; 0.3.23 is the newest that works.
|
|
90
|
-
*
|
|
91
|
-
* ⚠️ AND THE PIN ITSELF IS A STOPGAP. `@atbash/sdk@0.3.23` is DEPRECATED:
|
|
92
|
-
*
|
|
93
|
-
* npm warn deprecated @atbash/sdk@0.3.23: Old architecture with legacy chain
|
|
94
|
-
* structure. Please upgrade to stable version @atbash/sdk@^0.5.0
|
|
95
|
-
*
|
|
96
|
-
* So this runs the MCP server on a legacy chain architecture. It is verified to
|
|
97
|
-
* work — a real `atbash_check_agent` tool call round-trips against the live
|
|
98
|
-
* deployment and returns the correct answer — but it is the newest thing that
|
|
99
|
-
* works, not a thing that is right.
|
|
100
|
-
*
|
|
101
|
-
* THE REAL FIX is republishing `@atbash/mcp` against `@atbash/sdk@^0.7`, which
|
|
102
|
-
* moves `getOrgTierInfo` onto the `Atbash` class rather than exporting it. Until
|
|
103
|
-
* that ships, this pin is the only thing between an operator and a dead MCP
|
|
104
|
-
* server, so treat a "dependency update" here as a regression unless the
|
|
105
|
-
* republished package is verified to both START and serve a tool call.
|
|
106
|
-
*
|
|
107
|
-
* Cold start is ~12s while npx populates its cache, ~1s warm. A client with a
|
|
108
|
-
* short startup timeout may fail its very first launch and succeed on retry.
|
|
109
|
-
*/
|
|
110
|
-
const MCP_PKG = "@atbash/mcp";
|
|
111
|
-
const MCP_VERSION = "0.1.3";
|
|
112
|
-
const MCP_SDK_VERSION = "0.3.23";
|
|
113
|
-
/** Read `privkey=` out of the agent key file, or null if it is not usable. */
|
|
114
|
-
function privkeyFromKeyFile(keyPath) {
|
|
115
|
-
let text;
|
|
116
|
-
try {
|
|
117
|
-
text = fs.readFileSync(keyPath, "utf8");
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
if (text.trim().startsWith("{")) {
|
|
123
|
-
try {
|
|
124
|
-
const o = JSON.parse(text);
|
|
125
|
-
const value = String(o.privKey ?? o.privkey ?? o.privateKey ?? "").replace(/^0x/i, "").trim();
|
|
126
|
-
return /^[0-9a-fA-F]{64}$/.test(value) ? value.toLowerCase() : null;
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
for (const line of text.split(/\r?\n/)) {
|
|
133
|
-
const match = line.trim().match(/^privkey\s*[:=]\s*(.+)$/i);
|
|
134
|
-
if (match) {
|
|
135
|
-
const value = match[1].replace(/^0x/i, "").trim();
|
|
136
|
-
if (/^[0-9a-fA-F]{64}$/.test(value))
|
|
137
|
-
return value.toLowerCase();
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Where the launcher gets the key, in order.
|
|
144
|
-
*
|
|
145
|
-
* An `ATBASH_AGENT_PRIVKEY` already in the environment wins, so an operator who
|
|
146
|
-
* deliberately injects the key at launch time keeps working exactly as before —
|
|
147
|
-
* this command adds a safer default, it does not take the existing path away.
|
|
148
|
-
*/
|
|
149
|
-
function resolveLauncherKey(keyPathOverride) {
|
|
150
|
-
const fromEnv = process.env.ATBASH_AGENT_PRIVKEY?.replace(/^0x/i, "").trim();
|
|
151
|
-
if (fromEnv && /^[0-9a-fA-F]{64}$/.test(fromEnv))
|
|
152
|
-
return { privkey: fromEnv.toLowerCase(), from: "ATBASH_AGENT_PRIVKEY" };
|
|
153
|
-
const keyPath = (0, sdk_1.resolveKeyPath)(keyPathOverride);
|
|
154
|
-
const fromFile = privkeyFromKeyFile(keyPath);
|
|
155
|
-
if (fromFile)
|
|
156
|
-
return { privkey: fromFile, from: keyPath };
|
|
157
|
-
const fromConfig = (0, sdk_1.loadUserConfig)().agentKey?.replace(/^0x/i, "").trim();
|
|
158
|
-
if (fromConfig && /^[0-9a-fA-F]{64}$/.test(fromConfig))
|
|
159
|
-
return { privkey: fromConfig.toLowerCase(), from: "atbash config" };
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
function registerMcpCommand(program) {
|
|
163
|
-
program
|
|
164
|
-
.command("mcp")
|
|
165
|
-
.description("Run the Atbash MCP server, reading the agent key from ~/.config/atbash/guard-client-key instead of a config file (used as the `command` in an MCP client's server list)")
|
|
166
|
-
.option("--key-file <path>", "Agent key file to read (default: ~/.config/atbash/guard-client-key)")
|
|
167
|
-
.option("--endpoint <url>", "Atbash endpoint the server should use")
|
|
168
|
-
.action((opts) => {
|
|
169
|
-
const resolved = resolveLauncherKey(opts.keyFile);
|
|
170
|
-
if (!resolved) {
|
|
171
|
-
// stderr: stdout belongs to the JSON-RPC stream.
|
|
172
|
-
process.stderr.write("atbash mcp: no agent key found.\n" +
|
|
173
|
-
`Looked at ATBASH_AGENT_PRIVKEY, ${(0, sdk_1.resolveKeyPath)(opts.keyFile)}, and the atbash config.\n` +
|
|
174
|
-
"Run `atbash setup` on this machine first, or pass --key-file.\n");
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
177
|
-
const endpoint = opts.endpoint || process.env.ATBASH_ENDPOINT;
|
|
178
|
-
// The key goes to the CHILD ONLY. It is never written to disk here and
|
|
179
|
-
// never echoed — the parent's own environment is left untouched.
|
|
180
|
-
const env = {
|
|
181
|
-
...process.env,
|
|
182
|
-
ATBASH_AGENT_PRIVKEY: resolved.privkey,
|
|
183
|
-
...(endpoint ? { ATBASH_ENDPOINT: endpoint } : {}),
|
|
184
|
-
};
|
|
185
|
-
// Both packages are placed in ONE npx tree so the compatible SDK sits at its
|
|
186
|
-
// root and satisfies @atbash/mcp's own `^0.3.19` range — which is what stops
|
|
187
|
-
// npm installing the broken 0.3.25 underneath it. Installing @atbash/mcp
|
|
188
|
-
// alone (as a dependency of this CLI, or via a bare npx) reintroduces the
|
|
189
|
-
// crash, so this shape is deliberate, not incidental.
|
|
190
|
-
const command = "npx";
|
|
191
|
-
const args = [
|
|
192
|
-
"--yes",
|
|
193
|
-
"-p", `${MCP_PKG}@${MCP_VERSION}`,
|
|
194
|
-
"-p", `@atbash/sdk@${MCP_SDK_VERSION}`,
|
|
195
|
-
"atbash-mcp",
|
|
196
|
-
];
|
|
197
|
-
process.stderr.write(`atbash mcp: starting ${MCP_PKG}@${MCP_VERSION} with @atbash/sdk@${MCP_SDK_VERSION} (key from ${resolved.from})\n`);
|
|
198
|
-
// stdio inherit: the server talks JSON-RPC directly to the client that
|
|
199
|
-
// spawned us. Proxying it through here would add a failure point for no gain.
|
|
200
|
-
const child = (0, child_process_1.spawn)(command, args, { env, stdio: "inherit", shell: false });
|
|
201
|
-
child.on("error", (err) => {
|
|
202
|
-
process.stderr.write(`atbash mcp: could not start ${MCP_PKG}: ${err.message}\n`);
|
|
203
|
-
process.exit(1);
|
|
204
|
-
});
|
|
205
|
-
// Forward termination so the client's own shutdown reaches the server
|
|
206
|
-
// rather than orphaning it.
|
|
207
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
208
|
-
process.on(signal, () => { if (!child.killed)
|
|
209
|
-
child.kill(signal); });
|
|
210
|
-
}
|
|
211
|
-
child.on("exit", (code, signal) => {
|
|
212
|
-
process.exit(signal ? 1 : (code ?? 0));
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
//# sourceMappingURL=mcp-cmd.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-cmd.js","sourceRoot":"","sources":["../../src/commands/mcp-cmd.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/setup.d.ts
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
interface KeyMaterial {
|
|
3
|
-
privkey: string;
|
|
4
|
-
/** Present when the source stated one; always re-derived and cross-checked. */
|
|
5
|
-
statedPubkey?: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Pull key material out of whatever the owner pointed us at.
|
|
9
|
-
*
|
|
10
|
-
* Accepts every shape Atbash itself produces or documents, so "the file I
|
|
11
|
-
* downloaded from the modal" always works:
|
|
12
|
-
* - `privkey=…` / `pubkey=…` lines (what onboarding downloads, and what the
|
|
13
|
-
* plugin parses)
|
|
14
|
-
* - `{"privKey":…,"pubKey":…}` JSON (the alternate documented key-file form)
|
|
15
|
-
* - a bare 64-hex private key on its own line (someone who copied just the key)
|
|
16
|
-
*
|
|
17
|
-
* Returns null rather than throwing: the caller tries several sources in turn and
|
|
18
|
-
* an unparseable one is a reason to move on, not to abort.
|
|
19
|
-
*/
|
|
20
|
-
export declare function parseKeyMaterial(raw: string): KeyMaterial | null;
|
|
21
|
-
/** Normalize to the lowercase 64-hex the SDK validates, or "" if it is not one. */
|
|
22
|
-
export declare function normalizePrivkey(raw: string): string;
|
|
23
|
-
/**
|
|
24
|
-
* Files in a directory that plausibly hold an Atbash agent key, newest first.
|
|
25
|
-
*
|
|
26
|
-
* TWO PASSES, and the second one is the point.
|
|
27
|
-
*
|
|
28
|
-
* By name first — `guard-client-key`, `agent-keys-*.txt` and friends — because
|
|
29
|
-
* matching the name is cheap and unambiguous. But a name-only match is a cliff:
|
|
30
|
-
* rename the download, or export from a wallet UI that picks its own filename,
|
|
31
|
-
* and the operator gets "no key file found" while the key sits right there in the
|
|
32
|
-
* directory they explicitly pointed at.
|
|
33
|
-
*
|
|
34
|
-
* So if no name matches, read the small files and keep the ones that actually
|
|
35
|
-
* PARSE as key material. That is a narrow test — `privkey=`, the documented JSON
|
|
36
|
-
* shape, or a file that is nothing but a 64-hex key — not "contains something
|
|
37
|
-
* hex-looking", so an unrelated file does not get mistaken for an identity.
|
|
38
|
-
*
|
|
39
|
-
* Reading files the operator did not name individually is justified by the flag
|
|
40
|
-
* itself: `--keys-dir` is an explicit instruction to look in that directory. It
|
|
41
|
-
* is bounded to small regular files and a file count, nothing is transmitted, and
|
|
42
|
-
* the caller prints WHICH file it used before doing anything with it.
|
|
43
|
-
*/
|
|
44
|
-
export declare function keyCandidatesInDir(dir: string): string[];
|
|
45
|
-
interface KeySource {
|
|
46
|
-
material: KeyMaterial;
|
|
47
|
-
/** Human description of WHERE it came from. Never contains the key. */
|
|
48
|
-
from: string;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Find the agent key, trying every way an owner could plausibly have it.
|
|
52
|
-
*
|
|
53
|
-
* Order is "most explicit first": a flag the owner typed beats a file we guessed
|
|
54
|
-
* at. The last resort is the interactive prompt, and if there is no TTY the
|
|
55
|
-
* caller gets a clear error listing the flags rather than a hang.
|
|
56
|
-
*/
|
|
57
|
-
export declare function resolveKeySource(opts: {
|
|
58
|
-
key?: string;
|
|
59
|
-
keyFile?: string;
|
|
60
|
-
keysDir?: string;
|
|
61
|
-
home: string;
|
|
62
|
-
/** Set for non-interactive runs (CI, an AI assistant with no TTY). */
|
|
63
|
-
allowPrompt: boolean;
|
|
64
|
-
}): Promise<KeySource | {
|
|
65
|
-
error: string;
|
|
66
|
-
}>;
|
|
67
|
-
/**
|
|
68
|
-
* One thing setup will do. Building the whole plan BEFORE touching anything is
|
|
69
|
-
* what makes `--dry-run` truthful: the preview and the run are the same objects,
|
|
70
|
-
* so the preview cannot describe a merge the apply step then performs
|
|
71
|
-
* differently. Every `write` carries its exact final bytes.
|
|
72
|
-
*/
|
|
73
|
-
export type Step = {
|
|
74
|
-
kind: "write";
|
|
75
|
-
label: string;
|
|
76
|
-
file: string;
|
|
77
|
-
mode?: number;
|
|
78
|
-
before: string | null;
|
|
79
|
-
after: string;
|
|
80
|
-
secret?: boolean;
|
|
81
|
-
} | {
|
|
82
|
-
kind: "exec";
|
|
83
|
-
label: string;
|
|
84
|
-
command: string;
|
|
85
|
-
args: string[];
|
|
86
|
-
optionalWhy?: string;
|
|
87
|
-
} | {
|
|
88
|
-
kind: "manual";
|
|
89
|
-
label: string;
|
|
90
|
-
detail: string;
|
|
91
|
-
snippet?: string;
|
|
92
|
-
};
|
|
93
|
-
export interface Plan {
|
|
94
|
-
steps: Step[];
|
|
95
|
-
/** Things the owner must know, printed whether or not anything was written. */
|
|
96
|
-
notes: string[];
|
|
97
|
-
/** Runtimes found on this machine, for the summary line. */
|
|
98
|
-
found: string[];
|
|
99
|
-
}
|
|
100
|
-
/** The key file, in the `key=value` form the plugin and the SDK both parse. */
|
|
101
|
-
export declare function keyFileContents(privkey: string, pubkey: string): string;
|
|
102
|
-
/**
|
|
103
|
-
* True when a JSON file uses JSONC features (comments, trailing commas).
|
|
104
|
-
*
|
|
105
|
-
* We must not auto-merge into one: writing it back with JSON.stringify would
|
|
106
|
-
* silently delete the owner's comments. Detected by the disagreement between the
|
|
107
|
-
* strict and tolerant parsers — strict fails, tolerant succeeds.
|
|
108
|
-
*/
|
|
109
|
-
export declare function isJsonc(text: string): boolean;
|
|
110
|
-
/**
|
|
111
|
-
* Merge the Atbash plugin block into an OpenClaw config object, in place.
|
|
112
|
-
*
|
|
113
|
-
* A MERGE, not a replacement — that distinction is the whole reason this command
|
|
114
|
-
* exists. Other plugins already in `allow`, `load.paths` and `entries` are
|
|
115
|
-
* preserved, and an entry from the legacy `@atbash/atbash-plugin` install is
|
|
116
|
-
* updated where it stands rather than being shadowed by a duplicate: the
|
|
117
|
-
* dashboard scan recognizes both keys, so two entries would mean two hooks.
|
|
118
|
-
*
|
|
119
|
-
* `load.paths` gets the real absolute extension path. The published docs show a
|
|
120
|
-
* `<your-username>` placeholder that people paste verbatim, producing a path that
|
|
121
|
-
* does not exist and a plugin that never loads.
|
|
122
|
-
*/
|
|
123
|
-
export declare function mergeOpenclawConfig(config: Record<string, unknown>, home: string): Record<string, unknown>;
|
|
124
|
-
/**
|
|
125
|
-
* Detect the indentation a JSON file already uses, so a merge does not reformat
|
|
126
|
-
* the parts it did not touch.
|
|
127
|
-
*
|
|
128
|
-
* Without this, `JSON.stringify(obj, null, 2)` re-indents a tab-indented or
|
|
129
|
-
* 4-space config from top to bottom. The RESULT is still correct, but the diff
|
|
130
|
-
* shown for approval becomes every line in the file, which buries the two lines
|
|
131
|
-
* that actually changed — and the operator's own formatting choice is collateral
|
|
132
|
-
* damage in a file we were asked to make one addition to.
|
|
133
|
-
*
|
|
134
|
-
* Falls back to two spaces, which is what the published docs show.
|
|
135
|
-
*/
|
|
136
|
-
export declare function detectIndent(text: string | null): string | number;
|
|
137
|
-
/**
|
|
138
|
-
* Serialize a merged config the way the file was already written: same
|
|
139
|
-
* indentation, and a trailing newline only if the original had one.
|
|
140
|
-
*/
|
|
141
|
-
export declare function serializeLike(original: string | null, value: unknown): string;
|
|
142
|
-
interface McpClient {
|
|
143
|
-
label: string;
|
|
144
|
-
file: string;
|
|
145
|
-
format: "json" | "toml";
|
|
146
|
-
/** Which key holds the server map — VS Code and some others use `servers`. */
|
|
147
|
-
serversKey: "mcpServers" | "servers";
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* MCP client configs present under this home directory.
|
|
151
|
-
*
|
|
152
|
-
* Paths come from the shared MCP_CONFIGS so the writer and the scanner cannot
|
|
153
|
-
* drift: a client the scan reports but setup cannot find would look like a bug in
|
|
154
|
-
* whichever of the two the operator happened to trust.
|
|
155
|
-
*/
|
|
156
|
-
export declare function detectMcpClients(home: string): McpClient[];
|
|
157
|
-
/**
|
|
158
|
-
* Find the Python interpreter that actually runs Hermes.
|
|
159
|
-
*
|
|
160
|
-
* This is the difference between installing the plugin and only appearing to.
|
|
161
|
-
* `pip install atbash-hermes-plugin` puts the package wherever the *shell's*
|
|
162
|
-
* `pip` points — commonly a system or conda Python — while Hermes typically runs
|
|
163
|
-
* from its own virtualenv. The install succeeds, prints nothing alarming, and the
|
|
164
|
-
* plugin is invisible to Hermes forever. Nobody can debug that from the output.
|
|
165
|
-
*
|
|
166
|
-
* The launcher knows the answer. A pip-installed console script begins with a
|
|
167
|
-
* shebang naming the interpreter that created it:
|
|
168
|
-
*
|
|
169
|
-
* $ head -1 $(command -v hermes)
|
|
170
|
-
* #!/Users/me/.hermes/hermes-agent/venv/bin/python3
|
|
171
|
-
*
|
|
172
|
-
* So resolve `hermes`, read its first line, and use that interpreter directly via
|
|
173
|
-
* `-m pip`. Falls back to the conventional venv location under ~/.hermes, then to
|
|
174
|
-
* null — and a null becomes a printed command rather than a guess, because a
|
|
175
|
-
* wrong guess here is the silent failure this whole function exists to avoid.
|
|
176
|
-
*/
|
|
177
|
-
export declare function findHermesPython(home: string): {
|
|
178
|
-
python: string;
|
|
179
|
-
how: string;
|
|
180
|
-
} | null;
|
|
181
|
-
/** Same rule as buildPlan's `wanted`: an empty --runtime list means everything. */
|
|
182
|
-
export declare function wantedRuntime(id: string, only: string[]): boolean;
|
|
183
|
-
/**
|
|
184
|
-
* Add the plugin to Hermes' opt-in allow-list at `plugins.enabled` in
|
|
185
|
-
* `~/.hermes/config.yaml`.
|
|
186
|
-
*
|
|
187
|
-
* WHY NOT `hermes plugins enable`: that command cannot accept this plugin, on
|
|
188
|
-
* this version, ever. `_plugin_exists` (hermes_cli/plugins_cmd.py) looks only for
|
|
189
|
-
* a DIRECTORY in the user plugins dir or a bundled dir — it never consults entry
|
|
190
|
-
* points. Meanwhile the runtime loader (`plugins.py:_scan_entry_points`) does
|
|
191
|
-
* discover them. So Hermes will happily LOAD a pip-installed plugin but refuses
|
|
192
|
-
* to put one on the allow-list it requires, and since plugins are opt-in, a
|
|
193
|
-
* package that cannot get onto the list can never load. Running that command
|
|
194
|
-
* exits 1 with "not installed or bundled". Writing the list entry directly is the
|
|
195
|
-
* only route that works.
|
|
196
|
-
*
|
|
197
|
-
* A LINE MERGE, not a parse-and-reserialize. config.yaml is tens of kilobytes of
|
|
198
|
-
* heavily commented configuration; round-tripping it through a YAML emitter would
|
|
199
|
-
* strip every comment and reflow the file. So this inserts the one line needed and
|
|
200
|
-
* leaves every other byte alone — the same discipline as the .env merge.
|
|
201
|
-
*/
|
|
202
|
-
export declare function mergeHermesEnabledPlugins(existing: string | null, plugin?: string): string;
|
|
203
|
-
/**
|
|
204
|
-
* Whether Hermes has actually picked the plugin up.
|
|
205
|
-
*
|
|
206
|
-
* Installing the package is NOT the same as governing the agent. Hermes requires
|
|
207
|
-
* plugins to be enabled explicitly — a real run installed `atbash-hermes-plugin`
|
|
208
|
-
* into the right interpreter, with the right `hermes_agent.plugins` entry point,
|
|
209
|
-
* and `hermes plugins list` still showed only the bundled plugins, all "not
|
|
210
|
-
* enabled". The machine looked wired and enforced nothing, which is the exact
|
|
211
|
-
* failure this command exists to prevent.
|
|
212
|
-
*
|
|
213
|
-
* Parsing a rendered table is inherently fragile, so an unreadable result is
|
|
214
|
-
* reported as "unknown" and treated as "probably needs enabling" rather than as
|
|
215
|
-
* success. Claiming enforcement we have not observed is the one wrong answer.
|
|
216
|
-
*/
|
|
217
|
-
export type HermesPluginState = "configured" | "not-enabled" | "not-installed" | "unknown";
|
|
218
|
-
/**
|
|
219
|
-
* Is a Hermes gateway service running, and therefore restartable?
|
|
220
|
-
*
|
|
221
|
-
* "Restart Hermes" only means something when there is a service to bounce.
|
|
222
|
-
* Hermes has two shapes: `hermes` starts an interactive chat session, and
|
|
223
|
-
* `hermes gateway install` registers a launchd/systemd background service. A
|
|
224
|
-
* setup command must not conflate them —
|
|
225
|
-
*
|
|
226
|
-
* - with a service running, `hermes gateway restart` picks up the new config and
|
|
227
|
-
* is worth doing for the operator;
|
|
228
|
-
* - without one, there is nothing to restart. The plugin loads the next time
|
|
229
|
-
* they run `hermes`, and anything we "restarted" would either be a no-op or,
|
|
230
|
-
* worse, an interactive session someone is in the middle of using.
|
|
231
|
-
*
|
|
232
|
-
* `gateway status` exits 0 either way, so the state comes from its text.
|
|
233
|
-
*/
|
|
234
|
-
export declare function hermesGatewayRunning(): boolean;
|
|
235
|
-
/**
|
|
236
|
-
* Is Hermes CONFIGURED to load the plugin?
|
|
237
|
-
*
|
|
238
|
-
* This used to shell out to `hermes plugins list` and look for an atbash row —
|
|
239
|
-
* a signal that can never be true. `_discover_all_plugins` (plugins_cmd.py) walks
|
|
240
|
-
* plugin DIRECTORIES only: bundled, user, project. It never scans entry points,
|
|
241
|
-
* exactly like the `_plugin_exists` gate behind `plugins enable`. So a
|
|
242
|
-
* pip-installed plugin is invisible to both, and the check reported "not picked
|
|
243
|
-
* up" while everything was in fact correct — then told the operator to run the
|
|
244
|
-
* enable command that cannot work. Advising a known-impossible fix is worse than
|
|
245
|
-
* saying nothing.
|
|
246
|
-
*
|
|
247
|
-
* The two facts that actually decide it are both on disk:
|
|
248
|
-
*
|
|
249
|
-
* 1. the package is importable by the interpreter that runs Hermes, and
|
|
250
|
-
* 2. its name is on the `plugins.enabled` allow-list in config.yaml, which is
|
|
251
|
-
* what the RUNTIME loader (`plugins.py:_scan_entry_points`) honours.
|
|
252
|
-
*
|
|
253
|
-
* "configured" is as far as a setup command can honestly go. Proof of loading is
|
|
254
|
-
* a line in the agent log after Hermes next starts, which is why the caller points
|
|
255
|
-
* at that rather than claiming enforcement.
|
|
256
|
-
*/
|
|
257
|
-
export declare function hermesPluginState(home: string): HermesPluginState;
|
|
258
|
-
/**
|
|
259
|
-
* How to install a Python package into a specific interpreter on THIS machine.
|
|
260
|
-
*
|
|
261
|
-
* `<python> -m pip install` is the obvious answer and it is frequently wrong: a
|
|
262
|
-
* venv created by `uv venv` has no pip at all (that is uv's default), so the
|
|
263
|
-
* command fails with
|
|
264
|
-
*
|
|
265
|
-
* /path/venv/bin/python3: No module named pip
|
|
266
|
-
*
|
|
267
|
-
* after setup has already written every config file — which is exactly what
|
|
268
|
-
* happened on a real Hermes box. So probe, in order of what suits the venv:
|
|
269
|
-
*
|
|
270
|
-
* 1. `uv pip install --python <python>` when uv is present. Correct for a
|
|
271
|
-
* uv-created venv and fast; uv is also what created most pip-less venvs.
|
|
272
|
-
* 2. `<python> -m pip install` when pip actually answers.
|
|
273
|
-
* 3. Neither — hand it over, with `ensurepip` named, rather than planning a
|
|
274
|
-
* command that is known in advance to fail.
|
|
275
|
-
*/
|
|
276
|
-
export declare function pythonInstallStrategy(python: string, pkg: string): {
|
|
277
|
-
kind: "exec";
|
|
278
|
-
command: string;
|
|
279
|
-
args: string[];
|
|
280
|
-
how: string;
|
|
281
|
-
} | {
|
|
282
|
-
kind: "manual";
|
|
283
|
-
why: string;
|
|
284
|
-
snippet: string;
|
|
285
|
-
};
|
|
286
|
-
/**
|
|
287
|
-
* The env vars the Hermes plugin documents, merged into an existing `.env`.
|
|
288
|
-
*
|
|
289
|
-
* A `.env` is line-oriented and hand-maintained, so this is a line merge rather
|
|
290
|
-
* than a parse-and-reserialize: keys Atbash owns are replaced in place (keeping
|
|
291
|
-
* their position), keys it does not own are never touched, and anything else in
|
|
292
|
-
* the file — comments, blank lines, unrelated settings, ordering — survives
|
|
293
|
-
* exactly as written. Reformatting someone's .env to add four lines would be a
|
|
294
|
-
* poor trade.
|
|
295
|
-
*
|
|
296
|
-
* Values are from the published plugin README (PyPI atbash-hermes-plugin 0.4.5).
|
|
297
|
-
* `ATBASH_ORG_NAME` is deliberately NOT written: its value is the operator's org,
|
|
298
|
-
* which this command has no reliable way to know, and a wrong org sends the SDK
|
|
299
|
-
* at the wrong chain. It is called out in the manual step instead.
|
|
300
|
-
*/
|
|
301
|
-
export declare function mergeHermesEnv(existing: string | null): string;
|
|
302
|
-
/**
|
|
303
|
-
* Does this config's existing Atbash entry carry a key in its `env` block?
|
|
304
|
-
*
|
|
305
|
-
* True means the operator hand-wired it from the published docs and their private
|
|
306
|
-
* key is sitting in that file today. Setup takes it out, but the backup it writes
|
|
307
|
-
* first still has it — so this exists to make that sayable rather than silently
|
|
308
|
-
* relocating the leak.
|
|
309
|
-
*/
|
|
310
|
-
export declare function hadInlineKey(config: Record<string, unknown>, serversKey?: "mcpServers" | "servers"): boolean;
|
|
311
|
-
export declare function mergeMcpServer(config: Record<string, unknown>, serversKey?: "mcpServers" | "servers"): Record<string, unknown>;
|
|
312
|
-
/**
|
|
313
|
-
* Work out everything that needs doing on this machine, without doing any of it.
|
|
314
|
-
*
|
|
315
|
-
* Detection drives the plan rather than a flag the owner picks, for the same
|
|
316
|
-
* reason the scan does: what is actually installed here is knowable, and asking
|
|
317
|
-
* someone to identify their own runtime from a list invites a wrong answer that
|
|
318
|
-
* writes a config for a plugin they do not have.
|
|
319
|
-
*/
|
|
320
|
-
export declare function buildPlan(args: {
|
|
321
|
-
home: string;
|
|
322
|
-
privkey: string;
|
|
323
|
-
pubkey: string;
|
|
324
|
-
/** Skip package installation; write config and the key file only. */
|
|
325
|
-
noInstall: boolean;
|
|
326
|
-
/** Restrict to these runtime ids; empty means "everything detected". */
|
|
327
|
-
only: string[];
|
|
328
|
-
}): Plan;
|
|
329
|
-
/**
|
|
330
|
-
* A minimal line diff, so the preview shows what CHANGES rather than dumping a
|
|
331
|
-
* whole config and leaving the owner to spot the difference. Standard LCS; these
|
|
332
|
-
* files are small enough that the quadratic table is irrelevant.
|
|
333
|
-
*/
|
|
334
|
-
export declare function lineDiff(before: string, after: string): string[];
|
|
335
|
-
/**
|
|
336
|
-
* Print the plan. Used for `--dry-run` and for the confirmation prompt, so what
|
|
337
|
-
* the owner is shown and what they agree to cannot diverge.
|
|
338
|
-
*
|
|
339
|
-
* The key file's CONTENTS are never printed — the whole point of the file is that
|
|
340
|
-
* the private key stays put, and echoing it into a terminal scrollback undoes
|
|
341
|
-
* that. The path, mode and the public key are shown instead.
|
|
342
|
-
*/
|
|
343
|
-
export declare function renderPlan(plan: Plan, pubkey: string): void;
|
|
344
|
-
/**
|
|
345
|
-
* Copy a file aside before overwriting it, without ever clobbering an existing
|
|
346
|
-
* backup — a second run must not overwrite the pristine copy from the first.
|
|
347
|
-
*/
|
|
348
|
-
export declare function backupFile(file: string): string | null;
|
|
349
|
-
export interface ApplyResult {
|
|
350
|
-
written: string[];
|
|
351
|
-
backups: string[];
|
|
352
|
-
ran: string[];
|
|
353
|
-
failures: string[];
|
|
354
|
-
}
|
|
355
|
-
/** Execute the plan. Writes first, then commands, so a failed install still
|
|
356
|
-
* leaves a correct config and key file behind for a manual retry. */
|
|
357
|
-
export declare function applyPlan(plan: Plan): ApplyResult;
|
|
358
|
-
export declare function registerSetupCommand(program: Command): void;
|
|
359
|
-
export {};
|