@openape/apes 0.12.6 → 0.13.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
@@ -61,7 +61,7 @@ import {
61
61
  } from "./chunk-6GPSKAMU.js";
62
62
 
63
63
  // src/cli.ts
64
- import consola27 from "consola";
64
+ import consola28 from "consola";
65
65
 
66
66
  // src/ape-shell.ts
67
67
  import path from "path";
@@ -91,7 +91,7 @@ function rewriteApeShellArgs(argv, argv0) {
91
91
  }
92
92
 
93
93
  // src/cli.ts
94
- import { defineCommand as defineCommand33, runMain } from "citty";
94
+ import { defineCommand as defineCommand34, runMain } from "citty";
95
95
 
96
96
  // src/commands/auth/login.ts
97
97
  import { Buffer } from "buffer";
@@ -256,7 +256,7 @@ async function loginWithPKCE(idp) {
256
256
  authUrl.searchParams.set("state", state);
257
257
  authUrl.searchParams.set("nonce", nonce);
258
258
  authUrl.searchParams.set("scope", "openid email profile offline_access");
259
- const code = await new Promise((resolve3, reject) => {
259
+ const code = await new Promise((resolve4, reject) => {
260
260
  const server = createServer((req, res) => {
261
261
  const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
262
262
  if (url.pathname === "/callback") {
@@ -273,7 +273,7 @@ async function loginWithPKCE(idp) {
273
273
  res.writeHead(200, { "Content-Type": "text/html" });
274
274
  res.end("<h1>Login successful!</h1><p>You can close this window.</p>");
275
275
  server.close();
276
- resolve3(authCode);
276
+ resolve4(authCode);
277
277
  return;
278
278
  }
279
279
  res.writeHead(400);
@@ -833,7 +833,7 @@ async function waitForApproval2(grantsUrl, grantId) {
833
833
  if (grant.status === "revoked") {
834
834
  throw new CliError("Grant revoked.");
835
835
  }
836
- await new Promise((resolve3) => setTimeout(resolve3, interval));
836
+ await new Promise((resolve4) => setTimeout(resolve4, interval));
837
837
  }
838
838
  throw new CliError("Timed out waiting for approval.");
839
839
  }
@@ -2455,9 +2455,215 @@ async function runAudienceMode(audience, action, args) {
2455
2455
  }
2456
2456
  }
2457
2457
 
2458
- // src/commands/explain.ts
2458
+ // src/commands/proxy.ts
2459
+ import { spawn as spawn2 } from "child_process";
2459
2460
  import { defineCommand as defineCommand22 } from "citty";
2460
- var explainCommand = defineCommand22({
2461
+ import consola20 from "consola";
2462
+
2463
+ // src/proxy/config.ts
2464
+ import { homedir as homedir4 } from "os";
2465
+ import { join as join2 } from "path";
2466
+ function defaultAuditPath() {
2467
+ const xdg = process.env.XDG_STATE_HOME;
2468
+ const stateDir = xdg && xdg.length > 0 ? xdg : join2(homedir4(), ".local", "state");
2469
+ return join2(stateDir, "openape", "proxy-audit.jsonl");
2470
+ }
2471
+ function buildDefaultProxyConfigToml() {
2472
+ const auditPath = defaultAuditPath();
2473
+ return `# Auto-generated by apes proxy -- (M1a). Do not edit; this file is
2474
+ # recreated for every \`apes proxy --\` invocation and deleted on exit.
2475
+ [proxy]
2476
+ listen = "127.0.0.1:0"
2477
+ idp_url = "https://id.openape.ai"
2478
+ agent_email = "ephemeral@apes-proxy.local"
2479
+ default_action = "allow"
2480
+ audit_log = "${auditPath.replace(/"/g, '\\"')}"
2481
+
2482
+ # Cloud / link-local metadata endpoints \u2014 never let agent traffic reach these
2483
+ # even if a downstream policy mistake would otherwise allow it.
2484
+ [[deny]]
2485
+ domain = "169.254.169.254"
2486
+ note = "AWS / DigitalOcean / Azure metadata endpoint"
2487
+
2488
+ [[deny]]
2489
+ domain = "metadata.google.internal"
2490
+ note = "GCP metadata endpoint"
2491
+
2492
+ [[deny]]
2493
+ domain = "*.internal"
2494
+ note = "VPC-internal hostname suffix"
2495
+ `;
2496
+ }
2497
+
2498
+ // src/proxy/local-proxy.ts
2499
+ import { spawn } from "child_process";
2500
+ import { mkdtempSync, rmSync, writeFileSync } from "fs";
2501
+ import { createRequire } from "module";
2502
+ import { tmpdir } from "os";
2503
+ import { dirname, join as join3, resolve as resolve2 } from "path";
2504
+ var require2 = createRequire(import.meta.url);
2505
+ function findProxyBin() {
2506
+ const pkgPath = require2.resolve("@openape/proxy/package.json");
2507
+ const pkg = require2("@openape/proxy/package.json");
2508
+ const binRel = pkg.bin?.["openape-proxy"];
2509
+ if (!binRel) {
2510
+ throw new Error("@openape/proxy is missing the openape-proxy bin entry");
2511
+ }
2512
+ return resolve2(dirname(pkgPath), binRel);
2513
+ }
2514
+ async function startEphemeralProxy(configToml) {
2515
+ const tmpDir = mkdtempSync(join3(tmpdir(), "openape-proxy-"));
2516
+ const configPath = join3(tmpDir, "config.toml");
2517
+ writeFileSync(configPath, configToml, { mode: 384 });
2518
+ const binPath = findProxyBin();
2519
+ const child = spawn(process.execPath, [binPath, "-c", configPath], {
2520
+ stdio: ["ignore", "pipe", "pipe"],
2521
+ detached: false
2522
+ });
2523
+ const cleanupTmp = () => {
2524
+ try {
2525
+ rmSync(tmpDir, { recursive: true, force: true });
2526
+ } catch {
2527
+ }
2528
+ };
2529
+ let port;
2530
+ try {
2531
+ port = await waitForListenLine(child);
2532
+ } catch (err) {
2533
+ child.kill("SIGTERM");
2534
+ cleanupTmp();
2535
+ throw err;
2536
+ }
2537
+ child.stderr?.on("data", (chunk) => process.stderr.write(chunk));
2538
+ return {
2539
+ url: `http://127.0.0.1:${port}`,
2540
+ port,
2541
+ child,
2542
+ close: () => new Promise((resolveClose) => {
2543
+ const done = () => {
2544
+ cleanupTmp();
2545
+ resolveClose();
2546
+ };
2547
+ if (child.exitCode !== null || child.signalCode !== null) {
2548
+ done();
2549
+ return;
2550
+ }
2551
+ child.once("exit", done);
2552
+ child.kill("SIGTERM");
2553
+ setTimeout(() => {
2554
+ if (child.exitCode === null && child.signalCode === null) {
2555
+ child.kill("SIGKILL");
2556
+ }
2557
+ }, 2e3).unref();
2558
+ })
2559
+ };
2560
+ }
2561
+ function waitForListenLine(child) {
2562
+ return new Promise((resolveWait, rejectWait) => {
2563
+ let buf = "";
2564
+ let timer;
2565
+ function onData(chunk) {
2566
+ buf += chunk.toString("utf8");
2567
+ const m = buf.match(/Listening on http:\/\/[^:\s]+:(\d+)/);
2568
+ if (m) {
2569
+ cleanup();
2570
+ resolveWait(Number(m[1]));
2571
+ }
2572
+ }
2573
+ function onExit(code) {
2574
+ cleanup();
2575
+ rejectWait(new Error(`openape-proxy exited before listening (code=${code}, stderr accumulated above)`));
2576
+ }
2577
+ function onError(err) {
2578
+ cleanup();
2579
+ rejectWait(err);
2580
+ }
2581
+ function cleanup() {
2582
+ clearTimeout(timer);
2583
+ child.stdout?.off("data", onData);
2584
+ child.off("exit", onExit);
2585
+ child.off("error", onError);
2586
+ }
2587
+ timer = setTimeout(() => {
2588
+ cleanup();
2589
+ rejectWait(new Error("openape-proxy startup timeout (5s)"));
2590
+ }, 5e3);
2591
+ timer.unref();
2592
+ child.stdout?.on("data", onData);
2593
+ child.once("exit", onExit);
2594
+ child.once("error", onError);
2595
+ });
2596
+ }
2597
+
2598
+ // src/commands/proxy.ts
2599
+ var proxyCommand = defineCommand22({
2600
+ meta: {
2601
+ name: "proxy",
2602
+ description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
2603
+ },
2604
+ args: {
2605
+ _: {
2606
+ type: "positional",
2607
+ description: "Command to execute (after --)",
2608
+ required: false
2609
+ }
2610
+ },
2611
+ async run({ rawArgs }) {
2612
+ const wrapped = extractWrappedCommand(rawArgs ?? []);
2613
+ if (wrapped.length === 0) {
2614
+ throw new CliError("Usage: apes proxy -- <cmd> [args...]");
2615
+ }
2616
+ const reuseUrl = process.env.OPENAPE_PROXY_URL;
2617
+ let proxyUrl;
2618
+ let close = null;
2619
+ if (reuseUrl) {
2620
+ proxyUrl = reuseUrl;
2621
+ consola20.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
2622
+ } else {
2623
+ const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml());
2624
+ proxyUrl = ephemeral.url;
2625
+ close = ephemeral.close;
2626
+ consola20.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
2627
+ }
2628
+ const childEnv = {
2629
+ ...process.env,
2630
+ HTTPS_PROXY: proxyUrl,
2631
+ HTTP_PROXY: proxyUrl,
2632
+ NO_PROXY: process.env.NO_PROXY ?? "127.0.0.1,localhost"
2633
+ };
2634
+ const exitCode = await new Promise((resolveExit) => {
2635
+ const child = spawn2(wrapped[0], wrapped.slice(1), {
2636
+ stdio: "inherit",
2637
+ env: childEnv
2638
+ });
2639
+ const forward = (sig) => () => child.kill(sig);
2640
+ const onSigint = forward("SIGINT");
2641
+ const onSigterm = forward("SIGTERM");
2642
+ process.on("SIGINT", onSigint);
2643
+ process.on("SIGTERM", onSigterm);
2644
+ child.once("exit", (code, signal) => {
2645
+ process.off("SIGINT", onSigint);
2646
+ process.off("SIGTERM", onSigterm);
2647
+ if (signal) resolveExit(128 + (signalNumber(signal) ?? 0));
2648
+ else resolveExit(code ?? 0);
2649
+ });
2650
+ child.once("error", (err) => {
2651
+ consola20.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
2652
+ resolveExit(127);
2653
+ });
2654
+ });
2655
+ if (close) await close();
2656
+ if (exitCode !== 0) throw new CliExit(exitCode);
2657
+ }
2658
+ });
2659
+ function signalNumber(signal) {
2660
+ const map = { SIGINT: 2, SIGTERM: 15, SIGHUP: 1, SIGQUIT: 3, SIGKILL: 9 };
2661
+ return map[signal];
2662
+ }
2663
+
2664
+ // src/commands/explain.ts
2665
+ import { defineCommand as defineCommand23 } from "citty";
2666
+ var explainCommand = defineCommand23({
2461
2667
  meta: {
2462
2668
  name: "explain",
2463
2669
  description: "Show what permission a command would need"
@@ -2495,9 +2701,9 @@ var explainCommand = defineCommand22({
2495
2701
  });
2496
2702
 
2497
2703
  // src/commands/config/get.ts
2498
- import { defineCommand as defineCommand23 } from "citty";
2499
- import consola20 from "consola";
2500
- var configGetCommand = defineCommand23({
2704
+ import { defineCommand as defineCommand24 } from "citty";
2705
+ import consola21 from "consola";
2706
+ var configGetCommand = defineCommand24({
2501
2707
  meta: {
2502
2708
  name: "get",
2503
2709
  description: "Get a configuration value"
@@ -2517,7 +2723,7 @@ var configGetCommand = defineCommand23({
2517
2723
  if (idp)
2518
2724
  console.log(idp);
2519
2725
  else
2520
- consola20.info("No IdP configured.");
2726
+ consola21.info("No IdP configured.");
2521
2727
  break;
2522
2728
  }
2523
2729
  case "email": {
@@ -2525,7 +2731,7 @@ var configGetCommand = defineCommand23({
2525
2731
  if (auth?.email)
2526
2732
  console.log(auth.email);
2527
2733
  else
2528
- consola20.info("Not logged in.");
2734
+ consola21.info("Not logged in.");
2529
2735
  break;
2530
2736
  }
2531
2737
  default: {
@@ -2538,7 +2744,7 @@ var configGetCommand = defineCommand23({
2538
2744
  if (sectionObj && field in sectionObj) {
2539
2745
  console.log(sectionObj[field]);
2540
2746
  } else {
2541
- consola20.info(`Key "${key}" not set.`);
2747
+ consola21.info(`Key "${key}" not set.`);
2542
2748
  }
2543
2749
  } else {
2544
2750
  throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
@@ -2549,9 +2755,9 @@ var configGetCommand = defineCommand23({
2549
2755
  });
2550
2756
 
2551
2757
  // src/commands/config/set.ts
2552
- import { defineCommand as defineCommand24 } from "citty";
2553
- import consola21 from "consola";
2554
- var configSetCommand = defineCommand24({
2758
+ import { defineCommand as defineCommand25 } from "citty";
2759
+ import consola22 from "consola";
2760
+ var configSetCommand = defineCommand25({
2555
2761
  meta: {
2556
2762
  name: "set",
2557
2763
  description: "Set a configuration value"
@@ -2587,12 +2793,12 @@ var configSetCommand = defineCommand24({
2587
2793
  throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
2588
2794
  }
2589
2795
  saveConfig(config);
2590
- consola21.success(`Set ${key} = ${value}`);
2796
+ consola22.success(`Set ${key} = ${value}`);
2591
2797
  }
2592
2798
  });
2593
2799
 
2594
2800
  // src/commands/fetch/index.ts
2595
- import { defineCommand as defineCommand25 } from "citty";
2801
+ import { defineCommand as defineCommand26 } from "citty";
2596
2802
  async function doRequest(method, url, body, contentType, raw, showHeaders) {
2597
2803
  const token = getAuthToken();
2598
2804
  if (!token) {
@@ -2628,13 +2834,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
2628
2834
  throw new CliError(`HTTP ${response.status} ${response.statusText}`);
2629
2835
  }
2630
2836
  }
2631
- var fetchCommand = defineCommand25({
2837
+ var fetchCommand = defineCommand26({
2632
2838
  meta: {
2633
2839
  name: "fetch",
2634
2840
  description: "Make authenticated HTTP requests"
2635
2841
  },
2636
2842
  subCommands: {
2637
- get: defineCommand25({
2843
+ get: defineCommand26({
2638
2844
  meta: {
2639
2845
  name: "get",
2640
2846
  description: "GET request with auth token"
@@ -2660,7 +2866,7 @@ var fetchCommand = defineCommand25({
2660
2866
  await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
2661
2867
  }
2662
2868
  }),
2663
- post: defineCommand25({
2869
+ post: defineCommand26({
2664
2870
  meta: {
2665
2871
  name: "post",
2666
2872
  description: "POST request with auth token"
@@ -2699,8 +2905,8 @@ var fetchCommand = defineCommand25({
2699
2905
  });
2700
2906
 
2701
2907
  // src/commands/mcp/index.ts
2702
- import { defineCommand as defineCommand26 } from "citty";
2703
- var mcpCommand = defineCommand26({
2908
+ import { defineCommand as defineCommand27 } from "citty";
2909
+ var mcpCommand = defineCommand27({
2704
2910
  meta: {
2705
2911
  name: "mcp",
2706
2912
  description: "Start MCP server for AI agents"
@@ -2723,25 +2929,25 @@ var mcpCommand = defineCommand26({
2723
2929
  if (transport !== "stdio" && transport !== "sse") {
2724
2930
  throw new Error('Transport must be "stdio" or "sse"');
2725
2931
  }
2726
- const { startMcpServer } = await import("./server-RP56MNWI.js");
2932
+ const { startMcpServer } = await import("./server-QGNNBQHT.js");
2727
2933
  await startMcpServer(transport, port);
2728
2934
  }
2729
2935
  });
2730
2936
 
2731
2937
  // src/commands/init/index.ts
2732
- import { existsSync as existsSync3, copyFileSync, writeFileSync } from "fs";
2938
+ import { existsSync as existsSync3, copyFileSync, writeFileSync as writeFileSync2 } from "fs";
2733
2939
  import { randomBytes } from "crypto";
2734
2940
  import { execFileSync as execFileSync3 } from "child_process";
2735
- import { join as join2 } from "path";
2736
- import { defineCommand as defineCommand27 } from "citty";
2737
- import consola22 from "consola";
2941
+ import { join as join4 } from "path";
2942
+ import { defineCommand as defineCommand28 } from "citty";
2943
+ import consola23 from "consola";
2738
2944
  var DEFAULT_IDP_URL = "https://id.openape.at";
2739
2945
  async function downloadTemplate(repo, targetDir) {
2740
2946
  const { downloadTemplate: gigetDownload } = await import("giget");
2741
2947
  await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
2742
2948
  }
2743
2949
  function installDeps(dir) {
2744
- const hasLockFile = (name) => existsSync3(join2(dir, name));
2950
+ const hasLockFile = (name) => existsSync3(join4(dir, name));
2745
2951
  if (hasLockFile("pnpm-lock.yaml")) {
2746
2952
  execFileSync3("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
2747
2953
  } else if (hasLockFile("bun.lockb")) {
@@ -2751,20 +2957,20 @@ function installDeps(dir) {
2751
2957
  }
2752
2958
  }
2753
2959
  async function promptChoice(message, choices) {
2754
- const result = await consola22.prompt(message, { type: "select", options: choices });
2960
+ const result = await consola23.prompt(message, { type: "select", options: choices });
2755
2961
  if (typeof result === "symbol") {
2756
2962
  throw new CliExit(0);
2757
2963
  }
2758
2964
  return result;
2759
2965
  }
2760
2966
  async function promptText(message, defaultValue) {
2761
- const result = await consola22.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
2967
+ const result = await consola23.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
2762
2968
  if (typeof result === "symbol") {
2763
2969
  throw new CliExit(0);
2764
2970
  }
2765
2971
  return result || defaultValue || "";
2766
2972
  }
2767
- var initCommand = defineCommand27({
2973
+ var initCommand = defineCommand28({
2768
2974
  meta: {
2769
2975
  name: "init",
2770
2976
  description: "Scaffold a new OpenApe project"
@@ -2806,23 +3012,23 @@ var initCommand = defineCommand27({
2806
3012
  });
2807
3013
  async function initSP(targetDir) {
2808
3014
  const dir = targetDir || "my-app";
2809
- if (existsSync3(join2(dir, "package.json"))) {
3015
+ if (existsSync3(join4(dir, "package.json"))) {
2810
3016
  throw new CliError(`Directory "${dir}" already contains a project.`);
2811
3017
  }
2812
- consola22.start("Scaffolding SP starter...");
3018
+ consola23.start("Scaffolding SP starter...");
2813
3019
  await downloadTemplate("openape-ai/openape-sp-starter", dir);
2814
- consola22.success("Scaffolded from openape-sp-starter");
2815
- consola22.start("Installing dependencies...");
3020
+ consola23.success("Scaffolded from openape-sp-starter");
3021
+ consola23.start("Installing dependencies...");
2816
3022
  installDeps(dir);
2817
- consola22.success("Dependencies installed");
2818
- const envExample = join2(dir, ".env.example");
2819
- const envFile = join2(dir, ".env");
3023
+ consola23.success("Dependencies installed");
3024
+ const envExample = join4(dir, ".env.example");
3025
+ const envFile = join4(dir, ".env");
2820
3026
  if (existsSync3(envExample) && !existsSync3(envFile)) {
2821
3027
  copyFileSync(envExample, envFile);
2822
- consola22.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
3028
+ consola23.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
2823
3029
  }
2824
3030
  console.log("");
2825
- consola22.box([
3031
+ consola23.box([
2826
3032
  `cd ${dir}`,
2827
3033
  "npm run dev",
2828
3034
  "",
@@ -2831,7 +3037,7 @@ async function initSP(targetDir) {
2831
3037
  }
2832
3038
  async function initIdP(targetDir) {
2833
3039
  const dir = targetDir || "my-idp";
2834
- if (existsSync3(join2(dir, "package.json"))) {
3040
+ if (existsSync3(join4(dir, "package.json"))) {
2835
3041
  throw new CliError(`Directory "${dir}" already contains a project.`);
2836
3042
  }
2837
3043
  const domain = await promptText("Domain for the IdP", "localhost");
@@ -2841,15 +3047,15 @@ async function initIdP(targetDir) {
2841
3047
  "s3 (S3-compatible)"
2842
3048
  ]);
2843
3049
  const adminEmail = await promptText("Admin email");
2844
- consola22.start("Scaffolding IdP starter...");
3050
+ consola23.start("Scaffolding IdP starter...");
2845
3051
  await downloadTemplate("openape-ai/openape-idp-starter", dir);
2846
- consola22.success("Scaffolded from openape-idp-starter");
2847
- consola22.start("Installing dependencies...");
3052
+ consola23.success("Scaffolded from openape-idp-starter");
3053
+ consola23.start("Installing dependencies...");
2848
3054
  installDeps(dir);
2849
- consola22.success("Dependencies installed");
3055
+ consola23.success("Dependencies installed");
2850
3056
  const sessionSecret = randomBytes(32).toString("hex");
2851
3057
  const managementToken = randomBytes(32).toString("hex");
2852
- consola22.success("Secrets generated");
3058
+ consola23.success("Secrets generated");
2853
3059
  const isLocalhost = domain === "localhost";
2854
3060
  const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
2855
3061
  const envContent = [
@@ -2863,11 +3069,11 @@ async function initIdP(targetDir) {
2863
3069
  `NUXT_OPENAPE_RP_ID=${domain}`,
2864
3070
  `NUXT_OPENAPE_RP_ORIGIN=${origin}`
2865
3071
  ].join("\n");
2866
- writeFileSync(join2(dir, ".env"), `${envContent}
3072
+ writeFileSync2(join4(dir, ".env"), `${envContent}
2867
3073
  `, { mode: 384 });
2868
- consola22.success(".env created");
3074
+ consola23.success(".env created");
2869
3075
  console.log("");
2870
- consola22.box([
3076
+ consola23.box([
2871
3077
  `cd ${dir}`,
2872
3078
  "npm run dev",
2873
3079
  "",
@@ -2884,19 +3090,19 @@ async function initIdP(targetDir) {
2884
3090
 
2885
3091
  // src/commands/enroll.ts
2886
3092
  import { Buffer as Buffer2 } from "buffer";
2887
- import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
3093
+ import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync } from "fs";
2888
3094
  import { execFile as execFile2 } from "child_process";
2889
3095
  import { generateKeyPairSync, sign } from "crypto";
2890
- import { dirname, resolve as resolve2 } from "path";
2891
- import { homedir as homedir4 } from "os";
2892
- import { defineCommand as defineCommand28 } from "citty";
2893
- import consola23 from "consola";
3096
+ import { dirname as dirname2, resolve as resolve3 } from "path";
3097
+ import { homedir as homedir5 } from "os";
3098
+ import { defineCommand as defineCommand29 } from "citty";
3099
+ import consola24 from "consola";
2894
3100
  var DEFAULT_IDP_URL2 = "https://id.openape.at";
2895
3101
  var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
2896
3102
  var POLL_INTERVAL = 3e3;
2897
3103
  var POLL_TIMEOUT = 3e5;
2898
3104
  function resolvePath2(p) {
2899
- return resolve2(p.replace(/^~/, homedir4()));
3105
+ return resolve3(p.replace(/^~/, homedir5()));
2900
3106
  }
2901
3107
  function openBrowser2(url) {
2902
3108
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -2922,13 +3128,13 @@ function readPublicKey(keyPath) {
2922
3128
  }
2923
3129
  function generateAndSaveKey(keyPath) {
2924
3130
  const resolved = resolvePath2(keyPath);
2925
- const dir = dirname(resolved);
3131
+ const dir = dirname2(resolved);
2926
3132
  if (!existsSync4(dir)) {
2927
3133
  mkdirSync(dir, { recursive: true });
2928
3134
  }
2929
3135
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
2930
3136
  const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
2931
- writeFileSync2(resolved, privatePem, { mode: 384 });
3137
+ writeFileSync3(resolved, privatePem, { mode: 384 });
2932
3138
  const jwk = publicKey.export({ format: "jwk" });
2933
3139
  const pubBytes = Buffer2.from(jwk.x, "base64url");
2934
3140
  const keyTypeStr = "ssh-ed25519";
@@ -2938,7 +3144,7 @@ function generateAndSaveKey(keyPath) {
2938
3144
  pubKeyLen.writeUInt32BE(pubBytes.length);
2939
3145
  const blob = Buffer2.concat([keyTypeLen, Buffer2.from(keyTypeStr), pubKeyLen, pubBytes]);
2940
3146
  const pubKeyStr = `ssh-ed25519 ${blob.toString("base64")}`;
2941
- writeFileSync2(`${resolved}.pub`, `${pubKeyStr}
3147
+ writeFileSync3(`${resolved}.pub`, `${pubKeyStr}
2942
3148
  `, { mode: 420 });
2943
3149
  return pubKeyStr;
2944
3150
  }
@@ -2971,11 +3177,11 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
2971
3177
  }
2972
3178
  } catch {
2973
3179
  }
2974
- await new Promise((resolve3) => setTimeout(resolve3, POLL_INTERVAL));
3180
+ await new Promise((resolve4) => setTimeout(resolve4, POLL_INTERVAL));
2975
3181
  }
2976
3182
  throw new Error("Enrollment timed out. Please check the browser and try again.");
2977
3183
  }
2978
- var enrollCommand = defineCommand28({
3184
+ var enrollCommand = defineCommand29({
2979
3185
  meta: {
2980
3186
  name: "enroll",
2981
3187
  description: "Enroll an agent with an Identity Provider"
@@ -2995,18 +3201,18 @@ var enrollCommand = defineCommand28({
2995
3201
  }
2996
3202
  },
2997
3203
  async run({ args }) {
2998
- const idp = args.idp || await consola23.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
3204
+ const idp = args.idp || await consola24.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
2999
3205
  if (typeof r === "symbol") throw new CliExit(0);
3000
3206
  return r;
3001
3207
  }) || DEFAULT_IDP_URL2;
3002
- const agentName = args.name || await consola23.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
3208
+ const agentName = args.name || await consola24.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
3003
3209
  if (typeof r === "symbol") throw new CliExit(0);
3004
3210
  return r;
3005
3211
  });
3006
3212
  if (!agentName) {
3007
3213
  throw new CliError("Agent name is required.");
3008
3214
  }
3009
- const keyPath = args.key || await consola23.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
3215
+ const keyPath = args.key || await consola24.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
3010
3216
  if (typeof r === "symbol") throw new CliExit(0);
3011
3217
  return r;
3012
3218
  }) || DEFAULT_KEY_PATH;
@@ -3014,19 +3220,19 @@ var enrollCommand = defineCommand28({
3014
3220
  let publicKey;
3015
3221
  if (existsSync4(resolvedKey)) {
3016
3222
  publicKey = readPublicKey(resolvedKey);
3017
- consola23.success(`Using existing key ${keyPath}`);
3223
+ consola24.success(`Using existing key ${keyPath}`);
3018
3224
  } else {
3019
- consola23.start(`Generating Ed25519 key pair at ${keyPath}...`);
3225
+ consola24.start(`Generating Ed25519 key pair at ${keyPath}...`);
3020
3226
  publicKey = generateAndSaveKey(keyPath);
3021
- consola23.success(`Key pair generated at ${keyPath}`);
3227
+ consola24.success(`Key pair generated at ${keyPath}`);
3022
3228
  }
3023
3229
  const encodedKey = encodeURIComponent(publicKey);
3024
3230
  const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
3025
- consola23.info("Opening browser for enrollment...");
3026
- consola23.info(`\u2192 ${idp}/enroll`);
3231
+ consola24.info("Opening browser for enrollment...");
3232
+ consola24.info(`\u2192 ${idp}/enroll`);
3027
3233
  openBrowser2(enrollUrl);
3028
3234
  console.log("");
3029
- const agentEmail = await consola23.prompt(
3235
+ const agentEmail = await consola24.prompt(
3030
3236
  "Agent email (shown in browser after enrollment)",
3031
3237
  { type: "text", placeholder: `agent+${agentName}@...` }
3032
3238
  ).then((r) => {
@@ -3036,7 +3242,7 @@ var enrollCommand = defineCommand28({
3036
3242
  if (!agentEmail) {
3037
3243
  throw new CliError("Agent email is required to verify enrollment.");
3038
3244
  }
3039
- consola23.start("Verifying enrollment...");
3245
+ consola24.start("Verifying enrollment...");
3040
3246
  const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
3041
3247
  saveAuth({
3042
3248
  idp,
@@ -3048,18 +3254,18 @@ var enrollCommand = defineCommand28({
3048
3254
  config.defaults = { ...config.defaults, idp };
3049
3255
  config.agent = { key: keyPath, email: agentEmail };
3050
3256
  saveConfig(config);
3051
- consola23.success(`Agent enrolled as ${agentEmail}`);
3052
- consola23.success("Config saved to ~/.config/apes/");
3257
+ consola24.success(`Agent enrolled as ${agentEmail}`);
3258
+ consola24.success("Config saved to ~/.config/apes/");
3053
3259
  console.log("");
3054
- consola23.info("Verify with: apes whoami");
3260
+ consola24.info("Verify with: apes whoami");
3055
3261
  }
3056
3262
  });
3057
3263
 
3058
3264
  // src/commands/register-user.ts
3059
3265
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
3060
- import { defineCommand as defineCommand29 } from "citty";
3061
- import consola24 from "consola";
3062
- var registerUserCommand = defineCommand29({
3266
+ import { defineCommand as defineCommand30 } from "citty";
3267
+ import consola25 from "consola";
3268
+ var registerUserCommand = defineCommand30({
3063
3269
  meta: {
3064
3270
  name: "register-user",
3065
3271
  description: "Register a sub-user with SSH key"
@@ -3114,15 +3320,15 @@ var registerUserCommand = defineCommand29({
3114
3320
  ...userType ? { type: userType } : {}
3115
3321
  }
3116
3322
  });
3117
- consola24.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
3323
+ consola25.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
3118
3324
  }
3119
3325
  });
3120
3326
 
3121
3327
  // src/commands/dns-check.ts
3122
- import { defineCommand as defineCommand30 } from "citty";
3123
- import consola25 from "consola";
3328
+ import { defineCommand as defineCommand31 } from "citty";
3329
+ import consola26 from "consola";
3124
3330
  import { resolveDDISA as resolveDDISA2 } from "@openape/core";
3125
- var dnsCheckCommand = defineCommand30({
3331
+ var dnsCheckCommand = defineCommand31({
3126
3332
  meta: {
3127
3333
  name: "dns-check",
3128
3334
  description: "Validate DDISA DNS TXT records for a domain"
@@ -3136,7 +3342,7 @@ var dnsCheckCommand = defineCommand30({
3136
3342
  },
3137
3343
  async run({ args }) {
3138
3344
  const domain = args.domain;
3139
- consola25.start(`Checking _ddisa.${domain}...`);
3345
+ consola26.start(`Checking _ddisa.${domain}...`);
3140
3346
  try {
3141
3347
  const result = await resolveDDISA2(domain);
3142
3348
  if (!result) {
@@ -3145,7 +3351,7 @@ var dnsCheckCommand = defineCommand30({
3145
3351
  console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
3146
3352
  throw new CliError(`No DDISA record found for ${domain}`);
3147
3353
  }
3148
- consola25.success(`_ddisa.${domain} \u2192 ${result.idp}`);
3354
+ consola26.success(`_ddisa.${domain} \u2192 ${result.idp}`);
3149
3355
  console.log("");
3150
3356
  console.log(` Version: ${result.version || "ddisa1"}`);
3151
3357
  console.log(` IdP URL: ${result.idp}`);
@@ -3154,14 +3360,14 @@ var dnsCheckCommand = defineCommand30({
3154
3360
  if (result.priority !== void 0)
3155
3361
  console.log(` Priority: ${result.priority}`);
3156
3362
  console.log("");
3157
- consola25.start(`Verifying IdP at ${result.idp}...`);
3363
+ consola26.start(`Verifying IdP at ${result.idp}...`);
3158
3364
  const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
3159
3365
  if (!discoResp.ok) {
3160
- consola25.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
3366
+ consola26.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
3161
3367
  return;
3162
3368
  }
3163
3369
  const disco = await discoResp.json();
3164
- consola25.success(`IdP is reachable`);
3370
+ consola26.success(`IdP is reachable`);
3165
3371
  console.log(` Issuer: ${disco.issuer}`);
3166
3372
  console.log(` DDISA: v${disco.ddisa_version || "?"}`);
3167
3373
  if (disco.ddisa_auth_methods_supported) {
@@ -3179,7 +3385,7 @@ var dnsCheckCommand = defineCommand30({
3179
3385
  // src/commands/health.ts
3180
3386
  import { exec } from "child_process";
3181
3387
  import { promisify } from "util";
3182
- import { defineCommand as defineCommand31 } from "citty";
3388
+ import { defineCommand as defineCommand32 } from "citty";
3183
3389
  var execAsync = promisify(exec);
3184
3390
  async function resolveApeShellPath() {
3185
3391
  try {
@@ -3215,7 +3421,7 @@ async function bestEffortGrantCount(idp) {
3215
3421
  }
3216
3422
  }
3217
3423
  async function runHealth(args) {
3218
- const version = true ? "0.12.6" : "0.0.0";
3424
+ const version = true ? "0.13.0" : "0.0.0";
3219
3425
  const auth = loadAuth();
3220
3426
  if (!auth) {
3221
3427
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -3278,7 +3484,7 @@ async function runHealth(args) {
3278
3484
  throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
3279
3485
  }
3280
3486
  }
3281
- var healthCommand = defineCommand31({
3487
+ var healthCommand = defineCommand32({
3282
3488
  meta: {
3283
3489
  name: "health",
3284
3490
  description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
@@ -3296,8 +3502,8 @@ var healthCommand = defineCommand31({
3296
3502
  });
3297
3503
 
3298
3504
  // src/commands/workflows.ts
3299
- import { defineCommand as defineCommand32 } from "citty";
3300
- import consola26 from "consola";
3505
+ import { defineCommand as defineCommand33 } from "citty";
3506
+ import consola27 from "consola";
3301
3507
 
3302
3508
  // src/guides/index.ts
3303
3509
  var guides = [
@@ -3347,7 +3553,7 @@ var guides = [
3347
3553
  ];
3348
3554
 
3349
3555
  // src/commands/workflows.ts
3350
- var workflowsCommand = defineCommand32({
3556
+ var workflowsCommand = defineCommand33({
3351
3557
  meta: {
3352
3558
  name: "workflows",
3353
3559
  description: "Discover workflow guides"
@@ -3368,7 +3574,7 @@ var workflowsCommand = defineCommand32({
3368
3574
  if (args.id) {
3369
3575
  const guide = guides.find((g) => g.id === String(args.id));
3370
3576
  if (!guide) {
3371
- consola26.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
3577
+ consola27.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
3372
3578
  throw new CliError(`Guide not found: ${args.id}`);
3373
3579
  }
3374
3580
  if (args.json) {
@@ -3417,10 +3623,10 @@ if (shellRewrite) {
3417
3623
  if (shellRewrite.action === "rewrite") {
3418
3624
  process.argv = shellRewrite.argv;
3419
3625
  } else if (shellRewrite.action === "version") {
3420
- console.log(`ape-shell ${"0.12.6"} (OpenApe DDISA shell wrapper)`);
3626
+ console.log(`ape-shell ${"0.13.0"} (OpenApe DDISA shell wrapper)`);
3421
3627
  process.exit(0);
3422
3628
  } else if (shellRewrite.action === "help") {
3423
- console.log(`ape-shell ${"0.12.6"} \u2014 OpenApe DDISA shell wrapper`);
3629
+ console.log(`ape-shell ${"0.13.0"} \u2014 OpenApe DDISA shell wrapper`);
3424
3630
  console.log("");
3425
3631
  console.log("Usage:");
3426
3632
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -3444,7 +3650,7 @@ if (shellRewrite) {
3444
3650
  }
3445
3651
  }
3446
3652
  var debug = process.argv.includes("--debug");
3447
- var grantsCommand = defineCommand33({
3653
+ var grantsCommand = defineCommand34({
3448
3654
  meta: {
3449
3655
  name: "grants",
3450
3656
  description: "Grant management"
@@ -3465,7 +3671,7 @@ var grantsCommand = defineCommand33({
3465
3671
  "delegation-revoke": delegationRevokeCommand
3466
3672
  }
3467
3673
  });
3468
- var configCommand = defineCommand33({
3674
+ var configCommand = defineCommand34({
3469
3675
  meta: {
3470
3676
  name: "config",
3471
3677
  description: "Configuration management"
@@ -3475,10 +3681,10 @@ var configCommand = defineCommand33({
3475
3681
  set: configSetCommand
3476
3682
  }
3477
3683
  });
3478
- var main = defineCommand33({
3684
+ var main = defineCommand34({
3479
3685
  meta: {
3480
3686
  name: "apes",
3481
- version: "0.12.6",
3687
+ version: "0.13.0",
3482
3688
  description: "Unified CLI for OpenApe"
3483
3689
  },
3484
3690
  subCommands: {
@@ -3493,6 +3699,7 @@ var main = defineCommand33({
3493
3699
  grants: grantsCommand,
3494
3700
  admin: adminCommand,
3495
3701
  run: runCommand,
3702
+ proxy: proxyCommand,
3496
3703
  explain: explainCommand,
3497
3704
  adapter: adapterCommand,
3498
3705
  config: configCommand,
@@ -3506,13 +3713,13 @@ runMain(main).catch((err) => {
3506
3713
  process.exit(err.exitCode);
3507
3714
  }
3508
3715
  if (err instanceof CliError) {
3509
- consola27.error(err.message);
3716
+ consola28.error(err.message);
3510
3717
  process.exit(err.exitCode);
3511
3718
  }
3512
3719
  if (debug) {
3513
- consola27.error(err);
3720
+ consola28.error(err);
3514
3721
  } else {
3515
- consola27.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
3722
+ consola28.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
3516
3723
  }
3517
3724
  process.exit(1);
3518
3725
  });