@openape/apes 0.23.0 → 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
|
@@ -1891,23 +1891,26 @@ ${buildBridgeBootstrapBlock(input.bridge)}`;
|
|
|
1891
1891
|
function buildBridgeBlock(bridge) {
|
|
1892
1892
|
if (!bridge) return "";
|
|
1893
1893
|
return `
|
|
1894
|
-
mkdir -p "$HOME_DIR/Library/
|
|
1894
|
+
mkdir -p "$HOME_DIR/Library/Application Support/openape/bridge" "$HOME_DIR/Library/Logs" "$HOME_DIR/.pi/agent"
|
|
1895
1895
|
cat > "$HOME_DIR/.pi/agent/.env" ${shHeredoc(bridge.envFile)}
|
|
1896
1896
|
cat > "$HOME_DIR/Library/Application Support/openape/bridge/start.sh" ${shHeredoc(bridge.startScript)}
|
|
1897
1897
|
chmod 755 "$HOME_DIR/Library/Application Support/openape/bridge/start.sh"
|
|
1898
|
-
cat > "$HOME_DIR/Library/LaunchAgents/${bridge.plistLabel}.plist" ${shHeredoc(bridge.plistContent)}
|
|
1899
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)}
|
|
1900
1906
|
`;
|
|
1901
1907
|
}
|
|
1902
1908
|
function buildBridgeBootstrapBlock(bridge) {
|
|
1903
1909
|
if (!bridge) return "";
|
|
1904
1910
|
return `
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
# it up next time the agent logs in.
|
|
1909
|
-
launchctl bootstrap "gui/$NEXT_UID" "$HOME_DIR/Library/LaunchAgents/${bridge.plistLabel}.plist" || \\
|
|
1910
|
-
echo "warn: bridge bootstrap failed for gui/$NEXT_UID; loads on next login"
|
|
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}"
|
|
1911
1914
|
`;
|
|
1912
1915
|
}
|
|
1913
1916
|
function buildDestroyTeardownScript(input) {
|
|
@@ -1938,6 +1941,16 @@ if [ -n "$UID_OF" ]; then
|
|
|
1938
1941
|
pkill -9 -u "$UID_OF" 2>/dev/null || true
|
|
1939
1942
|
fi
|
|
1940
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
|
+
|
|
1941
1954
|
if [ -d "$HOME_DIR" ] && [ "$HOME_DIR" != "/" ] && [ "$HOME_DIR" != "" ]; then
|
|
1942
1955
|
rm -rf "$HOME_DIR"
|
|
1943
1956
|
fi
|
|
@@ -2355,6 +2368,57 @@ import { join as join4 } from "path";
|
|
|
2355
2368
|
import { defineCommand as defineCommand23 } from "citty";
|
|
2356
2369
|
import consola21 from "consola";
|
|
2357
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
|
+
|
|
2358
2422
|
// src/lib/keygen.ts
|
|
2359
2423
|
import { Buffer as Buffer4 } from "buffer";
|
|
2360
2424
|
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -2415,7 +2479,7 @@ function generateKeyPairInMemory() {
|
|
|
2415
2479
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
2416
2480
|
import { homedir as homedir5 } from "os";
|
|
2417
2481
|
import { join as join3 } from "path";
|
|
2418
|
-
var
|
|
2482
|
+
var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
|
|
2419
2483
|
function readLitellmEnv(envPath = join3(homedir5(), "litellm", ".env")) {
|
|
2420
2484
|
if (!existsSync6(envPath)) return null;
|
|
2421
2485
|
try {
|
|
@@ -2447,6 +2511,12 @@ function resolveBridgeConfig(opts) {
|
|
|
2447
2511
|
}
|
|
2448
2512
|
return { baseUrl, apiKey };
|
|
2449
2513
|
}
|
|
2514
|
+
function bridgePlistLabel(agentName) {
|
|
2515
|
+
return `${PLIST_LABEL_PREFIX}.${agentName}`;
|
|
2516
|
+
}
|
|
2517
|
+
function bridgePlistPath(agentName) {
|
|
2518
|
+
return `/Library/LaunchDaemons/${bridgePlistLabel(agentName)}.plist`;
|
|
2519
|
+
}
|
|
2450
2520
|
function buildBridgeEnvFile(cfg) {
|
|
2451
2521
|
return `# Auto-generated by 'apes agents spawn --bridge'.
|
|
2452
2522
|
# Read by the chat-bridge daemon at boot to talk to the local LLM proxy.
|
|
@@ -2457,20 +2527,66 @@ LITELLM_API_KEY=${cfg.apiKey}
|
|
|
2457
2527
|
function buildBridgeStartScript() {
|
|
2458
2528
|
return `#!/usr/bin/env bash
|
|
2459
2529
|
# Auto-generated by 'apes agents spawn --bridge'.
|
|
2460
|
-
# Idempotent installer + launcher.
|
|
2461
|
-
# globally via bun (homebrew-provided); subsequent boots skip the install.
|
|
2530
|
+
# Idempotent installer + launcher.
|
|
2462
2531
|
set -euo pipefail
|
|
2463
|
-
|
|
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
|
+
|
|
2464
2537
|
if ! command -v openape-chat-bridge >/dev/null 2>&1; then
|
|
2465
|
-
|
|
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
|
|
2466
2581
|
fi
|
|
2582
|
+
|
|
2467
2583
|
set -a
|
|
2468
2584
|
. "$HOME/.pi/agent/.env"
|
|
2469
2585
|
set +a
|
|
2470
2586
|
exec openape-chat-bridge
|
|
2471
2587
|
`;
|
|
2472
2588
|
}
|
|
2473
|
-
function buildBridgePlist(homeDir) {
|
|
2589
|
+
function buildBridgePlist(agentName, homeDir) {
|
|
2474
2590
|
const startScript = `${homeDir}/Library/Application Support/openape/bridge/start.sh`;
|
|
2475
2591
|
const stdoutLog = `${homeDir}/Library/Logs/openape-chat-bridge.log`;
|
|
2476
2592
|
const stderrLog = `${homeDir}/Library/Logs/openape-chat-bridge.err.log`;
|
|
@@ -2479,9 +2595,12 @@ function buildBridgePlist(homeDir) {
|
|
|
2479
2595
|
<plist version="1.0">
|
|
2480
2596
|
<dict>
|
|
2481
2597
|
<key>Label</key>
|
|
2482
|
-
<string>${
|
|
2598
|
+
<string>${bridgePlistLabel(agentName)}</string>
|
|
2599
|
+
<key>UserName</key>
|
|
2600
|
+
<string>${agentName}</string>
|
|
2483
2601
|
<key>ProgramArguments</key>
|
|
2484
2602
|
<array>
|
|
2603
|
+
<string>/bin/bash</string>
|
|
2485
2604
|
<string>${startScript}</string>
|
|
2486
2605
|
</array>
|
|
2487
2606
|
<key>WorkingDirectory</key>
|
|
@@ -2501,7 +2620,7 @@ function buildBridgePlist(homeDir) {
|
|
|
2501
2620
|
<key>HOME</key>
|
|
2502
2621
|
<string>${homeDir}</string>
|
|
2503
2622
|
<key>PATH</key>
|
|
2504
|
-
<string>/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
2623
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
2505
2624
|
</dict>
|
|
2506
2625
|
</dict>
|
|
2507
2626
|
</plist>
|
|
@@ -2547,6 +2666,10 @@ var spawnAgentCommand = defineCommand23({
|
|
|
2547
2666
|
"bridge-base-url": {
|
|
2548
2667
|
type: "string",
|
|
2549
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."
|
|
2550
2673
|
}
|
|
2551
2674
|
},
|
|
2552
2675
|
async run({ args }) {
|
|
@@ -2623,8 +2746,9 @@ and try again.`
|
|
|
2623
2746
|
cliBaseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0
|
|
2624
2747
|
});
|
|
2625
2748
|
return {
|
|
2626
|
-
plistLabel:
|
|
2627
|
-
|
|
2749
|
+
plistLabel: bridgePlistLabel(name),
|
|
2750
|
+
plistPath: bridgePlistPath(name),
|
|
2751
|
+
plistContent: buildBridgePlist(name, homeDir),
|
|
2628
2752
|
startScript: buildBridgeStartScript(),
|
|
2629
2753
|
envFile: buildBridgeEnvFile(cfg)
|
|
2630
2754
|
};
|
|
@@ -2646,6 +2770,24 @@ and try again.`
|
|
|
2646
2770
|
consola21.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
2647
2771
|
execFileSync4(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
|
|
2648
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
|
+
}
|
|
2649
2791
|
console.log("");
|
|
2650
2792
|
console.log("Run as the agent with:");
|
|
2651
2793
|
console.log(` apes run --as ${name} -- claude --session-name ${name} --dangerously-skip-permissions`);
|
|
@@ -3952,7 +4094,7 @@ var mcpCommand = defineCommand32({
|
|
|
3952
4094
|
if (transport !== "stdio" && transport !== "sse") {
|
|
3953
4095
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
3954
4096
|
}
|
|
3955
|
-
const { startMcpServer } = await import("./server-
|
|
4097
|
+
const { startMcpServer } = await import("./server-J4H4UV25.js");
|
|
3956
4098
|
await startMcpServer(transport, port);
|
|
3957
4099
|
}
|
|
3958
4100
|
});
|
|
@@ -4590,7 +4732,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
4590
4732
|
}
|
|
4591
4733
|
}
|
|
4592
4734
|
async function runHealth(args) {
|
|
4593
|
-
const version = true ? "0.
|
|
4735
|
+
const version = true ? "0.24.0" : "0.0.0";
|
|
4594
4736
|
const auth = loadAuth();
|
|
4595
4737
|
if (!auth) {
|
|
4596
4738
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -4863,10 +5005,10 @@ if (shellRewrite) {
|
|
|
4863
5005
|
if (shellRewrite.action === "rewrite") {
|
|
4864
5006
|
process.argv = shellRewrite.argv;
|
|
4865
5007
|
} else if (shellRewrite.action === "version") {
|
|
4866
|
-
console.log(`ape-shell ${"0.
|
|
5008
|
+
console.log(`ape-shell ${"0.24.0"} (OpenApe DDISA shell wrapper)`);
|
|
4867
5009
|
process.exit(0);
|
|
4868
5010
|
} else if (shellRewrite.action === "help") {
|
|
4869
|
-
console.log(`ape-shell ${"0.
|
|
5011
|
+
console.log(`ape-shell ${"0.24.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
4870
5012
|
console.log("");
|
|
4871
5013
|
console.log("Usage:");
|
|
4872
5014
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -4924,7 +5066,7 @@ var configCommand = defineCommand44({
|
|
|
4924
5066
|
var main = defineCommand44({
|
|
4925
5067
|
meta: {
|
|
4926
5068
|
name: "apes",
|
|
4927
|
-
version: "0.
|
|
5069
|
+
version: "0.24.0",
|
|
4928
5070
|
description: "Unified CLI for OpenApe"
|
|
4929
5071
|
},
|
|
4930
5072
|
subCommands: {
|
|
@@ -4979,7 +5121,7 @@ async function maybeRefreshAuth() {
|
|
|
4979
5121
|
}
|
|
4980
5122
|
}
|
|
4981
5123
|
await maybeRefreshAuth();
|
|
4982
|
-
await maybeWarnStaleVersion("0.
|
|
5124
|
+
await maybeWarnStaleVersion("0.24.0").catch(() => {
|
|
4983
5125
|
});
|
|
4984
5126
|
runMain(main).catch((err) => {
|
|
4985
5127
|
if (err instanceof CliExit) {
|