@openape/apes 0.22.1 → 0.24.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/cli.js
CHANGED
|
@@ -379,7 +379,7 @@ async function loginWithPKCE(idp) {
|
|
|
379
379
|
consola2.success(`Logged in as ${payload.email || payload.sub}`);
|
|
380
380
|
}
|
|
381
381
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
382
|
-
const { readFileSync:
|
|
382
|
+
const { readFileSync: readFileSync9 } = await import("fs");
|
|
383
383
|
const { sign: sign3 } = await import("crypto");
|
|
384
384
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
|
|
385
385
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
@@ -392,7 +392,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
392
392
|
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
393
393
|
}
|
|
394
394
|
const { challenge } = await challengeResp.json();
|
|
395
|
-
const keyContent =
|
|
395
|
+
const keyContent = readFileSync9(keyPath, "utf-8");
|
|
396
396
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
397
397
|
const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
|
|
398
398
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -1873,7 +1873,7 @@ mkdir -p "$HOME_DIR/.ssh" "$HOME_DIR/.config/apes"
|
|
|
1873
1873
|
cat > "$HOME_DIR/.ssh/id_ed25519" ${shHeredoc(privatePemForHeredoc.trimEnd())}
|
|
1874
1874
|
cat > "$HOME_DIR/.ssh/id_ed25519.pub" ${shHeredoc(`${input.publicKeySshLine}`)}
|
|
1875
1875
|
cat > "$HOME_DIR/.config/apes/auth.json" ${shHeredoc(input.authJson)}
|
|
1876
|
-
${claudeBlock}${claudeTokenBlock}
|
|
1876
|
+
${claudeBlock}${claudeTokenBlock}${buildBridgeBlock(input.bridge)}
|
|
1877
1877
|
chown -R "$NAME:staff" "$HOME_DIR"
|
|
1878
1878
|
chmod 700 "$HOME_DIR/.ssh"
|
|
1879
1879
|
chmod 700 "$HOME_DIR/.config"
|
|
@@ -1886,6 +1886,31 @@ if [ -f "$HOME_DIR/.config/openape/claude-token.env" ]; then
|
|
|
1886
1886
|
fi
|
|
1887
1887
|
|
|
1888
1888
|
echo "OK $NAME uid=$NEXT_UID home=$HOME_DIR"
|
|
1889
|
+
${buildBridgeBootstrapBlock(input.bridge)}`;
|
|
1890
|
+
}
|
|
1891
|
+
function buildBridgeBlock(bridge) {
|
|
1892
|
+
if (!bridge) return "";
|
|
1893
|
+
return `
|
|
1894
|
+
mkdir -p "$HOME_DIR/Library/Application Support/openape/bridge" "$HOME_DIR/Library/Logs" "$HOME_DIR/.pi/agent"
|
|
1895
|
+
cat > "$HOME_DIR/.pi/agent/.env" ${shHeredoc(bridge.envFile)}
|
|
1896
|
+
cat > "$HOME_DIR/Library/Application Support/openape/bridge/start.sh" ${shHeredoc(bridge.startScript)}
|
|
1897
|
+
chmod 755 "$HOME_DIR/Library/Application Support/openape/bridge/start.sh"
|
|
1898
|
+
chmod 600 "$HOME_DIR/.pi/agent/.env"
|
|
1899
|
+
|
|
1900
|
+
# System-wide LaunchDaemon \u2014 root-owned, mode 644 (launchd refuses
|
|
1901
|
+
# group/world-writable plists). UserName in the plist makes launchd run
|
|
1902
|
+
# the binary as the agent, not root.
|
|
1903
|
+
cat > ${shQuote(bridge.plistPath)} ${shHeredoc(bridge.plistContent)}
|
|
1904
|
+
chown root:wheel ${shQuote(bridge.plistPath)}
|
|
1905
|
+
chmod 644 ${shQuote(bridge.plistPath)}
|
|
1906
|
+
`;
|
|
1907
|
+
}
|
|
1908
|
+
function buildBridgeBootstrapBlock(bridge) {
|
|
1909
|
+
if (!bridge) return "";
|
|
1910
|
+
return `
|
|
1911
|
+
launchctl bootout "system/${bridge.plistLabel}" 2>/dev/null || true
|
|
1912
|
+
launchctl bootstrap system ${shQuote(bridge.plistPath)} || \\
|
|
1913
|
+
echo "warn: bridge bootstrap into system domain failed; check ${bridge.plistPath}"
|
|
1889
1914
|
`;
|
|
1890
1915
|
}
|
|
1891
1916
|
function buildDestroyTeardownScript(input) {
|
|
@@ -1916,6 +1941,16 @@ if [ -n "$UID_OF" ]; then
|
|
|
1916
1941
|
pkill -9 -u "$UID_OF" 2>/dev/null || true
|
|
1917
1942
|
fi
|
|
1918
1943
|
|
|
1944
|
+
# Per-agent system LaunchDaemon written by spawn --bridge. Bootout +
|
|
1945
|
+
# delete must come BEFORE we delete the user, otherwise launchd keeps a
|
|
1946
|
+
# zombie reference. No-op if the plist isn't there.
|
|
1947
|
+
BRIDGE_LABEL="eco.hofmann.apes.bridge.$NAME"
|
|
1948
|
+
BRIDGE_PLIST="/Library/LaunchDaemons/$BRIDGE_LABEL.plist"
|
|
1949
|
+
if [ -f "$BRIDGE_PLIST" ]; then
|
|
1950
|
+
launchctl bootout "system/$BRIDGE_LABEL" 2>/dev/null || true
|
|
1951
|
+
rm -f "$BRIDGE_PLIST"
|
|
1952
|
+
fi
|
|
1953
|
+
|
|
1919
1954
|
if [ -d "$HOME_DIR" ] && [ "$HOME_DIR" != "/" ] && [ "$HOME_DIR" != "" ]; then
|
|
1920
1955
|
rm -rf "$HOME_DIR"
|
|
1921
1956
|
fi
|
|
@@ -2329,10 +2364,61 @@ var registerAgentCommand = defineCommand22({
|
|
|
2329
2364
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
2330
2365
|
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2331
2366
|
import { tmpdir as tmpdir2 } from "os";
|
|
2332
|
-
import { join as
|
|
2367
|
+
import { join as join4 } from "path";
|
|
2333
2368
|
import { defineCommand as defineCommand23 } from "citty";
|
|
2334
2369
|
import consola21 from "consola";
|
|
2335
2370
|
|
|
2371
|
+
// src/lib/chat-room.ts
|
|
2372
|
+
var DEFAULT_CHAT_ENDPOINT = "https://chat.openape.ai";
|
|
2373
|
+
function chatEndpoint() {
|
|
2374
|
+
return (process.env.APE_CHAT_ENDPOINT ?? DEFAULT_CHAT_ENDPOINT).replace(/\/$/, "");
|
|
2375
|
+
}
|
|
2376
|
+
async function chatFetch(bearer, path2, init) {
|
|
2377
|
+
const url = `${chatEndpoint()}${path2}`;
|
|
2378
|
+
const res = await fetch(url, {
|
|
2379
|
+
method: init?.method ?? "GET",
|
|
2380
|
+
headers: {
|
|
2381
|
+
Authorization: `Bearer ${bearer}`,
|
|
2382
|
+
"Content-Type": "application/json"
|
|
2383
|
+
},
|
|
2384
|
+
body: init?.body !== void 0 ? JSON.stringify(init.body) : void 0
|
|
2385
|
+
});
|
|
2386
|
+
if (!res.ok) {
|
|
2387
|
+
const detail = await res.text().catch(() => "");
|
|
2388
|
+
throw new Error(`chat.openape.ai ${init?.method ?? "GET"} ${path2} \u2192 ${res.status}: ${detail.slice(0, 200)}`);
|
|
2389
|
+
}
|
|
2390
|
+
return await res.json();
|
|
2391
|
+
}
|
|
2392
|
+
async function findRoomByName(bearer, name) {
|
|
2393
|
+
const rooms = await chatFetch(bearer, "/api/rooms");
|
|
2394
|
+
return rooms.find((r) => r.name === name) ?? null;
|
|
2395
|
+
}
|
|
2396
|
+
async function createRoom(bearer, name) {
|
|
2397
|
+
return chatFetch(bearer, "/api/rooms", {
|
|
2398
|
+
method: "POST",
|
|
2399
|
+
body: { name, kind: "channel", members: [] }
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
async function addMember(bearer, roomId, email, role = "member") {
|
|
2403
|
+
await chatFetch(bearer, `/api/rooms/${encodeURIComponent(roomId)}/members`, {
|
|
2404
|
+
method: "POST",
|
|
2405
|
+
body: { email, role }
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
async function ensureRoomMembership(opts) {
|
|
2409
|
+
const existing = await findRoomByName(opts.callerBearer, opts.roomName);
|
|
2410
|
+
let room;
|
|
2411
|
+
let created = false;
|
|
2412
|
+
if (existing) {
|
|
2413
|
+
room = existing;
|
|
2414
|
+
} else {
|
|
2415
|
+
room = await createRoom(opts.callerBearer, opts.roomName);
|
|
2416
|
+
created = true;
|
|
2417
|
+
}
|
|
2418
|
+
await addMember(opts.callerBearer, room.id, opts.agentEmail);
|
|
2419
|
+
return { roomId: room.id, created };
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2336
2422
|
// src/lib/keygen.ts
|
|
2337
2423
|
import { Buffer as Buffer4 } from "buffer";
|
|
2338
2424
|
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -2389,6 +2475,158 @@ function generateKeyPairInMemory() {
|
|
|
2389
2475
|
};
|
|
2390
2476
|
}
|
|
2391
2477
|
|
|
2478
|
+
// src/lib/llm-bridge.ts
|
|
2479
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
2480
|
+
import { homedir as homedir5 } from "os";
|
|
2481
|
+
import { join as join3 } from "path";
|
|
2482
|
+
var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
|
|
2483
|
+
function readLitellmEnv(envPath = join3(homedir5(), "litellm", ".env")) {
|
|
2484
|
+
if (!existsSync6(envPath)) return null;
|
|
2485
|
+
try {
|
|
2486
|
+
const text = readFileSync5(envPath, "utf8");
|
|
2487
|
+
const out = {};
|
|
2488
|
+
for (const line of text.split("\n")) {
|
|
2489
|
+
const trimmed = line.trim();
|
|
2490
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
2491
|
+
const eq = trimmed.indexOf("=");
|
|
2492
|
+
if (eq < 0) continue;
|
|
2493
|
+
const key = trimmed.slice(0, eq).trim();
|
|
2494
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
2495
|
+
if (key === "LITELLM_MASTER_KEY" || key === "LITELLM_API_KEY") out.apiKey = value;
|
|
2496
|
+
if (key === "LITELLM_BASE_URL") out.baseUrl = value;
|
|
2497
|
+
}
|
|
2498
|
+
return out;
|
|
2499
|
+
} catch {
|
|
2500
|
+
return null;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function resolveBridgeConfig(opts) {
|
|
2504
|
+
const env = readLitellmEnv(opts.envPath);
|
|
2505
|
+
const apiKey = opts.cliKey ?? env?.apiKey;
|
|
2506
|
+
const baseUrl = opts.cliBaseUrl ?? env?.baseUrl ?? "http://127.0.0.1:4000/v1";
|
|
2507
|
+
if (!apiKey) {
|
|
2508
|
+
throw new Error(
|
|
2509
|
+
"No LITELLM_API_KEY resolved. Pass --bridge-key sk-\u2026 or write LITELLM_MASTER_KEY into ~/litellm/.env first."
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
return { baseUrl, apiKey };
|
|
2513
|
+
}
|
|
2514
|
+
function bridgePlistLabel(agentName) {
|
|
2515
|
+
return `${PLIST_LABEL_PREFIX}.${agentName}`;
|
|
2516
|
+
}
|
|
2517
|
+
function bridgePlistPath(agentName) {
|
|
2518
|
+
return `/Library/LaunchDaemons/${bridgePlistLabel(agentName)}.plist`;
|
|
2519
|
+
}
|
|
2520
|
+
function buildBridgeEnvFile(cfg) {
|
|
2521
|
+
return `# Auto-generated by 'apes agents spawn --bridge'.
|
|
2522
|
+
# Read by the chat-bridge daemon at boot to talk to the local LLM proxy.
|
|
2523
|
+
LITELLM_BASE_URL=${cfg.baseUrl}
|
|
2524
|
+
LITELLM_API_KEY=${cfg.apiKey}
|
|
2525
|
+
`;
|
|
2526
|
+
}
|
|
2527
|
+
function buildBridgeStartScript() {
|
|
2528
|
+
return `#!/usr/bin/env bash
|
|
2529
|
+
# Auto-generated by 'apes agents spawn --bridge'.
|
|
2530
|
+
# Idempotent installer + launcher.
|
|
2531
|
+
set -euo pipefail
|
|
2532
|
+
|
|
2533
|
+
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
|
2534
|
+
export PATH="$NPM_CONFIG_PREFIX/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
|
|
2535
|
+
mkdir -p "$NPM_CONFIG_PREFIX"
|
|
2536
|
+
|
|
2537
|
+
if ! command -v openape-chat-bridge >/dev/null 2>&1; then
|
|
2538
|
+
npm install -g --silent @openape/chat-bridge
|
|
2539
|
+
fi
|
|
2540
|
+
|
|
2541
|
+
if ! command -v pi >/dev/null 2>&1; then
|
|
2542
|
+
npm install -g --silent @mariozechner/pi-coding-agent
|
|
2543
|
+
fi
|
|
2544
|
+
|
|
2545
|
+
EXT_DIR="$HOME/.pi/agent/extensions"
|
|
2546
|
+
mkdir -p "$EXT_DIR"
|
|
2547
|
+
if [ ! -f "$EXT_DIR/litellm.ts" ]; then
|
|
2548
|
+
cat > "$EXT_DIR/litellm.ts" <<'PI_EXT_EOF'
|
|
2549
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2550
|
+
|
|
2551
|
+
const BASE_URL = process.env.LITELLM_BASE_URL ?? "http://127.0.0.1:4000/v1";
|
|
2552
|
+
|
|
2553
|
+
export default async function (pi: ExtensionAPI) {
|
|
2554
|
+
pi.registerProvider("litellm", {
|
|
2555
|
+
baseUrl: BASE_URL,
|
|
2556
|
+
apiKey: "LITELLM_API_KEY",
|
|
2557
|
+
api: "openai-completions",
|
|
2558
|
+
models: [
|
|
2559
|
+
{
|
|
2560
|
+
id: "gpt-5.4",
|
|
2561
|
+
name: "ChatGPT 5.4 (Subscription)",
|
|
2562
|
+
reasoning: false,
|
|
2563
|
+
input: ["text", "image"],
|
|
2564
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
2565
|
+
contextWindow: 200000,
|
|
2566
|
+
maxTokens: 8192,
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
id: "gpt-5.3-codex",
|
|
2570
|
+
name: "ChatGPT 5.3 Codex (Subscription)",
|
|
2571
|
+
reasoning: false,
|
|
2572
|
+
input: ["text"],
|
|
2573
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
2574
|
+
contextWindow: 200000,
|
|
2575
|
+
maxTokens: 8192,
|
|
2576
|
+
},
|
|
2577
|
+
],
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
PI_EXT_EOF
|
|
2581
|
+
fi
|
|
2582
|
+
|
|
2583
|
+
set -a
|
|
2584
|
+
. "$HOME/.pi/agent/.env"
|
|
2585
|
+
set +a
|
|
2586
|
+
exec openape-chat-bridge
|
|
2587
|
+
`;
|
|
2588
|
+
}
|
|
2589
|
+
function buildBridgePlist(agentName, homeDir) {
|
|
2590
|
+
const startScript = `${homeDir}/Library/Application Support/openape/bridge/start.sh`;
|
|
2591
|
+
const stdoutLog = `${homeDir}/Library/Logs/openape-chat-bridge.log`;
|
|
2592
|
+
const stderrLog = `${homeDir}/Library/Logs/openape-chat-bridge.err.log`;
|
|
2593
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2594
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
2595
|
+
<plist version="1.0">
|
|
2596
|
+
<dict>
|
|
2597
|
+
<key>Label</key>
|
|
2598
|
+
<string>${bridgePlistLabel(agentName)}</string>
|
|
2599
|
+
<key>UserName</key>
|
|
2600
|
+
<string>${agentName}</string>
|
|
2601
|
+
<key>ProgramArguments</key>
|
|
2602
|
+
<array>
|
|
2603
|
+
<string>/bin/bash</string>
|
|
2604
|
+
<string>${startScript}</string>
|
|
2605
|
+
</array>
|
|
2606
|
+
<key>WorkingDirectory</key>
|
|
2607
|
+
<string>${homeDir}</string>
|
|
2608
|
+
<key>RunAtLoad</key>
|
|
2609
|
+
<true/>
|
|
2610
|
+
<key>KeepAlive</key>
|
|
2611
|
+
<true/>
|
|
2612
|
+
<key>ThrottleInterval</key>
|
|
2613
|
+
<integer>10</integer>
|
|
2614
|
+
<key>StandardOutPath</key>
|
|
2615
|
+
<string>${stdoutLog}</string>
|
|
2616
|
+
<key>StandardErrorPath</key>
|
|
2617
|
+
<string>${stderrLog}</string>
|
|
2618
|
+
<key>EnvironmentVariables</key>
|
|
2619
|
+
<dict>
|
|
2620
|
+
<key>HOME</key>
|
|
2621
|
+
<string>${homeDir}</string>
|
|
2622
|
+
<key>PATH</key>
|
|
2623
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
2624
|
+
</dict>
|
|
2625
|
+
</dict>
|
|
2626
|
+
</plist>
|
|
2627
|
+
`;
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2392
2630
|
// src/commands/agents/spawn.ts
|
|
2393
2631
|
var spawnAgentCommand = defineCommand23({
|
|
2394
2632
|
meta: {
|
|
@@ -2416,6 +2654,22 @@ var spawnAgentCommand = defineCommand23({
|
|
|
2416
2654
|
"claude-token-stdin": {
|
|
2417
2655
|
type: "boolean",
|
|
2418
2656
|
description: "Read the Claude Code OAuth token from stdin (paranoid form of --claude-token)."
|
|
2657
|
+
},
|
|
2658
|
+
"bridge": {
|
|
2659
|
+
type: "boolean",
|
|
2660
|
+
description: "Install the openape-chat-bridge daemon for this agent: drops a launchd plist that runs `@openape/chat-bridge` so the agent answers chat.openape.ai messages. Reads LITELLM_API_KEY/BASE_URL defaults from ~/litellm/.env; override via --bridge-key / --bridge-base-url."
|
|
2661
|
+
},
|
|
2662
|
+
"bridge-key": {
|
|
2663
|
+
type: "string",
|
|
2664
|
+
description: "Override LITELLM_API_KEY for the bridge (default: read from ~/litellm/.env)."
|
|
2665
|
+
},
|
|
2666
|
+
"bridge-base-url": {
|
|
2667
|
+
type: "string",
|
|
2668
|
+
description: "Override LITELLM_BASE_URL for the bridge (default: read from ~/litellm/.env or http://127.0.0.1:4000/v1)."
|
|
2669
|
+
},
|
|
2670
|
+
"bridge-room": {
|
|
2671
|
+
type: "string",
|
|
2672
|
+
description: "After spawn, create (or find) a chat.openape.ai room with this name and add the new agent as a member. Uses the spawning user's IdP bearer."
|
|
2419
2673
|
}
|
|
2420
2674
|
},
|
|
2421
2675
|
async run({ args }) {
|
|
@@ -2461,8 +2715,8 @@ and try again.`
|
|
|
2461
2715
|
throw new CliError(`macOS user "${name}" already exists (uid=${existing.uid ?? "?"}). Refusing to overwrite.`);
|
|
2462
2716
|
}
|
|
2463
2717
|
const homeDir = `/Users/${name}`;
|
|
2464
|
-
const scratch = mkdtempSync2(
|
|
2465
|
-
const scriptPath =
|
|
2718
|
+
const scratch = mkdtempSync2(join4(tmpdir2(), `apes-spawn-${name}-`));
|
|
2719
|
+
const scriptPath = join4(scratch, "setup.sh");
|
|
2466
2720
|
try {
|
|
2467
2721
|
consola21.start(`Generating keypair for ${name}\u2026`);
|
|
2468
2722
|
const { privatePem, publicSshLine } = generateKeyPairInMemory();
|
|
@@ -2486,6 +2740,19 @@ and try again.`
|
|
|
2486
2740
|
flag: typeof args["claude-token"] === "string" ? args["claude-token"] : void 0,
|
|
2487
2741
|
fromStdin: !!args["claude-token-stdin"]
|
|
2488
2742
|
});
|
|
2743
|
+
const bridge = args.bridge ? (() => {
|
|
2744
|
+
const cfg = resolveBridgeConfig({
|
|
2745
|
+
cliKey: typeof args["bridge-key"] === "string" ? args["bridge-key"] : void 0,
|
|
2746
|
+
cliBaseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0
|
|
2747
|
+
});
|
|
2748
|
+
return {
|
|
2749
|
+
plistLabel: bridgePlistLabel(name),
|
|
2750
|
+
plistPath: bridgePlistPath(name),
|
|
2751
|
+
plistContent: buildBridgePlist(name, homeDir),
|
|
2752
|
+
startScript: buildBridgeStartScript(),
|
|
2753
|
+
envFile: buildBridgeEnvFile(cfg)
|
|
2754
|
+
};
|
|
2755
|
+
})() : null;
|
|
2489
2756
|
const script = buildSpawnSetupScript({
|
|
2490
2757
|
name,
|
|
2491
2758
|
homeDir,
|
|
@@ -2495,13 +2762,32 @@ and try again.`
|
|
|
2495
2762
|
authJson,
|
|
2496
2763
|
claudeSettingsJson: includeClaudeHook ? CLAUDE_SETTINGS_JSON : null,
|
|
2497
2764
|
hookScriptSource: includeClaudeHook ? BASH_VIA_APE_SHELL_HOOK_SOURCE : null,
|
|
2498
|
-
claudeOauthToken
|
|
2765
|
+
claudeOauthToken,
|
|
2766
|
+
bridge
|
|
2499
2767
|
});
|
|
2500
2768
|
writeFileSync3(scriptPath, script, { mode: 448 });
|
|
2501
2769
|
consola21.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
|
|
2502
2770
|
consola21.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
2503
2771
|
execFileSync4(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
|
|
2504
2772
|
consola21.success(`Agent ${name} spawned.`);
|
|
2773
|
+
const bridgeRoom = typeof args["bridge-room"] === "string" ? args["bridge-room"] : void 0;
|
|
2774
|
+
if (args.bridge && bridgeRoom) {
|
|
2775
|
+
try {
|
|
2776
|
+
consola21.start(`Inviting agent into chat.openape.ai room "${bridgeRoom}"\u2026`);
|
|
2777
|
+
const result = await ensureRoomMembership({
|
|
2778
|
+
callerBearer: auth.access_token,
|
|
2779
|
+
roomName: bridgeRoom,
|
|
2780
|
+
agentEmail: registration.email
|
|
2781
|
+
});
|
|
2782
|
+
consola21.success(
|
|
2783
|
+
result.created ? `Created room ${result.roomId} and added ${registration.email}` : `Room ${result.roomId} already existed; added ${registration.email}`
|
|
2784
|
+
);
|
|
2785
|
+
} catch (err) {
|
|
2786
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2787
|
+
consola21.warn(`Could not auto-create / invite to chat room: ${msg}`);
|
|
2788
|
+
consola21.info("Add the agent manually with: ape-chat members add <agent-email>");
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2505
2791
|
console.log("");
|
|
2506
2792
|
console.log("Run as the agent with:");
|
|
2507
2793
|
console.log(` apes run --as ${name} -- claude --session-name ${name} --dangerously-skip-permissions`);
|
|
@@ -3359,7 +3645,7 @@ import { spawn } from "child_process";
|
|
|
3359
3645
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
3360
3646
|
import { createRequire } from "module";
|
|
3361
3647
|
import { tmpdir as tmpdir3 } from "os";
|
|
3362
|
-
import { dirname as dirname2, join as
|
|
3648
|
+
import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
|
|
3363
3649
|
var require2 = createRequire(import.meta.url);
|
|
3364
3650
|
function findProxyBin() {
|
|
3365
3651
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -3371,8 +3657,8 @@ function findProxyBin() {
|
|
|
3371
3657
|
return resolve3(dirname2(pkgPath), binRel);
|
|
3372
3658
|
}
|
|
3373
3659
|
async function startEphemeralProxy(configToml) {
|
|
3374
|
-
const tmpDir = mkdtempSync3(
|
|
3375
|
-
const configPath =
|
|
3660
|
+
const tmpDir = mkdtempSync3(join5(tmpdir3(), "openape-proxy-"));
|
|
3661
|
+
const configPath = join5(tmpDir, "config.toml");
|
|
3376
3662
|
writeFileSync4(configPath, configToml, { mode: 384 });
|
|
3377
3663
|
const binPath = findProxyBin();
|
|
3378
3664
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
@@ -3808,16 +4094,16 @@ var mcpCommand = defineCommand32({
|
|
|
3808
4094
|
if (transport !== "stdio" && transport !== "sse") {
|
|
3809
4095
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
3810
4096
|
}
|
|
3811
|
-
const { startMcpServer } = await import("./server-
|
|
4097
|
+
const { startMcpServer } = await import("./server-J4H4UV25.js");
|
|
3812
4098
|
await startMcpServer(transport, port);
|
|
3813
4099
|
}
|
|
3814
4100
|
});
|
|
3815
4101
|
|
|
3816
4102
|
// src/commands/init/index.ts
|
|
3817
|
-
import { existsSync as
|
|
4103
|
+
import { existsSync as existsSync7, copyFileSync, writeFileSync as writeFileSync5 } from "fs";
|
|
3818
4104
|
import { randomBytes } from "crypto";
|
|
3819
4105
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
3820
|
-
import { join as
|
|
4106
|
+
import { join as join6 } from "path";
|
|
3821
4107
|
import { defineCommand as defineCommand33 } from "citty";
|
|
3822
4108
|
import consola27 from "consola";
|
|
3823
4109
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
@@ -3826,7 +4112,7 @@ async function downloadTemplate(repo, targetDir) {
|
|
|
3826
4112
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
3827
4113
|
}
|
|
3828
4114
|
function installDeps(dir) {
|
|
3829
|
-
const hasLockFile = (name) =>
|
|
4115
|
+
const hasLockFile = (name) => existsSync7(join6(dir, name));
|
|
3830
4116
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
3831
4117
|
execFileSync6("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
3832
4118
|
} else if (hasLockFile("bun.lockb")) {
|
|
@@ -3891,7 +4177,7 @@ var initCommand = defineCommand33({
|
|
|
3891
4177
|
});
|
|
3892
4178
|
async function initSP(targetDir) {
|
|
3893
4179
|
const dir = targetDir || "my-app";
|
|
3894
|
-
if (
|
|
4180
|
+
if (existsSync7(join6(dir, "package.json"))) {
|
|
3895
4181
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
3896
4182
|
}
|
|
3897
4183
|
consola27.start("Scaffolding SP starter...");
|
|
@@ -3900,9 +4186,9 @@ async function initSP(targetDir) {
|
|
|
3900
4186
|
consola27.start("Installing dependencies...");
|
|
3901
4187
|
installDeps(dir);
|
|
3902
4188
|
consola27.success("Dependencies installed");
|
|
3903
|
-
const envExample =
|
|
3904
|
-
const envFile =
|
|
3905
|
-
if (
|
|
4189
|
+
const envExample = join6(dir, ".env.example");
|
|
4190
|
+
const envFile = join6(dir, ".env");
|
|
4191
|
+
if (existsSync7(envExample) && !existsSync7(envFile)) {
|
|
3906
4192
|
copyFileSync(envExample, envFile);
|
|
3907
4193
|
consola27.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
3908
4194
|
}
|
|
@@ -3916,7 +4202,7 @@ async function initSP(targetDir) {
|
|
|
3916
4202
|
}
|
|
3917
4203
|
async function initIdP(targetDir) {
|
|
3918
4204
|
const dir = targetDir || "my-idp";
|
|
3919
|
-
if (
|
|
4205
|
+
if (existsSync7(join6(dir, "package.json"))) {
|
|
3920
4206
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
3921
4207
|
}
|
|
3922
4208
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -3948,7 +4234,7 @@ async function initIdP(targetDir) {
|
|
|
3948
4234
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
3949
4235
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
3950
4236
|
].join("\n");
|
|
3951
|
-
writeFileSync5(
|
|
4237
|
+
writeFileSync5(join6(dir, ".env"), `${envContent}
|
|
3952
4238
|
`, { mode: 384 });
|
|
3953
4239
|
consola27.success(".env created");
|
|
3954
4240
|
console.log("");
|
|
@@ -3969,7 +4255,7 @@ async function initIdP(targetDir) {
|
|
|
3969
4255
|
|
|
3970
4256
|
// src/commands/enroll.ts
|
|
3971
4257
|
import { Buffer as Buffer5 } from "buffer";
|
|
3972
|
-
import { existsSync as
|
|
4258
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
3973
4259
|
import { execFile as execFile2 } from "child_process";
|
|
3974
4260
|
import { sign as sign2 } from "crypto";
|
|
3975
4261
|
import { defineCommand as defineCommand34 } from "citty";
|
|
@@ -3985,7 +4271,7 @@ function openBrowser2(url) {
|
|
|
3985
4271
|
}
|
|
3986
4272
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
3987
4273
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
3988
|
-
const keyContent =
|
|
4274
|
+
const keyContent = readFileSync6(resolvedKey, "utf-8");
|
|
3989
4275
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
3990
4276
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
3991
4277
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -4053,7 +4339,7 @@ var enrollCommand = defineCommand34({
|
|
|
4053
4339
|
}) || DEFAULT_KEY_PATH;
|
|
4054
4340
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
4055
4341
|
let publicKey;
|
|
4056
|
-
if (
|
|
4342
|
+
if (existsSync8(resolvedKey)) {
|
|
4057
4343
|
publicKey = readPublicKey(resolvedKey);
|
|
4058
4344
|
consola28.success(`Using existing key ${keyPath}`);
|
|
4059
4345
|
} else {
|
|
@@ -4097,7 +4383,7 @@ var enrollCommand = defineCommand34({
|
|
|
4097
4383
|
});
|
|
4098
4384
|
|
|
4099
4385
|
// src/commands/register-user.ts
|
|
4100
|
-
import { existsSync as
|
|
4386
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
4101
4387
|
import { defineCommand as defineCommand35 } from "citty";
|
|
4102
4388
|
import consola29 from "consola";
|
|
4103
4389
|
var registerUserCommand = defineCommand35({
|
|
@@ -4136,8 +4422,8 @@ var registerUserCommand = defineCommand35({
|
|
|
4136
4422
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
4137
4423
|
}
|
|
4138
4424
|
let publicKey = args.key;
|
|
4139
|
-
if (
|
|
4140
|
-
publicKey =
|
|
4425
|
+
if (existsSync9(args.key)) {
|
|
4426
|
+
publicKey = readFileSync7(args.key, "utf-8").trim();
|
|
4141
4427
|
}
|
|
4142
4428
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
4143
4429
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -4446,7 +4732,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
4446
4732
|
}
|
|
4447
4733
|
}
|
|
4448
4734
|
async function runHealth(args) {
|
|
4449
|
-
const version = true ? "0.
|
|
4735
|
+
const version = true ? "0.24.0" : "0.0.0";
|
|
4450
4736
|
const auth = loadAuth();
|
|
4451
4737
|
if (!auth) {
|
|
4452
4738
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -4639,25 +4925,25 @@ var workflowsCommand = defineCommand43({
|
|
|
4639
4925
|
});
|
|
4640
4926
|
|
|
4641
4927
|
// src/version-check.ts
|
|
4642
|
-
import { existsSync as
|
|
4643
|
-
import { homedir as
|
|
4644
|
-
import { join as
|
|
4928
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
4929
|
+
import { homedir as homedir6 } from "os";
|
|
4930
|
+
import { join as join7 } from "path";
|
|
4645
4931
|
import consola35 from "consola";
|
|
4646
4932
|
var PACKAGE_NAME = "@openape/apes";
|
|
4647
4933
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4648
|
-
var CACHE_FILE =
|
|
4934
|
+
var CACHE_FILE = join7(homedir6(), ".config", "apes", ".version-check.json");
|
|
4649
4935
|
function readCache() {
|
|
4650
|
-
if (!
|
|
4936
|
+
if (!existsSync10(CACHE_FILE)) return null;
|
|
4651
4937
|
try {
|
|
4652
|
-
return JSON.parse(
|
|
4938
|
+
return JSON.parse(readFileSync8(CACHE_FILE, "utf-8"));
|
|
4653
4939
|
} catch {
|
|
4654
4940
|
return null;
|
|
4655
4941
|
}
|
|
4656
4942
|
}
|
|
4657
4943
|
function writeCache(entry) {
|
|
4658
4944
|
try {
|
|
4659
|
-
const dir =
|
|
4660
|
-
if (!
|
|
4945
|
+
const dir = join7(homedir6(), ".config", "apes");
|
|
4946
|
+
if (!existsSync10(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
4661
4947
|
writeFileSync6(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
4662
4948
|
} catch {
|
|
4663
4949
|
}
|
|
@@ -4719,10 +5005,10 @@ if (shellRewrite) {
|
|
|
4719
5005
|
if (shellRewrite.action === "rewrite") {
|
|
4720
5006
|
process.argv = shellRewrite.argv;
|
|
4721
5007
|
} else if (shellRewrite.action === "version") {
|
|
4722
|
-
console.log(`ape-shell ${"0.
|
|
5008
|
+
console.log(`ape-shell ${"0.24.0"} (OpenApe DDISA shell wrapper)`);
|
|
4723
5009
|
process.exit(0);
|
|
4724
5010
|
} else if (shellRewrite.action === "help") {
|
|
4725
|
-
console.log(`ape-shell ${"0.
|
|
5011
|
+
console.log(`ape-shell ${"0.24.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
4726
5012
|
console.log("");
|
|
4727
5013
|
console.log("Usage:");
|
|
4728
5014
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -4780,7 +5066,7 @@ var configCommand = defineCommand44({
|
|
|
4780
5066
|
var main = defineCommand44({
|
|
4781
5067
|
meta: {
|
|
4782
5068
|
name: "apes",
|
|
4783
|
-
version: "0.
|
|
5069
|
+
version: "0.24.0",
|
|
4784
5070
|
description: "Unified CLI for OpenApe"
|
|
4785
5071
|
},
|
|
4786
5072
|
subCommands: {
|
|
@@ -4835,7 +5121,7 @@ async function maybeRefreshAuth() {
|
|
|
4835
5121
|
}
|
|
4836
5122
|
}
|
|
4837
5123
|
await maybeRefreshAuth();
|
|
4838
|
-
await maybeWarnStaleVersion("0.
|
|
5124
|
+
await maybeWarnStaleVersion("0.24.0").catch(() => {
|
|
4839
5125
|
});
|
|
4840
5126
|
runMain(main).catch((err) => {
|
|
4841
5127
|
if (err instanceof CliExit) {
|