@openape/apes 1.30.0 → 1.31.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/{chunk-NYJSBFLG.js → chunk-MMBFV5WN.js} +14 -6
- package/dist/{chunk-NYJSBFLG.js.map → chunk-MMBFV5WN.js.map} +1 -1
- package/dist/cli.js +441 -393
- package/dist/cli.js.map +1 -1
- package/dist/{http-UPOTOYQV.js → http-SILH37L7.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{orchestrator-P7QFDBBK.js → orchestrator-6PZXCE54.js} +2 -2
- package/dist/{server-X4HHOCKV.js → server-MDXOGP2U.js} +3 -3
- package/package.json +5 -5
- /package/dist/{http-UPOTOYQV.js.map → http-SILH37L7.js.map} +0 -0
- /package/dist/{orchestrator-P7QFDBBK.js.map → orchestrator-6PZXCE54.js.map} +0 -0
- /package/dist/{server-X4HHOCKV.js.map → server-MDXOGP2U.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
getAgentChallengeEndpoint,
|
|
51
51
|
getDelegationsEndpoint,
|
|
52
52
|
getGrantsEndpoint
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-MMBFV5WN.js";
|
|
54
54
|
import {
|
|
55
55
|
AUTH_FILE,
|
|
56
56
|
CONFIG_DIR,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
} from "./chunk-OBF7IMQ2.js";
|
|
66
66
|
|
|
67
67
|
// src/cli.ts
|
|
68
|
-
import
|
|
68
|
+
import consola53 from "consola";
|
|
69
69
|
|
|
70
70
|
// src/ape-shell.ts
|
|
71
71
|
import path from "path";
|
|
@@ -98,7 +98,7 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
98
98
|
import { defineCommand as defineCommand64, runMain } from "citty";
|
|
99
99
|
|
|
100
100
|
// src/commands/auth/login.ts
|
|
101
|
-
import { Buffer
|
|
101
|
+
import { Buffer } from "buffer";
|
|
102
102
|
import { execFile } from "child_process";
|
|
103
103
|
import { createServer } from "http";
|
|
104
104
|
import { homedir as homedir2 } from "os";
|
|
@@ -308,7 +308,7 @@ async function loginWithPKCE(idp) {
|
|
|
308
308
|
authUrl.searchParams.set("state", state);
|
|
309
309
|
authUrl.searchParams.set("nonce", nonce);
|
|
310
310
|
authUrl.searchParams.set("scope", "openid email profile offline_access");
|
|
311
|
-
const code = await new Promise((
|
|
311
|
+
const code = await new Promise((resolve5, reject) => {
|
|
312
312
|
const server = createServer((req, res) => {
|
|
313
313
|
const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
|
|
314
314
|
if (url.pathname === "/callback") {
|
|
@@ -325,7 +325,7 @@ async function loginWithPKCE(idp) {
|
|
|
325
325
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
326
326
|
res.end("<h1>Login successful!</h1><p>You can close this window.</p>");
|
|
327
327
|
server.close();
|
|
328
|
-
|
|
328
|
+
resolve5(authCode);
|
|
329
329
|
return;
|
|
330
330
|
}
|
|
331
331
|
res.writeHead(400);
|
|
@@ -381,7 +381,7 @@ async function loginWithPKCE(idp) {
|
|
|
381
381
|
consola2.success(`Logged in as ${payload.email || payload.sub}`);
|
|
382
382
|
}
|
|
383
383
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
384
|
-
const { readFileSync:
|
|
384
|
+
const { readFileSync: readFileSync19 } = await import("fs");
|
|
385
385
|
const { sign: sign3 } = await import("crypto");
|
|
386
386
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
|
|
387
387
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
@@ -395,9 +395,9 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
395
395
|
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
396
396
|
}
|
|
397
397
|
const { challenge } = await challengeResp.json();
|
|
398
|
-
const keyContent =
|
|
398
|
+
const keyContent = readFileSync19(keyPath, "utf-8");
|
|
399
399
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
400
|
-
const signature = sign3(null,
|
|
400
|
+
const signature = sign3(null, Buffer.from(challenge), privateKey).toString("base64");
|
|
401
401
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
402
402
|
const authResp = await fetch(authenticateUrl, {
|
|
403
403
|
method: "POST",
|
|
@@ -888,7 +888,7 @@ async function waitForApproval2(grantsUrl, grantId) {
|
|
|
888
888
|
if (grant.status === "revoked") {
|
|
889
889
|
throw new CliError("Grant revoked.");
|
|
890
890
|
}
|
|
891
|
-
await new Promise((
|
|
891
|
+
await new Promise((resolve5) => setTimeout(resolve5, interval));
|
|
892
892
|
}
|
|
893
893
|
throw new CliError("Timed out waiting for approval.");
|
|
894
894
|
}
|
|
@@ -1981,7 +1981,7 @@ import { defineCommand as defineCommand22 } from "citty";
|
|
|
1981
1981
|
import consola19 from "consola";
|
|
1982
1982
|
|
|
1983
1983
|
// src/lib/agent-bootstrap.ts
|
|
1984
|
-
import { Buffer as
|
|
1984
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
1985
1985
|
import { createPrivateKey, sign } from "crypto";
|
|
1986
1986
|
import { exchangeWithDelegation } from "@openape/cli-auth";
|
|
1987
1987
|
var AGENT_NAME_REGEX = /^[a-z][a-z0-9-]{0,23}$/;
|
|
@@ -2047,7 +2047,7 @@ function decodeJwtClaims(token) {
|
|
|
2047
2047
|
const part = token.split(".")[1];
|
|
2048
2048
|
if (!part) return null;
|
|
2049
2049
|
const padded = part + "=".repeat((4 - part.length % 4) % 4);
|
|
2050
|
-
const json =
|
|
2050
|
+
const json = Buffer2.from(padded, "base64").toString("utf8");
|
|
2051
2051
|
return JSON.parse(json);
|
|
2052
2052
|
} catch {
|
|
2053
2053
|
return null;
|
|
@@ -2069,7 +2069,7 @@ async function issueAgentToken(input) {
|
|
|
2069
2069
|
throw new Error(`Challenge failed (${challengeResp.status}): ${text}`);
|
|
2070
2070
|
}
|
|
2071
2071
|
const { challenge } = await challengeResp.json();
|
|
2072
|
-
const signature = sign(null,
|
|
2072
|
+
const signature = sign(null, Buffer2.from(challenge), privateKey).toString("base64");
|
|
2073
2073
|
const authenticateUrl = await getAgentAuthenticateEndpoint(input.idp);
|
|
2074
2074
|
const authResp = await fetch(authenticateUrl, {
|
|
2075
2075
|
method: "POST",
|
|
@@ -2102,25 +2102,6 @@ mkdir -p "$HOME_DIR/.claude/hooks"
|
|
|
2102
2102
|
cat > "$HOME_DIR/.claude/settings.json" ${shHeredoc(input.claudeSettingsJson)}
|
|
2103
2103
|
cat > "$HOME_DIR/.claude/hooks/bash-via-ape-shell.sh" ${shHeredoc(input.hookScriptSource)}
|
|
2104
2104
|
chmod 755 "$HOME_DIR/.claude/hooks/bash-via-ape-shell.sh"
|
|
2105
|
-
` : "";
|
|
2106
|
-
const claudeTokenBlock = input.claudeOauthToken ? `
|
|
2107
|
-
mkdir -p "$HOME_DIR/.config/openape"
|
|
2108
|
-
cat > "$HOME_DIR/.config/openape/claude-token.env" ${shHeredoc(`# Auto-generated by 'apes agents spawn'. chmod 600 \u2014 contains a long-lived
|
|
2109
|
-
# Claude Code OAuth token. Rotate by editing this file in place; the
|
|
2110
|
-
# .zshenv / .profile source-lines below will pick it up automatically.
|
|
2111
|
-
export CLAUDE_CODE_OAUTH_TOKEN=${shQuote(input.claudeOauthToken)}
|
|
2112
|
-
`)}
|
|
2113
|
-
SOURCE_LINE='[ -f "$HOME/.config/openape/claude-token.env" ] && . "$HOME/.config/openape/claude-token.env"'
|
|
2114
|
-
for f in "$HOME_DIR/.zshenv" "$HOME_DIR/.profile"; do
|
|
2115
|
-
touch "$f"
|
|
2116
|
-
if ! grep -qF 'config/openape/claude-token.env' "$f" 2>/dev/null; then
|
|
2117
|
-
{
|
|
2118
|
-
echo ''
|
|
2119
|
-
echo '# OpenApe: load Claude Code OAuth token (added by apes agents spawn)'
|
|
2120
|
-
echo "$SOURCE_LINE"
|
|
2121
|
-
} >> "$f"
|
|
2122
|
-
fi
|
|
2123
|
-
done
|
|
2124
2105
|
` : "";
|
|
2125
2106
|
return `#!/bin/bash
|
|
2126
2107
|
set -euo pipefail
|
|
@@ -2163,7 +2144,7 @@ cat > "$HOME_DIR/.ssh/id_ed25519.pub" ${shHeredoc(input.publicKeySshLine)}
|
|
|
2163
2144
|
cat > "$HOME_DIR/.config/apes/auth.json" ${shHeredoc(input.authJson)}
|
|
2164
2145
|
cat > "$HOME_DIR/.config/openape/agent-x25519.key" ${shHeredoc(input.x25519PrivateKey)}
|
|
2165
2146
|
cat > "$HOME_DIR/.config/openape/agent-x25519.key.pub" ${shHeredoc(input.x25519PublicKey)}
|
|
2166
|
-
${claudeBlock}
|
|
2147
|
+
${claudeBlock}
|
|
2167
2148
|
# Per-agent task dir that \`apes agents sync\` writes to (XDG-style on
|
|
2168
2149
|
# Linux; was ~/Library/... on macOS).
|
|
2169
2150
|
mkdir -p "$HOME_DIR/.openape/agent/tasks"
|
|
@@ -2177,9 +2158,6 @@ chmod 644 "$HOME_DIR/.ssh/id_ed25519.pub"
|
|
|
2177
2158
|
chmod 600 "$HOME_DIR/.config/apes/auth.json"
|
|
2178
2159
|
chmod 600 "$HOME_DIR/.config/openape/agent-x25519.key"
|
|
2179
2160
|
chmod 644 "$HOME_DIR/.config/openape/agent-x25519.key.pub"
|
|
2180
|
-
if [ -f "$HOME_DIR/.config/openape/claude-token.env" ]; then
|
|
2181
|
-
chmod 600 "$HOME_DIR/.config/openape/claude-token.env"
|
|
2182
|
-
fi
|
|
2183
2161
|
|
|
2184
2162
|
echo "OK $NAME (linux user) uid=$NEW_UID home=$HOME_DIR"
|
|
2185
2163
|
`;
|
|
@@ -2533,9 +2511,9 @@ import { consola as consola21 } from "consola";
|
|
|
2533
2511
|
import { taskTools, runApeShell, runCodingTask, buildIssueGet, detectForge, createLlmReviewer, createLlmRiskAssessor, resolveMergePolicy } from "@openape/agent-runtime";
|
|
2534
2512
|
|
|
2535
2513
|
// src/lib/agent-secrets-runtime.ts
|
|
2536
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync4, watch } from "fs";
|
|
2514
|
+
import { existsSync as existsSync5, mkdirSync, readdirSync, readFileSync as readFileSync4, statSync, watch, writeFileSync as writeFileSync3 } from "fs";
|
|
2537
2515
|
import { homedir as homedir4 } from "os";
|
|
2538
|
-
import { join as join3 } from "path";
|
|
2516
|
+
import { dirname, join as join3 } from "path";
|
|
2539
2517
|
import { openString } from "@openape/core";
|
|
2540
2518
|
var CONFIG_DIR2 = join3(homedir4(), ".config", "openape");
|
|
2541
2519
|
var SECRETS_DIR = join3(CONFIG_DIR2, "secrets.d");
|
|
@@ -2570,7 +2548,17 @@ function materializeSecrets(opts = {}) {
|
|
|
2570
2548
|
if (!name) continue;
|
|
2571
2549
|
try {
|
|
2572
2550
|
const box = JSON.parse(readFileSync4(join3(dir, file), "utf8"));
|
|
2573
|
-
|
|
2551
|
+
const plaintext = openString(box, key);
|
|
2552
|
+
const target = typeof box.materializeTo === "string" ? box.materializeTo : null;
|
|
2553
|
+
if (target) {
|
|
2554
|
+
const blobMtime = statSync(join3(dir, file)).mtimeMs;
|
|
2555
|
+
if (!existsSync5(target) || statSync(target).mtimeMs < blobMtime) {
|
|
2556
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
2557
|
+
writeFileSync3(target, plaintext, { mode: 384 });
|
|
2558
|
+
}
|
|
2559
|
+
} else {
|
|
2560
|
+
env[name] = plaintext;
|
|
2561
|
+
}
|
|
2574
2562
|
applied.push(name);
|
|
2575
2563
|
} catch (e) {
|
|
2576
2564
|
failed.push(file);
|
|
@@ -2734,7 +2722,7 @@ import { defineCommand as defineCommand25 } from "citty";
|
|
|
2734
2722
|
import consola22 from "consola";
|
|
2735
2723
|
|
|
2736
2724
|
// src/lib/nest-registry.ts
|
|
2737
|
-
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync6, writeFileSync as
|
|
2725
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2738
2726
|
import { homedir as homedir6 } from "os";
|
|
2739
2727
|
import { join as join5 } from "path";
|
|
2740
2728
|
function resolveRegistryPath() {
|
|
@@ -2761,10 +2749,10 @@ function writeNestRegistry(reg) {
|
|
|
2761
2749
|
const path2 = resolveRegistryPath();
|
|
2762
2750
|
const dir = path2.replace(/\/agents\.json$/, "");
|
|
2763
2751
|
try {
|
|
2764
|
-
|
|
2752
|
+
mkdirSync2(dir, { recursive: true });
|
|
2765
2753
|
} catch {
|
|
2766
2754
|
}
|
|
2767
|
-
|
|
2755
|
+
writeFileSync4(path2, `${JSON.stringify(reg, null, 2)}
|
|
2768
2756
|
`, { mode: 432 });
|
|
2769
2757
|
}
|
|
2770
2758
|
function upsertNestAgent(entry) {
|
|
@@ -3348,22 +3336,22 @@ import { defineCommand as defineCommand30 } from "citty";
|
|
|
3348
3336
|
import consola26 from "consola";
|
|
3349
3337
|
|
|
3350
3338
|
// src/lib/keygen.ts
|
|
3351
|
-
import { Buffer as
|
|
3352
|
-
import { existsSync as existsSync11, mkdirSync as
|
|
3339
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
3340
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
3353
3341
|
import { generateKeyPairSync } from "crypto";
|
|
3354
3342
|
import { homedir as homedir9 } from "os";
|
|
3355
|
-
import { dirname, resolve as resolve2 } from "path";
|
|
3343
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
3356
3344
|
import { generateX25519KeyPair } from "@openape/core";
|
|
3357
3345
|
function resolveKeyPath(p) {
|
|
3358
3346
|
return resolve2(p.replace(/^~/, homedir9()));
|
|
3359
3347
|
}
|
|
3360
3348
|
function buildSshEd25519Line(rawPub) {
|
|
3361
3349
|
const keyTypeStr = "ssh-ed25519";
|
|
3362
|
-
const keyTypeLen =
|
|
3350
|
+
const keyTypeLen = Buffer3.alloc(4);
|
|
3363
3351
|
keyTypeLen.writeUInt32BE(keyTypeStr.length);
|
|
3364
|
-
const pubKeyLen =
|
|
3352
|
+
const pubKeyLen = Buffer3.alloc(4);
|
|
3365
3353
|
pubKeyLen.writeUInt32BE(rawPub.length);
|
|
3366
|
-
const blob =
|
|
3354
|
+
const blob = Buffer3.concat([keyTypeLen, Buffer3.from(keyTypeStr), pubKeyLen, rawPub]);
|
|
3367
3355
|
return `ssh-ed25519 ${blob.toString("base64")}`;
|
|
3368
3356
|
}
|
|
3369
3357
|
function readPublicKey(keyPath) {
|
|
@@ -3374,22 +3362,22 @@ function readPublicKey(keyPath) {
|
|
|
3374
3362
|
const keyContent = readFileSync10(keyPath, "utf-8");
|
|
3375
3363
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
3376
3364
|
const jwk = privateKey.export({ format: "jwk" });
|
|
3377
|
-
const pubBytes =
|
|
3365
|
+
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3378
3366
|
return buildSshEd25519Line(pubBytes);
|
|
3379
3367
|
}
|
|
3380
3368
|
function generateAndSaveKey(keyPath) {
|
|
3381
3369
|
const resolved = resolveKeyPath(keyPath);
|
|
3382
|
-
const dir =
|
|
3370
|
+
const dir = dirname2(resolved);
|
|
3383
3371
|
if (!existsSync11(dir)) {
|
|
3384
|
-
|
|
3372
|
+
mkdirSync3(dir, { recursive: true });
|
|
3385
3373
|
}
|
|
3386
3374
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
3387
3375
|
const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
3388
|
-
|
|
3376
|
+
writeFileSync5(resolved, privatePem, { mode: 384 });
|
|
3389
3377
|
const jwk = publicKey.export({ format: "jwk" });
|
|
3390
|
-
const pubBytes =
|
|
3378
|
+
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3391
3379
|
const pubKeyStr = buildSshEd25519Line(pubBytes);
|
|
3392
|
-
|
|
3380
|
+
writeFileSync5(`${resolved}.pub`, `${pubKeyStr}
|
|
3393
3381
|
`, { mode: 420 });
|
|
3394
3382
|
return pubKeyStr;
|
|
3395
3383
|
}
|
|
@@ -3397,7 +3385,7 @@ function generateKeyPairInMemory() {
|
|
|
3397
3385
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
3398
3386
|
const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
3399
3387
|
const jwk = publicKey.export({ format: "jwk" });
|
|
3400
|
-
const pubBytes =
|
|
3388
|
+
const pubBytes = Buffer3.from(jwk.x, "base64url");
|
|
3401
3389
|
const enc = generateX25519KeyPair();
|
|
3402
3390
|
return {
|
|
3403
3391
|
privatePem,
|
|
@@ -3435,14 +3423,6 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3435
3423
|
type: "boolean",
|
|
3436
3424
|
description: "Skip writing ~/.claude/settings.json + the Bash-rewrite hook"
|
|
3437
3425
|
},
|
|
3438
|
-
"claude-token": {
|
|
3439
|
-
type: "string",
|
|
3440
|
-
description: "Claude Code OAuth token (sk-ant-oat01-\u2026) from `claude setup-token`. Visible to ps \u2014 prefer --claude-token-stdin in scripts."
|
|
3441
|
-
},
|
|
3442
|
-
"claude-token-stdin": {
|
|
3443
|
-
type: "boolean",
|
|
3444
|
-
description: "Read the Claude Code OAuth token from stdin (paranoid form of --claude-token)."
|
|
3445
|
-
},
|
|
3446
3426
|
"no-bridge": {
|
|
3447
3427
|
type: "boolean",
|
|
3448
3428
|
description: "Skip the ape-agent runtime install. Default behaviour installs the runtime so the agent answers chat.openape.ai messages (reads LITELLM_API_KEY/BASE_URL from ~/litellm/.env; override via --bridge-key / --bridge-base-url). Use --no-bridge for headless / CI / IdP-only account provisioning where the agent will not run a chat loop."
|
|
@@ -3458,6 +3438,18 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3458
3438
|
"bridge-model": {
|
|
3459
3439
|
type: "string",
|
|
3460
3440
|
description: "Model the bridge sends in chat-completion requests (default: claude-haiku-4-5). Override when fronting a proxy that doesn't route the default \u2014 e.g. ChatGPT-only proxy needs `gpt-5.4`."
|
|
3441
|
+
},
|
|
3442
|
+
"kind": {
|
|
3443
|
+
type: "string",
|
|
3444
|
+
description: `Agent kind: "user" (default, connects to troop-chat) or "service" (polls an SP backend's task queue and runs each task through the LLM). With "service", --serves is required.`
|
|
3445
|
+
},
|
|
3446
|
+
"serves": {
|
|
3447
|
+
type: "string",
|
|
3448
|
+
description: "For --kind service: base URL of the SP backend this agent serves (e.g. https://zaz.delta-mind.at or http://127.0.0.1:3013). The agent pulls GetNextTask / posts ResolveTask there."
|
|
3449
|
+
},
|
|
3450
|
+
"poll-interval": {
|
|
3451
|
+
type: "string",
|
|
3452
|
+
description: "For --kind service: idle poll interval in ms (default 2000)."
|
|
3461
3453
|
}
|
|
3462
3454
|
},
|
|
3463
3455
|
async run({ args }) {
|
|
@@ -3467,6 +3459,13 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3467
3459
|
`Invalid agent name "${name}". Must match /^[a-z][a-z0-9-]{0,23}$/ \u2014 lowercase letters, digits and hyphens, 1\u201324 chars, must start with a letter.`
|
|
3468
3460
|
);
|
|
3469
3461
|
}
|
|
3462
|
+
if (args.kind != null && args.kind !== "user" && args.kind !== "service")
|
|
3463
|
+
throw new CliError(`Invalid --kind "${String(args.kind)}". Must be "user" or "service".`);
|
|
3464
|
+
const isService = args.kind === "service";
|
|
3465
|
+
const servesUrl = typeof args.serves === "string" ? args.serves.replace(/\/$/, "") : void 0;
|
|
3466
|
+
if (isService && !servesUrl)
|
|
3467
|
+
throw new CliError("--kind service requires --serves <SP base URL> (e.g. https://zaz.delta-mind.at).");
|
|
3468
|
+
const pollMs = typeof args["poll-interval"] === "string" ? Number.parseInt(args["poll-interval"], 10) : void 0;
|
|
3470
3469
|
const auth = loadAuth();
|
|
3471
3470
|
if (!auth) {
|
|
3472
3471
|
throw new CliError("Not authenticated. Run `apes login` first.");
|
|
@@ -3511,10 +3510,6 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3511
3510
|
ownerEmail: registration.owner
|
|
3512
3511
|
});
|
|
3513
3512
|
const includeClaudeHook = !args["no-claude-hook"];
|
|
3514
|
-
const claudeOauthToken = await resolveClaudeToken({
|
|
3515
|
-
flag: typeof args["claude-token"] === "string" ? args["claude-token"] : void 0,
|
|
3516
|
-
fromStdin: !!args["claude-token-stdin"]
|
|
3517
|
-
});
|
|
3518
3513
|
const withBridge = !args["no-bridge"];
|
|
3519
3514
|
const script = buildSpawnSetupScript({
|
|
3520
3515
|
name,
|
|
@@ -3526,8 +3521,7 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3526
3521
|
x25519PublicKey,
|
|
3527
3522
|
authJson,
|
|
3528
3523
|
claudeSettingsJson: includeClaudeHook ? CLAUDE_SETTINGS_JSON : null,
|
|
3529
|
-
hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null
|
|
3530
|
-
claudeOauthToken
|
|
3524
|
+
hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null
|
|
3531
3525
|
});
|
|
3532
3526
|
consola26.start("Running privileged setup\u2026");
|
|
3533
3527
|
if (process.getuid?.() !== 0) {
|
|
@@ -3542,7 +3536,11 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3542
3536
|
home: homeDir,
|
|
3543
3537
|
email: registration.email,
|
|
3544
3538
|
registeredAt: Math.floor(Date.now() / 1e3),
|
|
3545
|
-
|
|
3539
|
+
kind: isService ? "service" : void 0,
|
|
3540
|
+
service: isService && servesUrl ? { spBaseUrl: servesUrl, pollIntervalMs: pollMs != null && Number.isFinite(pollMs) && pollMs > 0 ? pollMs : void 0 } : void 0,
|
|
3541
|
+
// Service agents also carry bridge config — it's the LLM endpoint the
|
|
3542
|
+
// worker forwards to (baseUrl/key/model from --bridge-*).
|
|
3543
|
+
bridge: withBridge || isService ? {
|
|
3546
3544
|
baseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0,
|
|
3547
3545
|
apiKey: typeof args["bridge-key"] === "string" ? args["bridge-key"] : void 0,
|
|
3548
3546
|
model: typeof args["bridge-model"] === "string" ? args["bridge-model"] : void 0
|
|
@@ -3562,44 +3560,93 @@ var spawnAgentCommand = defineCommand30({
|
|
|
3562
3560
|
console.log(` apes run --as ${name} -- claude --session-name ${name} --dangerously-skip-permissions`);
|
|
3563
3561
|
}
|
|
3564
3562
|
});
|
|
3565
|
-
async function resolveClaudeToken(opts) {
|
|
3566
|
-
if (opts.flag && opts.fromStdin) {
|
|
3567
|
-
throw new CliError("Pass --claude-token OR --claude-token-stdin, not both.");
|
|
3568
|
-
}
|
|
3569
|
-
let raw = null;
|
|
3570
|
-
if (typeof opts.flag === "string") {
|
|
3571
|
-
raw = opts.flag.trim();
|
|
3572
|
-
} else if (opts.fromStdin) {
|
|
3573
|
-
const chunks = [];
|
|
3574
|
-
for await (const chunk of process.stdin) {
|
|
3575
|
-
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
3576
|
-
}
|
|
3577
|
-
raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
3578
|
-
}
|
|
3579
|
-
if (!raw) return null;
|
|
3580
|
-
if (!raw.startsWith("sk-ant-oat01-")) {
|
|
3581
|
-
throw new CliError(
|
|
3582
|
-
`Claude token doesn't look right (expected sk-ant-oat01-\u2026). Run \`claude setup-token\` and paste the resulting token.`
|
|
3583
|
-
);
|
|
3584
|
-
}
|
|
3585
|
-
return raw;
|
|
3586
|
-
}
|
|
3587
3563
|
|
|
3588
3564
|
// src/commands/agents/sync.ts
|
|
3589
|
-
import { chownSync, existsSync as
|
|
3565
|
+
import { chownSync, existsSync as existsSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync12, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
3590
3566
|
import { homedir as homedir10 } from "os";
|
|
3591
|
-
import { join as
|
|
3567
|
+
import { join as join9 } from "path";
|
|
3592
3568
|
import { defineCommand as defineCommand31 } from "citty";
|
|
3569
|
+
import consola28 from "consola";
|
|
3570
|
+
|
|
3571
|
+
// src/lib/recipe-checkout.ts
|
|
3572
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
3573
|
+
import { cpSync, existsSync as existsSync12, readFileSync as readFileSync11, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
3574
|
+
import { join as join8, resolve as resolve3, sep } from "path";
|
|
3593
3575
|
import consola27 from "consola";
|
|
3594
|
-
var
|
|
3595
|
-
var
|
|
3576
|
+
var MARKER_FILE = ".recipe-ref";
|
|
3577
|
+
var GITHUB_PREFIX = "github.com/";
|
|
3578
|
+
function ensureRecipeCheckout(recipeRef, recipeDir, exec2 = execFileSync7) {
|
|
3579
|
+
const at = recipeRef.lastIndexOf("@");
|
|
3580
|
+
if (at <= 0) {
|
|
3581
|
+
consola27.warn(`recipe: malformed recipeRef "${recipeRef}" (expected <owner>/<name>[/<subdir>]@<ref>) \u2014 skipping checkout`);
|
|
3582
|
+
return;
|
|
3583
|
+
}
|
|
3584
|
+
const spec = recipeRef.slice(0, at);
|
|
3585
|
+
const ref = recipeRef.slice(at + 1);
|
|
3586
|
+
const slug = spec.startsWith(GITHUB_PREFIX) ? spec.slice(GITHUB_PREFIX.length) : spec;
|
|
3587
|
+
if (!slug || !ref) {
|
|
3588
|
+
consola27.warn(`recipe: malformed recipeRef "${recipeRef}" (empty slug or ref) \u2014 skipping checkout`);
|
|
3589
|
+
return;
|
|
3590
|
+
}
|
|
3591
|
+
const segments = slug.split("/").filter(Boolean);
|
|
3592
|
+
if (segments.length < 2) {
|
|
3593
|
+
consola27.warn(`recipe: malformed recipeRef "${recipeRef}" (expected <owner>/<name>[/<subdir>]@<ref>) \u2014 skipping checkout`);
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3596
|
+
if (segments.some((s) => s === "." || s === ".." || s.includes("\\"))) {
|
|
3597
|
+
consola27.warn(`recipe: unsafe path segment in recipeRef "${recipeRef}" \u2014 skipping checkout`);
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
const repoSlug = segments.slice(0, 2).join("/");
|
|
3601
|
+
const subdir = segments.slice(2).join("/");
|
|
3602
|
+
const markerPath = join8(recipeDir, MARKER_FILE);
|
|
3603
|
+
if (existsSync12(markerPath)) {
|
|
3604
|
+
try {
|
|
3605
|
+
if (readFileSync11(markerPath, "utf8") === recipeRef) return;
|
|
3606
|
+
} catch {
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
const cloneUrl = `https://github.com/${repoSlug}`;
|
|
3610
|
+
const staging = subdir ? `${recipeDir}.checkout` : recipeDir;
|
|
3611
|
+
try {
|
|
3612
|
+
rmSync2(recipeDir, { recursive: true, force: true });
|
|
3613
|
+
if (subdir)
|
|
3614
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3615
|
+
exec2("git", ["clone", "--depth", "1", "--branch", ref, cloneUrl, staging]);
|
|
3616
|
+
if (subdir) {
|
|
3617
|
+
const src = join8(staging, subdir);
|
|
3618
|
+
if (!resolve3(src).startsWith(resolve3(staging) + sep)) {
|
|
3619
|
+
consola27.warn(`recipe: subdirectory "${subdir}" escapes the checkout dir \u2014 skipping`);
|
|
3620
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3621
|
+
return;
|
|
3622
|
+
}
|
|
3623
|
+
if (!existsSync12(src)) {
|
|
3624
|
+
consola27.warn(`recipe: subdirectory "${subdir}" not found in ${repoSlug}@${ref} \u2014 skipping checkout`);
|
|
3625
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3626
|
+
return;
|
|
3627
|
+
}
|
|
3628
|
+
cpSync(src, recipeDir, { recursive: true });
|
|
3629
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3630
|
+
}
|
|
3631
|
+
writeFileSync6(markerPath, recipeRef);
|
|
3632
|
+
consola27.info(`recipe: checked out ${slug}@${ref} \u2192 ${recipeDir}`);
|
|
3633
|
+
} catch (err) {
|
|
3634
|
+
consola27.warn(`recipe: checkout of ${slug}@${ref} failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
3635
|
+
if (subdir)
|
|
3636
|
+
rmSync2(staging, { recursive: true, force: true });
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
// src/commands/agents/sync.ts
|
|
3641
|
+
var AUTH_PATH3 = join9(homedir10(), ".config", "apes", "auth.json");
|
|
3642
|
+
var TASK_CACHE_DIR2 = join9(homedir10(), ".openape", "agent", "tasks");
|
|
3596
3643
|
function readAuthJson() {
|
|
3597
|
-
if (!
|
|
3644
|
+
if (!existsSync13(AUTH_PATH3)) {
|
|
3598
3645
|
throw new CliError(
|
|
3599
3646
|
`No agent auth found at ${AUTH_PATH3}. Run \`apes agents spawn <name>\` to provision an agent first.`
|
|
3600
3647
|
);
|
|
3601
3648
|
}
|
|
3602
|
-
const raw =
|
|
3649
|
+
const raw = readFileSync12(AUTH_PATH3, "utf8");
|
|
3603
3650
|
let parsed;
|
|
3604
3651
|
try {
|
|
3605
3652
|
parsed = JSON.parse(raw);
|
|
@@ -3648,7 +3695,7 @@ var syncAgentCommand = defineCommand31({
|
|
|
3648
3695
|
if (!auth.owner_email) {
|
|
3649
3696
|
throw new CliError(`${AUTH_PATH3} is missing owner_email \u2014 re-run \`apes agents spawn\` to update.`);
|
|
3650
3697
|
}
|
|
3651
|
-
|
|
3698
|
+
consola28.start(`Syncing ${agentName} (${host}, hostId ${hostId.slice(0, 8)}\u2026) with ${troopUrl}`);
|
|
3652
3699
|
const pubkeyX25519 = readAgentEncryptionPublicKey() ?? void 0;
|
|
3653
3700
|
const sync = await client.sync({
|
|
3654
3701
|
hostname: host,
|
|
@@ -3656,17 +3703,17 @@ var syncAgentCommand = defineCommand31({
|
|
|
3656
3703
|
ownerEmail: auth.owner_email,
|
|
3657
3704
|
...pubkeyX25519 ? { pubkeyX25519 } : {}
|
|
3658
3705
|
});
|
|
3659
|
-
|
|
3660
|
-
if (!pubkeyX25519)
|
|
3661
|
-
const { system_prompt: systemPrompt, tools, skills, tasks } = await client.listTasks();
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3706
|
+
consola28.info(sync.first_sync ? "\u2713 first sync \u2014 agent registered" : "\u2713 presence updated");
|
|
3707
|
+
if (!pubkeyX25519) consola28.warn("No X25519 public key found on disk \u2014 sealed capability secrets cannot be bound until the agent is re-spawned.");
|
|
3708
|
+
const { system_prompt: systemPrompt, tools, skills, tasks, recipe_ref: recipeRef } = await client.listTasks();
|
|
3709
|
+
consola28.info(`Pulled ${tasks.length} task${tasks.length === 1 ? "" : "s"}`);
|
|
3710
|
+
consola28.info(`Tools enabled: ${tools.length === 0 ? "(none)" : tools.join(", ")}`);
|
|
3711
|
+
consola28.info(`Skills: ${skills.length === 0 ? "(none)" : skills.map((s) => s.name).join(", ")}`);
|
|
3665
3712
|
let agentUid = null;
|
|
3666
3713
|
let agentGid = null;
|
|
3667
3714
|
if (process.geteuid?.() === 0) {
|
|
3668
3715
|
try {
|
|
3669
|
-
const homeStat =
|
|
3716
|
+
const homeStat = statSync2(homedir10());
|
|
3670
3717
|
agentUid = homeStat.uid;
|
|
3671
3718
|
agentGid = homeStat.gid;
|
|
3672
3719
|
} catch {
|
|
@@ -3680,50 +3727,51 @@ var syncAgentCommand = defineCommand31({
|
|
|
3680
3727
|
}
|
|
3681
3728
|
}
|
|
3682
3729
|
}
|
|
3683
|
-
const agentDir =
|
|
3684
|
-
|
|
3685
|
-
chownToAgent(
|
|
3730
|
+
const agentDir = join9(homedir10(), ".openape", "agent");
|
|
3731
|
+
mkdirSync4(agentDir, { recursive: true });
|
|
3732
|
+
chownToAgent(join9(homedir10(), ".openape"));
|
|
3686
3733
|
chownToAgent(agentDir);
|
|
3687
|
-
const agentJsonPath =
|
|
3688
|
-
|
|
3734
|
+
const agentJsonPath = join9(agentDir, "agent.json");
|
|
3735
|
+
writeFileSync7(
|
|
3689
3736
|
agentJsonPath,
|
|
3690
|
-
`${JSON.stringify({ systemPrompt, tools }, null, 2)}
|
|
3737
|
+
`${JSON.stringify({ systemPrompt, tools, ...recipeRef ? { recipeRef } : {} }, null, 2)}
|
|
3691
3738
|
`,
|
|
3692
3739
|
{ mode: 384 }
|
|
3693
3740
|
);
|
|
3694
3741
|
chownToAgent(agentJsonPath);
|
|
3695
|
-
|
|
3742
|
+
if (recipeRef) ensureRecipeCheckout(recipeRef, join9(homedir10(), "recipe"));
|
|
3743
|
+
mkdirSync4(TASK_CACHE_DIR2, { recursive: true });
|
|
3696
3744
|
chownToAgent(TASK_CACHE_DIR2);
|
|
3697
3745
|
for (const task of tasks) {
|
|
3698
|
-
const path2 =
|
|
3699
|
-
|
|
3746
|
+
const path2 = join9(TASK_CACHE_DIR2, `${task.taskId}.json`);
|
|
3747
|
+
writeFileSync7(path2, `${JSON.stringify(task, null, 2)}
|
|
3700
3748
|
`, { mode: 384 });
|
|
3701
3749
|
chownToAgent(path2);
|
|
3702
3750
|
}
|
|
3703
|
-
const skillsDir =
|
|
3704
|
-
|
|
3751
|
+
const skillsDir = join9(agentDir, "skills");
|
|
3752
|
+
mkdirSync4(skillsDir, { recursive: true });
|
|
3705
3753
|
chownToAgent(skillsDir);
|
|
3706
3754
|
const incomingNames = new Set(skills.map((s) => s.name));
|
|
3707
3755
|
try {
|
|
3708
3756
|
for (const entry of readdirSync2(skillsDir)) {
|
|
3709
3757
|
if (incomingNames.has(entry)) continue;
|
|
3710
3758
|
try {
|
|
3711
|
-
|
|
3759
|
+
rmSync3(join9(skillsDir, entry), { recursive: true, force: true });
|
|
3712
3760
|
} catch {
|
|
3713
3761
|
}
|
|
3714
3762
|
}
|
|
3715
3763
|
} catch {
|
|
3716
3764
|
}
|
|
3717
3765
|
for (const skill of skills) {
|
|
3718
|
-
const skillDir =
|
|
3719
|
-
|
|
3766
|
+
const skillDir = join9(skillsDir, skill.name);
|
|
3767
|
+
mkdirSync4(skillDir, { recursive: true });
|
|
3720
3768
|
chownToAgent(skillDir);
|
|
3721
|
-
const skillPath =
|
|
3722
|
-
|
|
3769
|
+
const skillPath = join9(skillDir, "SKILL.md");
|
|
3770
|
+
writeFileSync7(skillPath, skill.body.endsWith("\n") ? skill.body : `${skill.body}
|
|
3723
3771
|
`, { mode: 384 });
|
|
3724
3772
|
chownToAgent(skillPath);
|
|
3725
3773
|
}
|
|
3726
|
-
|
|
3774
|
+
consola28.success("Sync complete.");
|
|
3727
3775
|
}
|
|
3728
3776
|
});
|
|
3729
3777
|
|
|
@@ -3751,19 +3799,19 @@ var agentsCommand = defineCommand32({
|
|
|
3751
3799
|
import { defineCommand as defineCommand40 } from "citty";
|
|
3752
3800
|
|
|
3753
3801
|
// src/commands/nest/authorize.ts
|
|
3754
|
-
import { execFileSync as
|
|
3755
|
-
import { existsSync as
|
|
3756
|
-
import { join as
|
|
3802
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
3803
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
3804
|
+
import { join as join11 } from "path";
|
|
3757
3805
|
import { defineCommand as defineCommand34 } from "citty";
|
|
3758
|
-
import
|
|
3806
|
+
import consola30 from "consola";
|
|
3759
3807
|
|
|
3760
3808
|
// src/commands/nest/enroll.ts
|
|
3761
3809
|
import { hostname as hostname4, homedir as homedir11 } from "os";
|
|
3762
|
-
import { existsSync as
|
|
3763
|
-
import { join as
|
|
3810
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8, chmodSync } from "fs";
|
|
3811
|
+
import { join as join10 } from "path";
|
|
3764
3812
|
import { defineCommand as defineCommand33 } from "citty";
|
|
3765
|
-
import
|
|
3766
|
-
var NEST_DATA_DIR =
|
|
3813
|
+
import consola29 from "consola";
|
|
3814
|
+
var NEST_DATA_DIR = join10(homedir11(), ".openape", "nest");
|
|
3767
3815
|
function nestAgentName() {
|
|
3768
3816
|
const raw = hostname4().toLowerCase();
|
|
3769
3817
|
const head = raw.split(".")[0] ?? raw;
|
|
@@ -3796,25 +3844,25 @@ var enrollNestCommand = defineCommand33({
|
|
|
3796
3844
|
throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
|
|
3797
3845
|
}
|
|
3798
3846
|
const name = args.name || nestAgentName();
|
|
3799
|
-
const authPath =
|
|
3800
|
-
if (
|
|
3847
|
+
const authPath = join10(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
3848
|
+
if (existsSync14(authPath) && !args.force) {
|
|
3801
3849
|
throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
|
|
3802
3850
|
}
|
|
3803
|
-
const sshDir =
|
|
3804
|
-
const configDir =
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3851
|
+
const sshDir = join10(NEST_DATA_DIR, ".ssh");
|
|
3852
|
+
const configDir = join10(NEST_DATA_DIR, ".config", "apes");
|
|
3853
|
+
mkdirSync5(sshDir, { recursive: true });
|
|
3854
|
+
mkdirSync5(configDir, { recursive: true });
|
|
3855
|
+
consola29.start(`Generating keypair for ${name}\u2026`);
|
|
3808
3856
|
const { privatePem, publicSshLine } = generateKeyPairInMemory();
|
|
3809
|
-
|
|
3857
|
+
writeFileSync8(join10(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
|
|
3810
3858
|
`, { mode: 384 });
|
|
3811
|
-
|
|
3859
|
+
writeFileSync8(join10(sshDir, "id_ed25519.pub"), `${publicSshLine}
|
|
3812
3860
|
`, { mode: 420 });
|
|
3813
3861
|
chmodSync(sshDir, 448);
|
|
3814
|
-
|
|
3862
|
+
consola29.start(`Registering nest at ${idp}\u2026`);
|
|
3815
3863
|
const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
|
|
3816
|
-
|
|
3817
|
-
|
|
3864
|
+
consola29.success(`Registered as ${registration.email}`);
|
|
3865
|
+
consola29.start("Issuing nest access token\u2026");
|
|
3818
3866
|
const { token, expiresIn } = await issueAgentToken({
|
|
3819
3867
|
idp,
|
|
3820
3868
|
agentEmail: registration.email,
|
|
@@ -3825,16 +3873,16 @@ var enrollNestCommand = defineCommand33({
|
|
|
3825
3873
|
accessToken: token,
|
|
3826
3874
|
email: registration.email,
|
|
3827
3875
|
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
3828
|
-
keyPath:
|
|
3876
|
+
keyPath: join10(sshDir, "id_ed25519"),
|
|
3829
3877
|
ownerEmail: ownerAuth.email
|
|
3830
3878
|
});
|
|
3831
|
-
|
|
3879
|
+
writeFileSync8(authPath, authJson, { mode: 384 });
|
|
3832
3880
|
chmodSync(configDir, 448);
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3881
|
+
consola29.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
|
|
3882
|
+
consola29.info("");
|
|
3883
|
+
consola29.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
|
|
3884
|
+
consola29.info("");
|
|
3885
|
+
consola29.info(" apes nest authorize");
|
|
3838
3886
|
}
|
|
3839
3887
|
});
|
|
3840
3888
|
|
|
@@ -3897,14 +3945,14 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3897
3945
|
}
|
|
3898
3946
|
},
|
|
3899
3947
|
async run({ args }) {
|
|
3900
|
-
const nestAuthPath =
|
|
3901
|
-
if (!
|
|
3948
|
+
const nestAuthPath = join11(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
3949
|
+
if (!existsSync15(nestAuthPath)) {
|
|
3902
3950
|
throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
|
|
3903
3951
|
}
|
|
3904
|
-
const nestAuth = JSON.parse(
|
|
3952
|
+
const nestAuth = JSON.parse(readFileSync13(nestAuthPath, "utf8"));
|
|
3905
3953
|
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
3906
3954
|
const allow = args.allow ?? DEFAULT_ALLOW_PATTERNS.join(",");
|
|
3907
|
-
|
|
3955
|
+
consola30.info(`Configuring YOLO-policy on ${nestAuth.email} via \`apes yolo set\`\u2026`);
|
|
3908
3956
|
const cmdArgs = [
|
|
3909
3957
|
"yolo",
|
|
3910
3958
|
"set",
|
|
@@ -3918,19 +3966,19 @@ var authorizeNestCommand = defineCommand34({
|
|
|
3918
3966
|
cmdArgs.push("--expires-in", args["expires-in"]);
|
|
3919
3967
|
}
|
|
3920
3968
|
try {
|
|
3921
|
-
|
|
3969
|
+
execFileSync8("apes", cmdArgs, { stdio: "inherit" });
|
|
3922
3970
|
} catch (err) {
|
|
3923
3971
|
throw new CliError(err instanceof Error ? err.message : String(err));
|
|
3924
3972
|
}
|
|
3925
|
-
|
|
3926
|
-
|
|
3973
|
+
consola30.success("Nest-driven agent lifecycle is now zero-prompt.");
|
|
3974
|
+
consola30.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
|
|
3927
3975
|
}
|
|
3928
3976
|
});
|
|
3929
3977
|
|
|
3930
3978
|
// src/commands/nest/destroy.ts
|
|
3931
|
-
import { execFileSync as
|
|
3979
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
3932
3980
|
import { defineCommand as defineCommand35 } from "citty";
|
|
3933
|
-
import
|
|
3981
|
+
import consola31 from "consola";
|
|
3934
3982
|
var destroyNestCommand = defineCommand35({
|
|
3935
3983
|
meta: {
|
|
3936
3984
|
name: "destroy",
|
|
@@ -3942,8 +3990,8 @@ var destroyNestCommand = defineCommand35({
|
|
|
3942
3990
|
async run({ args }) {
|
|
3943
3991
|
const name = String(args.name);
|
|
3944
3992
|
try {
|
|
3945
|
-
|
|
3946
|
-
|
|
3993
|
+
execFileSync9("apes", ["run", "--as", "root", "--wait", "--", "apes", "agents", "destroy", name, "--force"], { stdio: "inherit" });
|
|
3994
|
+
consola31.success(`Nest will tear down ${name}'s pm2 process on its next reconcile (\u22642s).`);
|
|
3947
3995
|
} catch (err) {
|
|
3948
3996
|
const status = err.status ?? 1;
|
|
3949
3997
|
throw new CliExit(status);
|
|
@@ -3952,11 +4000,11 @@ var destroyNestCommand = defineCommand35({
|
|
|
3952
4000
|
});
|
|
3953
4001
|
|
|
3954
4002
|
// src/commands/nest/install.ts
|
|
3955
|
-
import { existsSync as
|
|
4003
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
|
|
3956
4004
|
import { homedir as homedir12 } from "os";
|
|
3957
|
-
import { dirname as
|
|
4005
|
+
import { dirname as dirname3, join as join12 } from "path";
|
|
3958
4006
|
import { defineCommand as defineCommand36 } from "citty";
|
|
3959
|
-
import
|
|
4007
|
+
import consola32 from "consola";
|
|
3960
4008
|
|
|
3961
4009
|
// src/commands/nest/apes-agents-adapter.ts
|
|
3962
4010
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4022,41 +4070,41 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4022
4070
|
|
|
4023
4071
|
// src/commands/nest/install.ts
|
|
4024
4072
|
function installAdapter2() {
|
|
4025
|
-
const target =
|
|
4026
|
-
|
|
4073
|
+
const target = join12(homedir12(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4074
|
+
mkdirSync6(dirname3(target), { recursive: true });
|
|
4027
4075
|
let existing = "";
|
|
4028
4076
|
try {
|
|
4029
|
-
existing =
|
|
4077
|
+
existing = readFileSync14(target, "utf8");
|
|
4030
4078
|
} catch {
|
|
4031
4079
|
}
|
|
4032
4080
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4033
|
-
|
|
4034
|
-
|
|
4081
|
+
writeFileSync9(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4082
|
+
consola32.success(`Wrote shapes adapter ${target}`);
|
|
4035
4083
|
return true;
|
|
4036
4084
|
}
|
|
4037
4085
|
function writeBridgeModelDefault(model) {
|
|
4038
|
-
for (const envDir of [
|
|
4039
|
-
const envFile =
|
|
4040
|
-
|
|
4086
|
+
for (const envDir of [join12(homedir12(), "litellm"), join12(NEST_DATA_DIR, "litellm")]) {
|
|
4087
|
+
const envFile = join12(envDir, ".env");
|
|
4088
|
+
mkdirSync6(envDir, { recursive: true });
|
|
4041
4089
|
let lines = [];
|
|
4042
|
-
if (
|
|
4043
|
-
lines =
|
|
4090
|
+
if (existsSync16(envFile)) {
|
|
4091
|
+
lines = readFileSync14(envFile, "utf8").split("\n").filter((l) => !l.startsWith("APE_CHAT_BRIDGE_MODEL="));
|
|
4044
4092
|
}
|
|
4045
4093
|
lines.push(`APE_CHAT_BRIDGE_MODEL=${model}`);
|
|
4046
4094
|
while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
|
|
4047
|
-
|
|
4095
|
+
writeFileSync9(envFile, `${lines.join("\n")}
|
|
4048
4096
|
`, { mode: 384 });
|
|
4049
4097
|
}
|
|
4050
4098
|
}
|
|
4051
4099
|
function findBinary(name) {
|
|
4052
4100
|
for (const dir of [
|
|
4053
|
-
|
|
4101
|
+
join12(homedir12(), ".bun", "bin"),
|
|
4054
4102
|
"/opt/homebrew/bin",
|
|
4055
4103
|
"/usr/local/bin",
|
|
4056
4104
|
"/usr/bin"
|
|
4057
4105
|
]) {
|
|
4058
|
-
const p =
|
|
4059
|
-
if (
|
|
4106
|
+
const p = join12(dir, name);
|
|
4107
|
+
if (existsSync16(p)) return p;
|
|
4060
4108
|
}
|
|
4061
4109
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4062
4110
|
}
|
|
@@ -4083,16 +4131,16 @@ var installNestCommand = defineCommand36({
|
|
|
4083
4131
|
}
|
|
4084
4132
|
const nestBin = findBinary("openape-nest");
|
|
4085
4133
|
const apesBin = findBinary("apes");
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4134
|
+
consola32.info(`Installing nest supervisor`);
|
|
4135
|
+
consola32.info(` nest binary: ${nestBin}`);
|
|
4136
|
+
consola32.info(` apes binary: ${apesBin}`);
|
|
4137
|
+
consola32.info(` HTTP port: ${port}`);
|
|
4090
4138
|
if (typeof args["bridge-model"] === "string" && args["bridge-model"]) {
|
|
4091
4139
|
writeBridgeModelDefault(args["bridge-model"]);
|
|
4092
|
-
|
|
4140
|
+
consola32.success(`Default bridge model set to ${args["bridge-model"]} (in ~/litellm/.env)`);
|
|
4093
4141
|
}
|
|
4094
4142
|
installAdapter2();
|
|
4095
|
-
|
|
4143
|
+
mkdirSync6(NEST_DATA_DIR, { recursive: true });
|
|
4096
4144
|
await getHostPlatform().installNestSupervisor({
|
|
4097
4145
|
nestBin,
|
|
4098
4146
|
apesBin,
|
|
@@ -4100,20 +4148,20 @@ var installNestCommand = defineCommand36({
|
|
|
4100
4148
|
nestHome: NEST_DATA_DIR,
|
|
4101
4149
|
port
|
|
4102
4150
|
});
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4151
|
+
consola32.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4152
|
+
consola32.info("");
|
|
4153
|
+
consola32.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4154
|
+
consola32.info("");
|
|
4155
|
+
consola32.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4156
|
+
consola32.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4157
|
+
consola32.info("");
|
|
4158
|
+
consola32.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
4111
4159
|
}
|
|
4112
4160
|
});
|
|
4113
4161
|
|
|
4114
4162
|
// src/commands/nest/list.ts
|
|
4115
4163
|
import { defineCommand as defineCommand37 } from "citty";
|
|
4116
|
-
import
|
|
4164
|
+
import consola33 from "consola";
|
|
4117
4165
|
var listNestCommand = defineCommand37({
|
|
4118
4166
|
meta: {
|
|
4119
4167
|
name: "list",
|
|
@@ -4129,21 +4177,21 @@ var listNestCommand = defineCommand37({
|
|
|
4129
4177
|
return;
|
|
4130
4178
|
}
|
|
4131
4179
|
if (reg.agents.length === 0) {
|
|
4132
|
-
|
|
4180
|
+
consola33.info("(no agents registered with this nest)");
|
|
4133
4181
|
return;
|
|
4134
4182
|
}
|
|
4135
|
-
|
|
4183
|
+
consola33.info(`${reg.agents.length} agent(s) registered with this nest:`);
|
|
4136
4184
|
for (const a of reg.agents) {
|
|
4137
4185
|
const bridge = a.bridge ? " bridge=on" : "";
|
|
4138
|
-
|
|
4186
|
+
consola33.info(` ${a.name.padEnd(16)} uid=${String(a.uid).padEnd(5)} home=${a.home}${bridge}`);
|
|
4139
4187
|
}
|
|
4140
4188
|
}
|
|
4141
4189
|
});
|
|
4142
4190
|
|
|
4143
4191
|
// src/commands/nest/spawn.ts
|
|
4144
|
-
import { execFileSync as
|
|
4192
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4145
4193
|
import { defineCommand as defineCommand38 } from "citty";
|
|
4146
|
-
import
|
|
4194
|
+
import consola34 from "consola";
|
|
4147
4195
|
var spawnNestCommand = defineCommand38({
|
|
4148
4196
|
meta: {
|
|
4149
4197
|
name: "spawn",
|
|
@@ -4174,8 +4222,8 @@ var spawnNestCommand = defineCommand38({
|
|
|
4174
4222
|
if (typeof args["bridge-base-url"] === "string") apesArgs.push("--bridge-base-url", args["bridge-base-url"]);
|
|
4175
4223
|
if (typeof args["bridge-model"] === "string") apesArgs.push("--bridge-model", args["bridge-model"]);
|
|
4176
4224
|
try {
|
|
4177
|
-
|
|
4178
|
-
|
|
4225
|
+
execFileSync10("apes", apesArgs, { stdio: "inherit" });
|
|
4226
|
+
consola34.success(`Nest will pick up ${name} on its next reconcile (\u22642s).`);
|
|
4179
4227
|
} catch (err) {
|
|
4180
4228
|
const status = err.status ?? 1;
|
|
4181
4229
|
throw new CliExit(status);
|
|
@@ -4185,7 +4233,7 @@ var spawnNestCommand = defineCommand38({
|
|
|
4185
4233
|
|
|
4186
4234
|
// src/commands/nest/uninstall.ts
|
|
4187
4235
|
import { defineCommand as defineCommand39 } from "citty";
|
|
4188
|
-
import
|
|
4236
|
+
import consola35 from "consola";
|
|
4189
4237
|
var uninstallNestCommand = defineCommand39({
|
|
4190
4238
|
meta: {
|
|
4191
4239
|
name: "uninstall",
|
|
@@ -4193,8 +4241,8 @@ var uninstallNestCommand = defineCommand39({
|
|
|
4193
4241
|
},
|
|
4194
4242
|
async run() {
|
|
4195
4243
|
await getHostPlatform().uninstallNestSupervisor();
|
|
4196
|
-
|
|
4197
|
-
|
|
4244
|
+
consola35.success("Nest daemon stopped + supervisor unit removed");
|
|
4245
|
+
consola35.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4198
4246
|
}
|
|
4199
4247
|
});
|
|
4200
4248
|
|
|
@@ -4220,7 +4268,7 @@ import { defineCommand as defineCommand44 } from "citty";
|
|
|
4220
4268
|
|
|
4221
4269
|
// src/commands/yolo/clear.ts
|
|
4222
4270
|
import { defineCommand as defineCommand41 } from "citty";
|
|
4223
|
-
import
|
|
4271
|
+
import consola36 from "consola";
|
|
4224
4272
|
var yoloClearCommand = defineCommand41({
|
|
4225
4273
|
meta: {
|
|
4226
4274
|
name: "clear",
|
|
@@ -4250,13 +4298,13 @@ var yoloClearCommand = defineCommand41({
|
|
|
4250
4298
|
const text = await res.text().catch(() => "");
|
|
4251
4299
|
throw new CliError(`DELETE /yolo-policy failed (${res.status}): ${text}`);
|
|
4252
4300
|
}
|
|
4253
|
-
|
|
4301
|
+
consola36.success(`YOLO-policy cleared on ${email}`);
|
|
4254
4302
|
}
|
|
4255
4303
|
});
|
|
4256
4304
|
|
|
4257
4305
|
// src/commands/yolo/set.ts
|
|
4258
4306
|
import { defineCommand as defineCommand42 } from "citty";
|
|
4259
|
-
import
|
|
4307
|
+
import consola37 from "consola";
|
|
4260
4308
|
var VALID_MODES = ["allow-list", "deny-list"];
|
|
4261
4309
|
var yoloSetCommand = defineCommand42({
|
|
4262
4310
|
meta: {
|
|
@@ -4306,12 +4354,12 @@ var yoloSetCommand = defineCommand42({
|
|
|
4306
4354
|
const denyPatterns = parseList(args.deny);
|
|
4307
4355
|
const denyRiskThreshold = args["deny-risk"] ?? null;
|
|
4308
4356
|
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
if (allowPatterns.length)
|
|
4312
|
-
if (denyPatterns.length)
|
|
4313
|
-
if (denyRiskThreshold)
|
|
4314
|
-
if (expiresAt)
|
|
4357
|
+
consola37.info(`Setting YOLO-policy on ${email}`);
|
|
4358
|
+
consola37.info(` mode: ${mode}`);
|
|
4359
|
+
if (allowPatterns.length) consola37.info(` allow_patterns: ${allowPatterns.join(", ")}`);
|
|
4360
|
+
if (denyPatterns.length) consola37.info(` deny_patterns: ${denyPatterns.join(", ")}`);
|
|
4361
|
+
if (denyRiskThreshold) consola37.info(` deny_risk: ${denyRiskThreshold}`);
|
|
4362
|
+
if (expiresAt) consola37.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4315
4363
|
const url = `${idp}/api/users/${encodeURIComponent(email)}/yolo-policy`;
|
|
4316
4364
|
const res = await fetch(url, {
|
|
4317
4365
|
method: "PUT",
|
|
@@ -4331,7 +4379,7 @@ var yoloSetCommand = defineCommand42({
|
|
|
4331
4379
|
const text = await res.text().catch(() => "");
|
|
4332
4380
|
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
4333
4381
|
}
|
|
4334
|
-
|
|
4382
|
+
consola37.success(`YOLO-policy applied to ${email}`);
|
|
4335
4383
|
}
|
|
4336
4384
|
});
|
|
4337
4385
|
function parseList(s) {
|
|
@@ -4350,7 +4398,7 @@ function parseExpiresIn(s) {
|
|
|
4350
4398
|
|
|
4351
4399
|
// src/commands/yolo/show.ts
|
|
4352
4400
|
import { defineCommand as defineCommand43 } from "citty";
|
|
4353
|
-
import
|
|
4401
|
+
import consola38 from "consola";
|
|
4354
4402
|
var yoloShowCommand = defineCommand43({
|
|
4355
4403
|
meta: {
|
|
4356
4404
|
name: "show",
|
|
@@ -4388,12 +4436,12 @@ var yoloShowCommand = defineCommand43({
|
|
|
4388
4436
|
console.log(JSON.stringify(policy, null, 2));
|
|
4389
4437
|
return;
|
|
4390
4438
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4439
|
+
consola38.info(`YOLO-policy for ${email}`);
|
|
4440
|
+
consola38.info(` mode: ${policy.mode}`);
|
|
4441
|
+
consola38.info(` allow_patterns: ${policy.allowPatterns.length ? policy.allowPatterns.join(", ") : "(none)"}`);
|
|
4442
|
+
consola38.info(` deny_patterns: ${policy.denyPatterns.length ? policy.denyPatterns.join(", ") : "(none)"}`);
|
|
4443
|
+
consola38.info(` deny_risk: ${policy.denyRiskThreshold ?? "(none)"}`);
|
|
4444
|
+
consola38.info(` expires_at: ${policy.expiresAt ? new Date(policy.expiresAt * 1e3).toISOString() : "(never)"}`);
|
|
4397
4445
|
}
|
|
4398
4446
|
});
|
|
4399
4447
|
|
|
@@ -4412,7 +4460,7 @@ var yoloCommand = defineCommand44({
|
|
|
4412
4460
|
|
|
4413
4461
|
// src/commands/adapter/index.ts
|
|
4414
4462
|
import { defineCommand as defineCommand45 } from "citty";
|
|
4415
|
-
import
|
|
4463
|
+
import consola39 from "consola";
|
|
4416
4464
|
var adapterCommand = defineCommand45({
|
|
4417
4465
|
meta: {
|
|
4418
4466
|
name: "adapter",
|
|
@@ -4450,7 +4498,7 @@ var adapterCommand = defineCommand45({
|
|
|
4450
4498
|
`);
|
|
4451
4499
|
return;
|
|
4452
4500
|
}
|
|
4453
|
-
|
|
4501
|
+
consola39.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4454
4502
|
for (const a of index2.adapters) {
|
|
4455
4503
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4456
4504
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4472,7 +4520,7 @@ var adapterCommand = defineCommand45({
|
|
|
4472
4520
|
return;
|
|
4473
4521
|
}
|
|
4474
4522
|
if (local.length === 0) {
|
|
4475
|
-
|
|
4523
|
+
consola39.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4476
4524
|
return;
|
|
4477
4525
|
}
|
|
4478
4526
|
for (const a of local) {
|
|
@@ -4509,20 +4557,20 @@ var adapterCommand = defineCommand45({
|
|
|
4509
4557
|
for (const id of ids) {
|
|
4510
4558
|
const entry = findAdapter(index, id);
|
|
4511
4559
|
if (!entry) {
|
|
4512
|
-
|
|
4560
|
+
consola39.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4513
4561
|
continue;
|
|
4514
4562
|
}
|
|
4515
4563
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4516
4564
|
if (conflicts.length > 0) {
|
|
4517
4565
|
for (const c of conflicts) {
|
|
4518
|
-
|
|
4519
|
-
|
|
4566
|
+
consola39.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
|
|
4567
|
+
consola39.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
|
|
4520
4568
|
}
|
|
4521
4569
|
}
|
|
4522
4570
|
const result = await installAdapter(entry, { local });
|
|
4523
4571
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4524
|
-
|
|
4525
|
-
|
|
4572
|
+
consola39.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4573
|
+
consola39.info(`Digest: ${result.digest}`);
|
|
4526
4574
|
}
|
|
4527
4575
|
}
|
|
4528
4576
|
}),
|
|
@@ -4549,9 +4597,9 @@ var adapterCommand = defineCommand45({
|
|
|
4549
4597
|
let failed = false;
|
|
4550
4598
|
for (const id of ids) {
|
|
4551
4599
|
if (removeAdapter(id, local)) {
|
|
4552
|
-
|
|
4600
|
+
consola39.success(`Removed adapter: ${id}`);
|
|
4553
4601
|
} else {
|
|
4554
|
-
|
|
4602
|
+
consola39.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4555
4603
|
failed = true;
|
|
4556
4604
|
}
|
|
4557
4605
|
}
|
|
@@ -4633,7 +4681,7 @@ var adapterCommand = defineCommand45({
|
|
|
4633
4681
|
return;
|
|
4634
4682
|
}
|
|
4635
4683
|
if (results.length === 0) {
|
|
4636
|
-
|
|
4684
|
+
consola39.info(`No adapters matching "${query}"`);
|
|
4637
4685
|
return;
|
|
4638
4686
|
}
|
|
4639
4687
|
for (const a of results) {
|
|
@@ -4668,29 +4716,29 @@ var adapterCommand = defineCommand45({
|
|
|
4668
4716
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4669
4717
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4670
4718
|
if (targets.length === 0) {
|
|
4671
|
-
|
|
4719
|
+
consola39.info("No adapters installed to update.");
|
|
4672
4720
|
return;
|
|
4673
4721
|
}
|
|
4674
4722
|
for (const id of targets) {
|
|
4675
4723
|
const entry = findAdapter(index, id);
|
|
4676
4724
|
if (!entry) {
|
|
4677
|
-
|
|
4725
|
+
consola39.warn(`${id}: not found in registry, skipping`);
|
|
4678
4726
|
continue;
|
|
4679
4727
|
}
|
|
4680
4728
|
const localDigest = getInstalledDigest(id, false);
|
|
4681
4729
|
if (localDigest === entry.digest) {
|
|
4682
|
-
|
|
4730
|
+
consola39.info(`${id}: already up to date`);
|
|
4683
4731
|
continue;
|
|
4684
4732
|
}
|
|
4685
4733
|
if (localDigest && !args.yes) {
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4734
|
+
consola39.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
4735
|
+
consola39.info(` Old: ${localDigest}`);
|
|
4736
|
+
consola39.info(` New: ${entry.digest}`);
|
|
4737
|
+
consola39.info(" Use --yes to confirm");
|
|
4690
4738
|
continue;
|
|
4691
4739
|
}
|
|
4692
4740
|
const result = await installAdapter(entry);
|
|
4693
|
-
|
|
4741
|
+
consola39.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4694
4742
|
}
|
|
4695
4743
|
}
|
|
4696
4744
|
}),
|
|
@@ -4727,7 +4775,7 @@ var adapterCommand = defineCommand45({
|
|
|
4727
4775
|
if (!localDigest)
|
|
4728
4776
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4729
4777
|
if (localDigest === entry.digest) {
|
|
4730
|
-
|
|
4778
|
+
consola39.success(`${id}: digest matches registry`);
|
|
4731
4779
|
} else {
|
|
4732
4780
|
console.log(` Local: ${localDigest}`);
|
|
4733
4781
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4739,11 +4787,11 @@ var adapterCommand = defineCommand45({
|
|
|
4739
4787
|
});
|
|
4740
4788
|
|
|
4741
4789
|
// src/commands/run.ts
|
|
4742
|
-
import { execFileSync as
|
|
4790
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
4743
4791
|
import { hostname as hostname5 } from "os";
|
|
4744
4792
|
import { basename } from "path";
|
|
4745
4793
|
import { defineCommand as defineCommand46 } from "citty";
|
|
4746
|
-
import
|
|
4794
|
+
import consola40 from "consola";
|
|
4747
4795
|
function resolveRunAsTarget(runAs) {
|
|
4748
4796
|
if (!runAs) return runAs;
|
|
4749
4797
|
if (!AGENT_NAME_REGEX.test(runAs)) return runAs;
|
|
@@ -4799,7 +4847,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4799
4847
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4800
4848
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4801
4849
|
if (mode === "human") {
|
|
4802
|
-
|
|
4850
|
+
consola40.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4803
4851
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4804
4852
|
console.log(` Check status: ${statusCmd}`);
|
|
4805
4853
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4809,7 +4857,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4809
4857
|
return;
|
|
4810
4858
|
}
|
|
4811
4859
|
const maxMin = getPollMaxMinutes();
|
|
4812
|
-
|
|
4860
|
+
consola40.success(`Grant ${grant.id} created (pending approval)`);
|
|
4813
4861
|
console.log(` Approve: ${approveUrl}`);
|
|
4814
4862
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4815
4863
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4937,7 +4985,7 @@ async function runShellMode(command, args) {
|
|
|
4937
4985
|
}
|
|
4938
4986
|
} catch {
|
|
4939
4987
|
}
|
|
4940
|
-
|
|
4988
|
+
consola40.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4941
4989
|
const grant = await apiFetch(grantsUrl, {
|
|
4942
4990
|
method: "POST",
|
|
4943
4991
|
body: {
|
|
@@ -4957,8 +5005,8 @@ async function runShellMode(command, args) {
|
|
|
4957
5005
|
host: targetHost
|
|
4958
5006
|
});
|
|
4959
5007
|
if (shouldWaitForGrant(args)) {
|
|
4960
|
-
|
|
4961
|
-
|
|
5008
|
+
consola40.info(`Grant requested: ${grant.id}`);
|
|
5009
|
+
consola40.info("Waiting for approval...");
|
|
4962
5010
|
const maxWait = 3e5;
|
|
4963
5011
|
const interval = 3e3;
|
|
4964
5012
|
const start = Date.now();
|
|
@@ -4989,13 +5037,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4989
5037
|
try {
|
|
4990
5038
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
4991
5039
|
} catch (err) {
|
|
4992
|
-
|
|
5040
|
+
consola40.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
4993
5041
|
return false;
|
|
4994
5042
|
}
|
|
4995
5043
|
try {
|
|
4996
5044
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4997
5045
|
if (existingGrantId) {
|
|
4998
|
-
|
|
5046
|
+
consola40.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
4999
5047
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5000
5048
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5001
5049
|
return true;
|
|
@@ -5003,7 +5051,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5003
5051
|
} catch {
|
|
5004
5052
|
}
|
|
5005
5053
|
const approval = args.approval ?? "once";
|
|
5006
|
-
|
|
5054
|
+
consola40.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
5007
5055
|
const grant = await createShapesGrant(resolved, {
|
|
5008
5056
|
idp,
|
|
5009
5057
|
approval,
|
|
@@ -5011,8 +5059,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5011
5059
|
});
|
|
5012
5060
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5013
5061
|
const n = grant.similar_grants.similar_grants.length;
|
|
5014
|
-
|
|
5015
|
-
|
|
5062
|
+
consola40.info("");
|
|
5063
|
+
consola40.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
5016
5064
|
}
|
|
5017
5065
|
notifyGrantPending({
|
|
5018
5066
|
grantId: grant.id,
|
|
@@ -5022,8 +5070,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
5022
5070
|
host: args.host || hostname5()
|
|
5023
5071
|
});
|
|
5024
5072
|
if (shouldWaitForGrant(args)) {
|
|
5025
|
-
|
|
5026
|
-
|
|
5073
|
+
consola40.info(`Grant requested: ${grant.id}`);
|
|
5074
|
+
consola40.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5027
5075
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5028
5076
|
if (status !== "approved")
|
|
5029
5077
|
throw new CliError(`Grant ${status}`);
|
|
@@ -5039,7 +5087,7 @@ function execShellCommand(command) {
|
|
|
5039
5087
|
throw new CliError("No command to execute");
|
|
5040
5088
|
try {
|
|
5041
5089
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5042
|
-
|
|
5090
|
+
execFileSync11(command[0], command.slice(1), {
|
|
5043
5091
|
stdio: "inherit",
|
|
5044
5092
|
env: inheritedEnv
|
|
5045
5093
|
});
|
|
@@ -5097,7 +5145,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5097
5145
|
try {
|
|
5098
5146
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
5099
5147
|
if (existingGrantId) {
|
|
5100
|
-
|
|
5148
|
+
consola40.info(`Reusing existing grant: ${existingGrantId}`);
|
|
5101
5149
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
5102
5150
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
5103
5151
|
return;
|
|
@@ -5111,17 +5159,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
5111
5159
|
});
|
|
5112
5160
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
5113
5161
|
const n = grant.similar_grants.similar_grants.length;
|
|
5114
|
-
|
|
5115
|
-
|
|
5162
|
+
consola40.info("");
|
|
5163
|
+
consola40.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
5116
5164
|
if (grant.similar_grants.widened_details?.length) {
|
|
5117
5165
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
5118
|
-
|
|
5166
|
+
consola40.info(` Broader scope: ${wider}`);
|
|
5119
5167
|
}
|
|
5120
|
-
|
|
5168
|
+
consola40.info("");
|
|
5121
5169
|
}
|
|
5122
5170
|
if (shouldWaitForGrant(args)) {
|
|
5123
|
-
|
|
5124
|
-
|
|
5171
|
+
consola40.info(`Grant requested: ${grant.id}`);
|
|
5172
|
+
consola40.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5125
5173
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
5126
5174
|
if (status !== "approved")
|
|
5127
5175
|
throw new Error(`Grant ${status}`);
|
|
@@ -5154,7 +5202,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5154
5202
|
const { authz_jwt: authz_jwt2 } = await apiFetch(`${grantsUrl}/${reusableId}/token`, { method: "POST" });
|
|
5155
5203
|
return executeWithGrantToken({ audience, command, args, token: authz_jwt2 });
|
|
5156
5204
|
}
|
|
5157
|
-
|
|
5205
|
+
consola40.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
5158
5206
|
const grant = await apiFetch(grantsUrl, {
|
|
5159
5207
|
method: "POST",
|
|
5160
5208
|
body: {
|
|
@@ -5171,9 +5219,9 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5171
5219
|
printPendingGrantInfo(grant, idp);
|
|
5172
5220
|
throw new CliExit(getAsyncExitCode());
|
|
5173
5221
|
}
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5222
|
+
consola40.success(`Grant requested: ${grant.id}`);
|
|
5223
|
+
consola40.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5224
|
+
consola40.info("Waiting for approval...");
|
|
5177
5225
|
const maxWait = 15 * 60 * 1e3;
|
|
5178
5226
|
const interval = 3e3;
|
|
5179
5227
|
const start = Date.now();
|
|
@@ -5181,7 +5229,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5181
5229
|
while (Date.now() - start < maxWait) {
|
|
5182
5230
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5183
5231
|
if (status.status === "approved") {
|
|
5184
|
-
|
|
5232
|
+
consola40.success("Grant approved!");
|
|
5185
5233
|
approved = true;
|
|
5186
5234
|
break;
|
|
5187
5235
|
}
|
|
@@ -5196,7 +5244,7 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5196
5244
|
`Grant approval timed out after ${minutes} min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id} \u2014 if approved later, re-run the same \`apes run\` command and it will reuse the grant.`
|
|
5197
5245
|
);
|
|
5198
5246
|
}
|
|
5199
|
-
|
|
5247
|
+
consola40.info("Fetching grant token...");
|
|
5200
5248
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5201
5249
|
method: "POST"
|
|
5202
5250
|
});
|
|
@@ -5205,10 +5253,10 @@ async function runAudienceMode(audience, action, args, commandArgv) {
|
|
|
5205
5253
|
function executeWithGrantToken(opts) {
|
|
5206
5254
|
const { audience, command, args, token } = opts;
|
|
5207
5255
|
if (audience === "escapes") {
|
|
5208
|
-
|
|
5256
|
+
consola40.info(`Executing: ${command.join(" ")}`);
|
|
5209
5257
|
try {
|
|
5210
5258
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5211
|
-
|
|
5259
|
+
execFileSync11(args["escapes-path"] || "escapes", ["--grant", token, "--", ...command], {
|
|
5212
5260
|
stdio: "inherit",
|
|
5213
5261
|
env: inheritedEnv
|
|
5214
5262
|
});
|
|
@@ -5244,11 +5292,11 @@ async function findReusableAudienceGrant(opts) {
|
|
|
5244
5292
|
|
|
5245
5293
|
// src/commands/proxy.ts
|
|
5246
5294
|
import { spawn as spawn2 } from "child_process";
|
|
5247
|
-
import { existsSync as
|
|
5295
|
+
import { existsSync as existsSync18 } from "fs";
|
|
5248
5296
|
import { homedir as homedir13 } from "os";
|
|
5249
|
-
import { join as
|
|
5297
|
+
import { join as join15 } from "path";
|
|
5250
5298
|
import { defineCommand as defineCommand47 } from "citty";
|
|
5251
|
-
import
|
|
5299
|
+
import consola41 from "consola";
|
|
5252
5300
|
|
|
5253
5301
|
// src/proxy/config.ts
|
|
5254
5302
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5282,10 +5330,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5282
5330
|
|
|
5283
5331
|
// src/proxy/local-proxy.ts
|
|
5284
5332
|
import { spawn } from "child_process";
|
|
5285
|
-
import { mkdtempSync as mkdtempSync2, rmSync as
|
|
5333
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
5286
5334
|
import { createRequire } from "module";
|
|
5287
5335
|
import { tmpdir as tmpdir2 } from "os";
|
|
5288
|
-
import { dirname as
|
|
5336
|
+
import { dirname as dirname4, join as join13, resolve as resolve4 } from "path";
|
|
5289
5337
|
var require2 = createRequire(import.meta.url);
|
|
5290
5338
|
function findProxyBin() {
|
|
5291
5339
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5294,12 +5342,12 @@ function findProxyBin() {
|
|
|
5294
5342
|
if (!binRel) {
|
|
5295
5343
|
throw new Error("@openape/proxy is missing the openape-proxy bin entry");
|
|
5296
5344
|
}
|
|
5297
|
-
return
|
|
5345
|
+
return resolve4(dirname4(pkgPath), binRel);
|
|
5298
5346
|
}
|
|
5299
5347
|
async function startEphemeralProxy(configToml) {
|
|
5300
|
-
const tmpDir = mkdtempSync2(
|
|
5301
|
-
const configPath =
|
|
5302
|
-
|
|
5348
|
+
const tmpDir = mkdtempSync2(join13(tmpdir2(), "openape-proxy-"));
|
|
5349
|
+
const configPath = join13(tmpDir, "config.toml");
|
|
5350
|
+
writeFileSync10(configPath, configToml, { mode: 384 });
|
|
5303
5351
|
const binPath = findProxyBin();
|
|
5304
5352
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
5305
5353
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5307,7 +5355,7 @@ async function startEphemeralProxy(configToml) {
|
|
|
5307
5355
|
});
|
|
5308
5356
|
const cleanupTmp = () => {
|
|
5309
5357
|
try {
|
|
5310
|
-
|
|
5358
|
+
rmSync4(tmpDir, { recursive: true, force: true });
|
|
5311
5359
|
} catch {
|
|
5312
5360
|
}
|
|
5313
5361
|
};
|
|
@@ -5381,9 +5429,9 @@ function waitForListenLine(child) {
|
|
|
5381
5429
|
}
|
|
5382
5430
|
|
|
5383
5431
|
// src/proxy/trust-bundle.ts
|
|
5384
|
-
import { existsSync as
|
|
5432
|
+
import { existsSync as existsSync17, mkdtempSync as mkdtempSync3, readFileSync as readFileSync15, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
5385
5433
|
import { tmpdir as tmpdir3 } from "os";
|
|
5386
|
-
import { join as
|
|
5434
|
+
import { join as join14 } from "path";
|
|
5387
5435
|
var CANDIDATES = [
|
|
5388
5436
|
"/etc/ssl/cert.pem",
|
|
5389
5437
|
// macOS
|
|
@@ -5396,18 +5444,18 @@ var CANDIDATES = [
|
|
|
5396
5444
|
];
|
|
5397
5445
|
function detectSystemCaPath() {
|
|
5398
5446
|
for (const p of CANDIDATES) {
|
|
5399
|
-
if (
|
|
5447
|
+
if (existsSync17(p)) return p;
|
|
5400
5448
|
}
|
|
5401
5449
|
throw new Error(
|
|
5402
5450
|
`Could not locate a system CA bundle. Tried: ${CANDIDATES.join(", ")}. Set NODE_EXTRA_CA_CERTS yourself or pass --allow-no-system-ca.`
|
|
5403
5451
|
);
|
|
5404
5452
|
}
|
|
5405
5453
|
function buildTrustBundle(opts) {
|
|
5406
|
-
const dir = mkdtempSync3(
|
|
5407
|
-
const path2 =
|
|
5408
|
-
const sys =
|
|
5409
|
-
const local =
|
|
5410
|
-
|
|
5454
|
+
const dir = mkdtempSync3(join14(tmpdir3(), "openape-trust-"));
|
|
5455
|
+
const path2 = join14(dir, "bundle.pem");
|
|
5456
|
+
const sys = readFileSync15(opts.systemCaPath, "utf-8");
|
|
5457
|
+
const local = readFileSync15(opts.localCaPath, "utf-8");
|
|
5458
|
+
writeFileSync11(path2, `${sys.trimEnd()}
|
|
5411
5459
|
${local.trimEnd()}
|
|
5412
5460
|
`, { mode: 384 });
|
|
5413
5461
|
return {
|
|
@@ -5436,7 +5484,7 @@ function resolveProxyConfigOptions() {
|
|
|
5436
5484
|
77
|
|
5437
5485
|
);
|
|
5438
5486
|
}
|
|
5439
|
-
|
|
5487
|
+
consola41.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5440
5488
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5441
5489
|
}
|
|
5442
5490
|
var proxyCommand = defineCommand47({
|
|
@@ -5463,9 +5511,9 @@ var proxyCommand = defineCommand47({
|
|
|
5463
5511
|
let close = null;
|
|
5464
5512
|
if (reuseHostPort) {
|
|
5465
5513
|
proxyUrl = `http://${reuseHostPort}`;
|
|
5466
|
-
|
|
5467
|
-
const localCaPath =
|
|
5468
|
-
if (!
|
|
5514
|
+
consola41.info(`[apes proxy] using long-running daemon at ${proxyUrl}`);
|
|
5515
|
+
const localCaPath = join15(homedir13(), ".openape", "proxy", "ca.crt");
|
|
5516
|
+
if (!existsSync18(localCaPath)) {
|
|
5469
5517
|
throw new CliError(
|
|
5470
5518
|
`OPENAPE_PROXY is set but no local CA found at ${localCaPath}. Start the daemon (sudo -u <agent> apes proxy --global < secrets.toml) first.`
|
|
5471
5519
|
);
|
|
@@ -5474,15 +5522,15 @@ var proxyCommand = defineCommand47({
|
|
|
5474
5522
|
systemCaPath: detectSystemCaPath(),
|
|
5475
5523
|
localCaPath
|
|
5476
5524
|
});
|
|
5477
|
-
|
|
5525
|
+
consola41.debug(`[apes proxy] trust bundle: ${bundle.path}`);
|
|
5478
5526
|
} else if (reuseUrl) {
|
|
5479
5527
|
proxyUrl = reuseUrl;
|
|
5480
|
-
|
|
5528
|
+
consola41.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5481
5529
|
} else {
|
|
5482
5530
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5483
5531
|
proxyUrl = ephemeral.url;
|
|
5484
5532
|
close = ephemeral.close;
|
|
5485
|
-
|
|
5533
|
+
consola41.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5486
5534
|
}
|
|
5487
5535
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5488
5536
|
const childEnv = {
|
|
@@ -5523,7 +5571,7 @@ var proxyCommand = defineCommand47({
|
|
|
5523
5571
|
else resolveExit(code ?? 0);
|
|
5524
5572
|
});
|
|
5525
5573
|
child.once("error", (err) => {
|
|
5526
|
-
|
|
5574
|
+
consola41.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5527
5575
|
resolveExit(127);
|
|
5528
5576
|
});
|
|
5529
5577
|
});
|
|
@@ -5580,7 +5628,7 @@ var explainCommand = defineCommand48({
|
|
|
5580
5628
|
|
|
5581
5629
|
// src/commands/config/get.ts
|
|
5582
5630
|
import { defineCommand as defineCommand49 } from "citty";
|
|
5583
|
-
import
|
|
5631
|
+
import consola42 from "consola";
|
|
5584
5632
|
var configGetCommand = defineCommand49({
|
|
5585
5633
|
meta: {
|
|
5586
5634
|
name: "get",
|
|
@@ -5601,7 +5649,7 @@ var configGetCommand = defineCommand49({
|
|
|
5601
5649
|
if (idp)
|
|
5602
5650
|
console.log(idp);
|
|
5603
5651
|
else
|
|
5604
|
-
|
|
5652
|
+
consola42.info("No IdP configured.");
|
|
5605
5653
|
break;
|
|
5606
5654
|
}
|
|
5607
5655
|
case "email": {
|
|
@@ -5609,7 +5657,7 @@ var configGetCommand = defineCommand49({
|
|
|
5609
5657
|
if (auth?.email)
|
|
5610
5658
|
console.log(auth.email);
|
|
5611
5659
|
else
|
|
5612
|
-
|
|
5660
|
+
consola42.info("Not logged in.");
|
|
5613
5661
|
break;
|
|
5614
5662
|
}
|
|
5615
5663
|
default: {
|
|
@@ -5622,7 +5670,7 @@ var configGetCommand = defineCommand49({
|
|
|
5622
5670
|
if (sectionObj && field in sectionObj) {
|
|
5623
5671
|
console.log(sectionObj[field]);
|
|
5624
5672
|
} else {
|
|
5625
|
-
|
|
5673
|
+
consola42.info(`Key "${key}" not set.`);
|
|
5626
5674
|
}
|
|
5627
5675
|
} else {
|
|
5628
5676
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5634,7 +5682,7 @@ var configGetCommand = defineCommand49({
|
|
|
5634
5682
|
|
|
5635
5683
|
// src/commands/config/set.ts
|
|
5636
5684
|
import { defineCommand as defineCommand50 } from "citty";
|
|
5637
|
-
import
|
|
5685
|
+
import consola43 from "consola";
|
|
5638
5686
|
var configSetCommand = defineCommand50({
|
|
5639
5687
|
meta: {
|
|
5640
5688
|
name: "set",
|
|
@@ -5671,7 +5719,7 @@ var configSetCommand = defineCommand50({
|
|
|
5671
5719
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5672
5720
|
}
|
|
5673
5721
|
saveConfig(config);
|
|
5674
|
-
|
|
5722
|
+
consola43.success(`Set ${key} = ${value}`);
|
|
5675
5723
|
}
|
|
5676
5724
|
});
|
|
5677
5725
|
|
|
@@ -5807,42 +5855,42 @@ var mcpCommand = defineCommand52({
|
|
|
5807
5855
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5808
5856
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5809
5857
|
}
|
|
5810
|
-
const { startMcpServer } = await import("./server-
|
|
5858
|
+
const { startMcpServer } = await import("./server-MDXOGP2U.js");
|
|
5811
5859
|
await startMcpServer(transport, port);
|
|
5812
5860
|
}
|
|
5813
5861
|
});
|
|
5814
5862
|
|
|
5815
5863
|
// src/commands/init/index.ts
|
|
5816
|
-
import { existsSync as
|
|
5864
|
+
import { existsSync as existsSync19, copyFileSync, writeFileSync as writeFileSync12 } from "fs";
|
|
5817
5865
|
import { randomBytes } from "crypto";
|
|
5818
|
-
import { execFileSync as
|
|
5819
|
-
import { join as
|
|
5866
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
5867
|
+
import { join as join16 } from "path";
|
|
5820
5868
|
import { defineCommand as defineCommand53 } from "citty";
|
|
5821
|
-
import
|
|
5869
|
+
import consola44 from "consola";
|
|
5822
5870
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5823
5871
|
async function downloadTemplate(repo, targetDir) {
|
|
5824
5872
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
5825
5873
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
5826
5874
|
}
|
|
5827
5875
|
function installDeps(dir) {
|
|
5828
|
-
const hasLockFile = (name) =>
|
|
5876
|
+
const hasLockFile = (name) => existsSync19(join16(dir, name));
|
|
5829
5877
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5830
|
-
|
|
5878
|
+
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5831
5879
|
} else if (hasLockFile("bun.lockb")) {
|
|
5832
|
-
|
|
5880
|
+
execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5833
5881
|
} else {
|
|
5834
|
-
|
|
5882
|
+
execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5835
5883
|
}
|
|
5836
5884
|
}
|
|
5837
5885
|
async function promptChoice(message, choices) {
|
|
5838
|
-
const result = await
|
|
5886
|
+
const result = await consola44.prompt(message, { type: "select", options: choices });
|
|
5839
5887
|
if (typeof result === "symbol") {
|
|
5840
5888
|
throw new CliExit(0);
|
|
5841
5889
|
}
|
|
5842
5890
|
return result;
|
|
5843
5891
|
}
|
|
5844
5892
|
async function promptText(message, defaultValue) {
|
|
5845
|
-
const result = await
|
|
5893
|
+
const result = await consola44.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5846
5894
|
if (typeof result === "symbol") {
|
|
5847
5895
|
throw new CliExit(0);
|
|
5848
5896
|
}
|
|
@@ -5890,23 +5938,23 @@ var initCommand = defineCommand53({
|
|
|
5890
5938
|
});
|
|
5891
5939
|
async function initSP(targetDir) {
|
|
5892
5940
|
const dir = targetDir || "my-app";
|
|
5893
|
-
if (
|
|
5941
|
+
if (existsSync19(join16(dir, "package.json"))) {
|
|
5894
5942
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5895
5943
|
}
|
|
5896
|
-
|
|
5944
|
+
consola44.start("Scaffolding SP starter...");
|
|
5897
5945
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5898
|
-
|
|
5899
|
-
|
|
5946
|
+
consola44.success("Scaffolded from openape-sp-starter");
|
|
5947
|
+
consola44.start("Installing dependencies...");
|
|
5900
5948
|
installDeps(dir);
|
|
5901
|
-
|
|
5902
|
-
const envExample =
|
|
5903
|
-
const envFile =
|
|
5904
|
-
if (
|
|
5949
|
+
consola44.success("Dependencies installed");
|
|
5950
|
+
const envExample = join16(dir, ".env.example");
|
|
5951
|
+
const envFile = join16(dir, ".env");
|
|
5952
|
+
if (existsSync19(envExample) && !existsSync19(envFile)) {
|
|
5905
5953
|
copyFileSync(envExample, envFile);
|
|
5906
|
-
|
|
5954
|
+
consola44.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5907
5955
|
}
|
|
5908
5956
|
console.log("");
|
|
5909
|
-
|
|
5957
|
+
consola44.box([
|
|
5910
5958
|
`cd ${dir}`,
|
|
5911
5959
|
"npm run dev",
|
|
5912
5960
|
"",
|
|
@@ -5915,7 +5963,7 @@ async function initSP(targetDir) {
|
|
|
5915
5963
|
}
|
|
5916
5964
|
async function initIdP(targetDir) {
|
|
5917
5965
|
const dir = targetDir || "my-idp";
|
|
5918
|
-
if (
|
|
5966
|
+
if (existsSync19(join16(dir, "package.json"))) {
|
|
5919
5967
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5920
5968
|
}
|
|
5921
5969
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -5925,15 +5973,15 @@ async function initIdP(targetDir) {
|
|
|
5925
5973
|
"s3 (S3-compatible)"
|
|
5926
5974
|
]);
|
|
5927
5975
|
const adminEmail = await promptText("Admin email");
|
|
5928
|
-
|
|
5976
|
+
consola44.start("Scaffolding IdP starter...");
|
|
5929
5977
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5930
|
-
|
|
5931
|
-
|
|
5978
|
+
consola44.success("Scaffolded from openape-idp-starter");
|
|
5979
|
+
consola44.start("Installing dependencies...");
|
|
5932
5980
|
installDeps(dir);
|
|
5933
|
-
|
|
5981
|
+
consola44.success("Dependencies installed");
|
|
5934
5982
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5935
5983
|
const managementToken = randomBytes(32).toString("hex");
|
|
5936
|
-
|
|
5984
|
+
consola44.success("Secrets generated");
|
|
5937
5985
|
const isLocalhost = domain === "localhost";
|
|
5938
5986
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5939
5987
|
const envContent = [
|
|
@@ -5947,11 +5995,11 @@ async function initIdP(targetDir) {
|
|
|
5947
5995
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5948
5996
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5949
5997
|
].join("\n");
|
|
5950
|
-
|
|
5998
|
+
writeFileSync12(join16(dir, ".env"), `${envContent}
|
|
5951
5999
|
`, { mode: 384 });
|
|
5952
|
-
|
|
6000
|
+
consola44.success(".env created");
|
|
5953
6001
|
console.log("");
|
|
5954
|
-
|
|
6002
|
+
consola44.box([
|
|
5955
6003
|
`cd ${dir}`,
|
|
5956
6004
|
"npm run dev",
|
|
5957
6005
|
"",
|
|
@@ -5967,12 +6015,12 @@ async function initIdP(targetDir) {
|
|
|
5967
6015
|
}
|
|
5968
6016
|
|
|
5969
6017
|
// src/commands/enroll.ts
|
|
5970
|
-
import { Buffer as
|
|
5971
|
-
import { existsSync as
|
|
6018
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
6019
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
|
|
5972
6020
|
import { execFile as execFile2 } from "child_process";
|
|
5973
6021
|
import { sign as sign2 } from "crypto";
|
|
5974
6022
|
import { defineCommand as defineCommand54 } from "citty";
|
|
5975
|
-
import
|
|
6023
|
+
import consola45 from "consola";
|
|
5976
6024
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5977
6025
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5978
6026
|
var POLL_INTERVAL = 3e3;
|
|
@@ -5984,7 +6032,7 @@ function openBrowser2(url) {
|
|
|
5984
6032
|
}
|
|
5985
6033
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
5986
6034
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5987
|
-
const keyContent =
|
|
6035
|
+
const keyContent = readFileSync16(resolvedKey, "utf-8");
|
|
5988
6036
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
5989
6037
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
5990
6038
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -5998,7 +6046,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
5998
6046
|
});
|
|
5999
6047
|
if (challengeResp.ok) {
|
|
6000
6048
|
const { challenge } = await challengeResp.json();
|
|
6001
|
-
const signature = sign2(null,
|
|
6049
|
+
const signature = sign2(null, Buffer4.from(challenge), privateKey).toString("base64");
|
|
6002
6050
|
const authResp = await fetch(authenticateUrl, {
|
|
6003
6051
|
method: "POST",
|
|
6004
6052
|
headers: { "Content-Type": "application/json" },
|
|
@@ -6011,7 +6059,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
6011
6059
|
}
|
|
6012
6060
|
} catch {
|
|
6013
6061
|
}
|
|
6014
|
-
await new Promise((
|
|
6062
|
+
await new Promise((resolve5) => setTimeout(resolve5, POLL_INTERVAL));
|
|
6015
6063
|
}
|
|
6016
6064
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
6017
6065
|
}
|
|
@@ -6035,38 +6083,38 @@ var enrollCommand = defineCommand54({
|
|
|
6035
6083
|
}
|
|
6036
6084
|
},
|
|
6037
6085
|
async run({ args }) {
|
|
6038
|
-
const idp = args.idp || await
|
|
6086
|
+
const idp = args.idp || await consola45.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
6039
6087
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6040
6088
|
return r;
|
|
6041
6089
|
}) || DEFAULT_IDP_URL2;
|
|
6042
|
-
const agentName = args.name || await
|
|
6090
|
+
const agentName = args.name || await consola45.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
6043
6091
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6044
6092
|
return r;
|
|
6045
6093
|
});
|
|
6046
6094
|
if (!agentName) {
|
|
6047
6095
|
throw new CliError("Agent name is required.");
|
|
6048
6096
|
}
|
|
6049
|
-
const keyPath = args.key || await
|
|
6097
|
+
const keyPath = args.key || await consola45.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
6050
6098
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
6051
6099
|
return r;
|
|
6052
6100
|
}) || DEFAULT_KEY_PATH;
|
|
6053
6101
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
6054
6102
|
let publicKey;
|
|
6055
|
-
if (
|
|
6103
|
+
if (existsSync20(resolvedKey)) {
|
|
6056
6104
|
publicKey = readPublicKey(resolvedKey);
|
|
6057
|
-
|
|
6105
|
+
consola45.success(`Using existing key ${keyPath}`);
|
|
6058
6106
|
} else {
|
|
6059
|
-
|
|
6107
|
+
consola45.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
6060
6108
|
publicKey = generateAndSaveKey(keyPath);
|
|
6061
|
-
|
|
6109
|
+
consola45.success(`Key pair generated at ${keyPath}`);
|
|
6062
6110
|
}
|
|
6063
6111
|
const encodedKey = encodeURIComponent(publicKey);
|
|
6064
6112
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
6065
|
-
|
|
6066
|
-
|
|
6113
|
+
consola45.info("Opening browser for enrollment...");
|
|
6114
|
+
consola45.info(`\u2192 ${idp}/enroll`);
|
|
6067
6115
|
openBrowser2(enrollUrl);
|
|
6068
6116
|
console.log("");
|
|
6069
|
-
const agentEmail = await
|
|
6117
|
+
const agentEmail = await consola45.prompt(
|
|
6070
6118
|
"Agent email (shown in browser after enrollment)",
|
|
6071
6119
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
6072
6120
|
).then((r) => {
|
|
@@ -6076,7 +6124,7 @@ var enrollCommand = defineCommand54({
|
|
|
6076
6124
|
if (!agentEmail) {
|
|
6077
6125
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
6078
6126
|
}
|
|
6079
|
-
|
|
6127
|
+
consola45.start("Verifying enrollment...");
|
|
6080
6128
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
6081
6129
|
saveAuth({
|
|
6082
6130
|
idp,
|
|
@@ -6088,17 +6136,17 @@ var enrollCommand = defineCommand54({
|
|
|
6088
6136
|
config.defaults = { ...config.defaults, idp };
|
|
6089
6137
|
config.agent = { key: keyPath, email: agentEmail };
|
|
6090
6138
|
saveConfig(config);
|
|
6091
|
-
|
|
6092
|
-
|
|
6139
|
+
consola45.success(`Agent enrolled as ${agentEmail}`);
|
|
6140
|
+
consola45.success("Config saved to ~/.config/apes/");
|
|
6093
6141
|
console.log("");
|
|
6094
|
-
|
|
6142
|
+
consola45.info("Verify with: apes whoami");
|
|
6095
6143
|
}
|
|
6096
6144
|
});
|
|
6097
6145
|
|
|
6098
6146
|
// src/commands/register-user.ts
|
|
6099
|
-
import { existsSync as
|
|
6147
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17 } from "fs";
|
|
6100
6148
|
import { defineCommand as defineCommand55 } from "citty";
|
|
6101
|
-
import
|
|
6149
|
+
import consola46 from "consola";
|
|
6102
6150
|
var registerUserCommand = defineCommand55({
|
|
6103
6151
|
meta: {
|
|
6104
6152
|
name: "register-user",
|
|
@@ -6135,8 +6183,8 @@ var registerUserCommand = defineCommand55({
|
|
|
6135
6183
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
6136
6184
|
}
|
|
6137
6185
|
let publicKey = args.key;
|
|
6138
|
-
if (
|
|
6139
|
-
publicKey =
|
|
6186
|
+
if (existsSync21(args.key)) {
|
|
6187
|
+
publicKey = readFileSync17(args.key, "utf-8").trim();
|
|
6140
6188
|
}
|
|
6141
6189
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
6142
6190
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -6154,7 +6202,7 @@ var registerUserCommand = defineCommand55({
|
|
|
6154
6202
|
...userType ? { type: userType } : {}
|
|
6155
6203
|
}
|
|
6156
6204
|
});
|
|
6157
|
-
|
|
6205
|
+
consola46.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
6158
6206
|
}
|
|
6159
6207
|
});
|
|
6160
6208
|
|
|
@@ -6163,7 +6211,7 @@ import { defineCommand as defineCommand57 } from "citty";
|
|
|
6163
6211
|
|
|
6164
6212
|
// src/commands/utils/dig.ts
|
|
6165
6213
|
import { defineCommand as defineCommand56 } from "citty";
|
|
6166
|
-
import
|
|
6214
|
+
import consola47 from "consola";
|
|
6167
6215
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
6168
6216
|
var digCommand = defineCommand56({
|
|
6169
6217
|
meta: {
|
|
@@ -6238,12 +6286,12 @@ var digCommand = defineCommand56({
|
|
|
6238
6286
|
console.log(` domain: ${domain}`);
|
|
6239
6287
|
console.log("");
|
|
6240
6288
|
if (!result.ddisa.found) {
|
|
6241
|
-
|
|
6289
|
+
consola47.warn(`No DDISA record at _ddisa.${domain}`);
|
|
6242
6290
|
if (result.hint) console.log(`
|
|
6243
6291
|
${result.hint}`);
|
|
6244
6292
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6245
6293
|
}
|
|
6246
|
-
|
|
6294
|
+
consola47.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
6247
6295
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
6248
6296
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
6249
6297
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -6253,13 +6301,13 @@ ${result.hint}`);
|
|
|
6253
6301
|
return;
|
|
6254
6302
|
}
|
|
6255
6303
|
if (result.idpDiscovery.ok) {
|
|
6256
|
-
|
|
6304
|
+
consola47.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
6257
6305
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
6258
6306
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
6259
6307
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
6260
6308
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
6261
6309
|
} else {
|
|
6262
|
-
|
|
6310
|
+
consola47.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
6263
6311
|
if (result.hint) console.log(`
|
|
6264
6312
|
${result.hint}`);
|
|
6265
6313
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -6283,7 +6331,7 @@ import { defineCommand as defineCommand60 } from "citty";
|
|
|
6283
6331
|
|
|
6284
6332
|
// src/commands/sessions/list.ts
|
|
6285
6333
|
import { defineCommand as defineCommand58 } from "citty";
|
|
6286
|
-
import
|
|
6334
|
+
import consola48 from "consola";
|
|
6287
6335
|
var sessionsListCommand = defineCommand58({
|
|
6288
6336
|
meta: {
|
|
6289
6337
|
name: "list",
|
|
@@ -6302,7 +6350,7 @@ var sessionsListCommand = defineCommand58({
|
|
|
6302
6350
|
return;
|
|
6303
6351
|
}
|
|
6304
6352
|
if (result.data.length === 0) {
|
|
6305
|
-
|
|
6353
|
+
consola48.info("No active sessions.");
|
|
6306
6354
|
return;
|
|
6307
6355
|
}
|
|
6308
6356
|
for (const f of result.data) {
|
|
@@ -6315,7 +6363,7 @@ var sessionsListCommand = defineCommand58({
|
|
|
6315
6363
|
|
|
6316
6364
|
// src/commands/sessions/remove.ts
|
|
6317
6365
|
import { defineCommand as defineCommand59 } from "citty";
|
|
6318
|
-
import
|
|
6366
|
+
import consola49 from "consola";
|
|
6319
6367
|
var sessionsRemoveCommand = defineCommand59({
|
|
6320
6368
|
meta: {
|
|
6321
6369
|
name: "remove",
|
|
@@ -6332,7 +6380,7 @@ var sessionsRemoveCommand = defineCommand59({
|
|
|
6332
6380
|
const id = String(args.familyId).trim();
|
|
6333
6381
|
if (!id) throw new CliError("familyId required");
|
|
6334
6382
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6335
|
-
|
|
6383
|
+
consola49.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6336
6384
|
}
|
|
6337
6385
|
});
|
|
6338
6386
|
|
|
@@ -6350,7 +6398,7 @@ var sessionsCommand = defineCommand60({
|
|
|
6350
6398
|
|
|
6351
6399
|
// src/commands/dns-check.ts
|
|
6352
6400
|
import { defineCommand as defineCommand61 } from "citty";
|
|
6353
|
-
import
|
|
6401
|
+
import consola50 from "consola";
|
|
6354
6402
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6355
6403
|
var dnsCheckCommand = defineCommand61({
|
|
6356
6404
|
meta: {
|
|
@@ -6366,7 +6414,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6366
6414
|
},
|
|
6367
6415
|
async run({ args }) {
|
|
6368
6416
|
const domain = args.domain;
|
|
6369
|
-
|
|
6417
|
+
consola50.start(`Checking _ddisa.${domain}...`);
|
|
6370
6418
|
try {
|
|
6371
6419
|
const result = await resolveDDISA3(domain);
|
|
6372
6420
|
if (!result) {
|
|
@@ -6375,7 +6423,7 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6375
6423
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6376
6424
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6377
6425
|
}
|
|
6378
|
-
|
|
6426
|
+
consola50.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6379
6427
|
console.log("");
|
|
6380
6428
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6381
6429
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6384,14 +6432,14 @@ var dnsCheckCommand = defineCommand61({
|
|
|
6384
6432
|
if (result.priority !== void 0)
|
|
6385
6433
|
console.log(` Priority: ${result.priority}`);
|
|
6386
6434
|
console.log("");
|
|
6387
|
-
|
|
6435
|
+
consola50.start(`Verifying IdP at ${result.idp}...`);
|
|
6388
6436
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6389
6437
|
if (!discoResp.ok) {
|
|
6390
|
-
|
|
6438
|
+
consola50.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6391
6439
|
return;
|
|
6392
6440
|
}
|
|
6393
6441
|
const disco = await discoResp.json();
|
|
6394
|
-
|
|
6442
|
+
consola50.success(`IdP is reachable`);
|
|
6395
6443
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6396
6444
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6397
6445
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6445,7 +6493,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6445
6493
|
}
|
|
6446
6494
|
}
|
|
6447
6495
|
async function runHealth(args) {
|
|
6448
|
-
const version = true ? "1.
|
|
6496
|
+
const version = true ? "1.31.0" : "0.0.0";
|
|
6449
6497
|
const auth = loadAuth();
|
|
6450
6498
|
if (!auth) {
|
|
6451
6499
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6527,7 +6575,7 @@ var healthCommand = defineCommand62({
|
|
|
6527
6575
|
|
|
6528
6576
|
// src/commands/workflows.ts
|
|
6529
6577
|
import { defineCommand as defineCommand63 } from "citty";
|
|
6530
|
-
import
|
|
6578
|
+
import consola51 from "consola";
|
|
6531
6579
|
|
|
6532
6580
|
// src/guides/index.ts
|
|
6533
6581
|
var guides = [
|
|
@@ -6598,7 +6646,7 @@ var workflowsCommand = defineCommand63({
|
|
|
6598
6646
|
if (args.id) {
|
|
6599
6647
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6600
6648
|
if (!guide) {
|
|
6601
|
-
|
|
6649
|
+
consola51.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6602
6650
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6603
6651
|
}
|
|
6604
6652
|
if (args.json) {
|
|
@@ -6638,26 +6686,26 @@ var workflowsCommand = defineCommand63({
|
|
|
6638
6686
|
});
|
|
6639
6687
|
|
|
6640
6688
|
// src/version-check.ts
|
|
6641
|
-
import { existsSync as
|
|
6689
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync7, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
|
|
6642
6690
|
import { homedir as homedir14 } from "os";
|
|
6643
|
-
import { join as
|
|
6644
|
-
import
|
|
6691
|
+
import { join as join17 } from "path";
|
|
6692
|
+
import consola52 from "consola";
|
|
6645
6693
|
var PACKAGE_NAME = "@openape/apes";
|
|
6646
6694
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6647
|
-
var CACHE_FILE =
|
|
6695
|
+
var CACHE_FILE = join17(homedir14(), ".config", "apes", ".version-check.json");
|
|
6648
6696
|
function readCache() {
|
|
6649
|
-
if (!
|
|
6697
|
+
if (!existsSync22(CACHE_FILE)) return null;
|
|
6650
6698
|
try {
|
|
6651
|
-
return JSON.parse(
|
|
6699
|
+
return JSON.parse(readFileSync18(CACHE_FILE, "utf-8"));
|
|
6652
6700
|
} catch {
|
|
6653
6701
|
return null;
|
|
6654
6702
|
}
|
|
6655
6703
|
}
|
|
6656
6704
|
function writeCache(entry) {
|
|
6657
6705
|
try {
|
|
6658
|
-
const dir =
|
|
6659
|
-
if (!
|
|
6660
|
-
|
|
6706
|
+
const dir = join17(homedir14(), ".config", "apes");
|
|
6707
|
+
if (!existsSync22(dir)) mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
6708
|
+
writeFileSync13(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6661
6709
|
} catch {
|
|
6662
6710
|
}
|
|
6663
6711
|
}
|
|
@@ -6686,7 +6734,7 @@ async function fetchLatestVersion() {
|
|
|
6686
6734
|
}
|
|
6687
6735
|
function warnIfBehind(currentVersion, latest) {
|
|
6688
6736
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6689
|
-
|
|
6737
|
+
consola52.warn(
|
|
6690
6738
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6691
6739
|
);
|
|
6692
6740
|
}
|
|
@@ -6718,10 +6766,10 @@ if (shellRewrite) {
|
|
|
6718
6766
|
if (shellRewrite.action === "rewrite") {
|
|
6719
6767
|
process.argv = shellRewrite.argv;
|
|
6720
6768
|
} else if (shellRewrite.action === "version") {
|
|
6721
|
-
console.log(`ape-shell ${"1.
|
|
6769
|
+
console.log(`ape-shell ${"1.31.0"} (OpenApe DDISA shell wrapper)`);
|
|
6722
6770
|
process.exit(0);
|
|
6723
6771
|
} else if (shellRewrite.action === "help") {
|
|
6724
|
-
console.log(`ape-shell ${"1.
|
|
6772
|
+
console.log(`ape-shell ${"1.31.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6725
6773
|
console.log("");
|
|
6726
6774
|
console.log("Usage:");
|
|
6727
6775
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6736,7 +6784,7 @@ if (shellRewrite) {
|
|
|
6736
6784
|
console.log(" --help, -h Show this help message");
|
|
6737
6785
|
process.exit(0);
|
|
6738
6786
|
} else if (shellRewrite.action === "interactive") {
|
|
6739
|
-
const { runInteractiveShell } = await import("./orchestrator-
|
|
6787
|
+
const { runInteractiveShell } = await import("./orchestrator-6PZXCE54.js");
|
|
6740
6788
|
await runInteractiveShell();
|
|
6741
6789
|
process.exit(0);
|
|
6742
6790
|
} else {
|
|
@@ -6779,7 +6827,7 @@ var configCommand = defineCommand64({
|
|
|
6779
6827
|
var main = defineCommand64({
|
|
6780
6828
|
meta: {
|
|
6781
6829
|
name: "apes",
|
|
6782
|
-
version: "1.
|
|
6830
|
+
version: "1.31.0",
|
|
6783
6831
|
description: "Unified CLI for OpenApe"
|
|
6784
6832
|
},
|
|
6785
6833
|
subCommands: {
|
|
@@ -6831,26 +6879,26 @@ async function maybeRefreshAuth() {
|
|
|
6831
6879
|
const { loadAuth: loadAuth2 } = await import("./config-MOB5DJ6H.js");
|
|
6832
6880
|
if (!loadAuth2()) return;
|
|
6833
6881
|
try {
|
|
6834
|
-
const { ensureFreshToken } = await import("./http-
|
|
6882
|
+
const { ensureFreshToken } = await import("./http-SILH37L7.js");
|
|
6835
6883
|
await ensureFreshToken();
|
|
6836
6884
|
} catch {
|
|
6837
6885
|
}
|
|
6838
6886
|
}
|
|
6839
6887
|
await maybeRefreshAuth();
|
|
6840
|
-
await maybeWarnStaleVersion("1.
|
|
6888
|
+
await maybeWarnStaleVersion("1.31.0").catch(() => {
|
|
6841
6889
|
});
|
|
6842
6890
|
runMain(main).catch((err) => {
|
|
6843
6891
|
if (err instanceof CliExit) {
|
|
6844
6892
|
process.exit(err.exitCode);
|
|
6845
6893
|
}
|
|
6846
6894
|
if (err instanceof CliError) {
|
|
6847
|
-
|
|
6895
|
+
consola53.error(err.message);
|
|
6848
6896
|
process.exit(err.exitCode);
|
|
6849
6897
|
}
|
|
6850
6898
|
if (debug) {
|
|
6851
|
-
|
|
6899
|
+
consola53.error(err);
|
|
6852
6900
|
} else {
|
|
6853
|
-
|
|
6901
|
+
consola53.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6854
6902
|
}
|
|
6855
6903
|
process.exit(1);
|
|
6856
6904
|
});
|