@openape/apes 0.30.0 → 0.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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_DIR
4
- } from "./chunk-IDPV5SNB.js";
4
+ } from "./chunk-OBF7IMQ2.js";
5
5
 
6
6
  // src/auth-lock.ts
7
7
  import { open, rm, stat } from "fs/promises";
@@ -38,4 +38,4 @@ export {
38
38
  acquireAuthLock,
39
39
  releaseAuthLock
40
40
  };
41
- //# sourceMappingURL=auth-lock-GPLHRXOM.js.map
41
+ //# sourceMappingURL=auth-lock-4IRWI3ED.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadConfig
4
- } from "./chunk-IDPV5SNB.js";
4
+ } from "./chunk-OBF7IMQ2.js";
5
5
 
6
6
  // src/notifications.ts
7
7
  import { spawn } from "child_process";
@@ -77,4 +77,4 @@ export {
77
77
  isApesSelfDispatch,
78
78
  checkSudoRejection
79
79
  };
80
- //# sourceMappingURL=chunk-EDYICCBC.js.map
80
+ //# sourceMappingURL=chunk-3COOEDPF.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getGenericAuditLogPath
4
- } from "./chunk-IDPV5SNB.js";
4
+ } from "./chunk-OBF7IMQ2.js";
5
5
 
6
6
  // src/shapes/adapters.ts
7
7
  import { createHash } from "crypto";
@@ -1212,4 +1212,4 @@ export {
1212
1212
  buildExactCommandGrantRequest,
1213
1213
  buildStructuredCliGrantRequest
1214
1214
  };
1215
- //# sourceMappingURL=chunk-IT6T6AKX.js.map
1215
+ //# sourceMappingURL=chunk-DYSFQ26B.js.map
@@ -5,7 +5,7 @@ import {
5
5
  loadAuth,
6
6
  loadConfig,
7
7
  saveAuth
8
- } from "./chunk-IDPV5SNB.js";
8
+ } from "./chunk-OBF7IMQ2.js";
9
9
 
10
10
  // src/http.ts
11
11
  import consola from "consola";
@@ -104,7 +104,7 @@ async function refreshOAuthToken() {
104
104
  const auth = loadAuth();
105
105
  if (!auth?.refresh_token)
106
106
  return null;
107
- const { acquireAuthLock, releaseAuthLock } = await import("./auth-lock-GPLHRXOM.js");
107
+ const { acquireAuthLock, releaseAuthLock } = await import("./auth-lock-4IRWI3ED.js");
108
108
  const lock = await acquireAuthLock({ timeoutMs: 5e3 });
109
109
  if (!lock) {
110
110
  return getAuthToken();
@@ -217,4 +217,4 @@ export {
217
217
  ensureFreshToken,
218
218
  apiFetch
219
219
  };
220
- //# sourceMappingURL=chunk-7NUT2PFT.js.map
220
+ //# sourceMappingURL=chunk-N3THIFIS.js.map
@@ -23,7 +23,24 @@ function loadAuth() {
23
23
  }
24
24
  function saveAuth(data) {
25
25
  ensureDir();
26
- writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
26
+ let extra = {};
27
+ if (existsSync(AUTH_FILE)) {
28
+ try {
29
+ const raw = readFileSync(AUTH_FILE, "utf-8");
30
+ if (raw.trim()) {
31
+ const prev = JSON.parse(raw);
32
+ for (const key of Object.keys(prev)) {
33
+ if (!(key in data)) {
34
+ extra[key] = prev[key];
35
+ }
36
+ }
37
+ }
38
+ } catch {
39
+ extra = {};
40
+ }
41
+ }
42
+ const merged = { ...extra, ...data };
43
+ writeFileSync(AUTH_FILE, JSON.stringify(merged, null, 2), { mode: 384 });
27
44
  }
28
45
  function clearAuth() {
29
46
  if (existsSync(AUTH_FILE)) {
@@ -169,4 +186,4 @@ export {
169
186
  getAuthToken,
170
187
  getRequesterIdentity
171
188
  };
172
- //# sourceMappingURL=chunk-IDPV5SNB.js.map
189
+ //# sourceMappingURL=chunk-OBF7IMQ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport interface AuthData {\n idp: string\n access_token: string\n refresh_token?: string\n email: string\n expires_at: number\n /**\n * Set by `apes login --key …` (and `apes agents spawn`), absolute\n * path to the Ed25519 key the agent signs challenges with. Lets\n * `@openape/cli-auth` refresh agent tokens in-process; see #259.\n */\n key_path?: string\n /**\n * Email of the human who owns this agent — written by\n * `apes agents spawn`. The chat-bridge reads it for owner-only\n * contact handshakes. Optional for human auth.json files.\n */\n owner_email?: string\n}\n\nexport interface ApesConfig {\n defaults?: {\n idp?: string\n approval?: string\n /**\n * Audience for the `apes run` async info block. `agent` (default)\n * emits verbose agent-facing instructions with a polling protocol;\n * `human` emits a short friendly block. Env var `APES_USER` wins.\n */\n user?: 'agent' | 'human'\n /**\n * Poll interval (seconds) embedded in the agent-mode instructions.\n * Default 10. Env var `APES_GRANT_POLL_INTERVAL` wins. Stored as a\n * string in TOML because the hand-rolled parser only handles quoted\n * values — casting to number happens at read time.\n */\n grant_poll_interval_seconds?: string\n /**\n * Maximum poll duration (minutes) embedded in the agent-mode\n * instructions. Default 5. Env var `APES_GRANT_POLL_MAX_MINUTES` wins.\n */\n grant_poll_max_minutes?: string\n /**\n * Exit code emitted by `apes run` / `ape-shell -c` when the async\n * default path creates a pending grant. Default `75` (`EX_TEMPFAIL`\n * from sysexits.h — \"temporary failure, retry later\"). Set to `0`\n * to restore the pre-0.10.0 exit-0 behaviour. Env var\n * `APES_ASYNC_EXIT_CODE` wins. Valid range 0–255.\n */\n async_exit_code?: string\n }\n agent?: {\n key?: string\n email?: string\n }\n notifications?: {\n pending_command?: string\n }\n /**\n * Generic-fallback mode: when `apes run -- <cli>` is called with a CLI\n * that has no registered shape, fall through to a synthetic adapter that\n * requests a single-use, forced-high-risk grant for the exact argv.\n * See `shapes/generic.ts`.\n */\n generic?: {\n /**\n * Master switch. Default `true` (permissive). Set to `false` to restore\n * the legacy \"No adapter found\" hard-fail. Stored as string in TOML\n * (hand-parser limitation) and parsed as `value !== 'false'` at read time.\n */\n enabled?: string\n /** Override the audit-log location. Default `~/.config/apes/generic-calls.log`. Tilde-expanded at read time. */\n audit_log?: string\n }\n}\n\nconst CONFIG_DIR = join(homedir(), '.config', 'apes')\nconst AUTH_FILE = join(CONFIG_DIR, 'auth.json')\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.toml')\n\nfunction ensureDir() {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true })\n }\n}\n\nexport function loadAuth(): AuthData | null {\n if (!existsSync(AUTH_FILE))\n return null\n try {\n return JSON.parse(readFileSync(AUTH_FILE, 'utf-8'))\n }\n catch {\n return null\n }\n}\n\nexport function saveAuth(data: AuthData): void {\n ensureDir()\n // Preserve unmodelled fields from any prior version of auth.json\n // (e.g. older installs of this package, or fields a sibling CLI\n // wrote). Symmetric with cli-auth's saveIdpAuth — see #257 for the\n // bridge crash-loop the bare overwrite caused before.\n let extra: Record<string, unknown> = {}\n if (existsSync(AUTH_FILE)) {\n try {\n const raw = readFileSync(AUTH_FILE, 'utf-8')\n if (raw.trim()) {\n const prev = JSON.parse(raw) as Record<string, unknown>\n for (const key of Object.keys(prev)) {\n if (!(key in (data as unknown as Record<string, unknown>))) {\n extra[key] = prev[key]\n }\n }\n }\n }\n catch {\n extra = {}\n }\n }\n const merged = { ...extra, ...data }\n writeFileSync(AUTH_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 })\n}\n\nexport function clearAuth(): void {\n if (existsSync(AUTH_FILE)) {\n writeFileSync(AUTH_FILE, '', { mode: 0o600 })\n }\n // Also wipe the [agent] section from config.toml so logout disables\n // auto-refresh. Preserves [defaults] so the IdP URL stays configured.\n if (existsSync(CONFIG_FILE)) {\n const existing = loadConfig()\n if (existing.agent) {\n const { agent: _removed, ...rest } = existing\n saveConfig(rest)\n }\n }\n}\n\nexport function loadConfig(): ApesConfig {\n if (!existsSync(CONFIG_FILE))\n return {}\n try {\n return parseTOML(readFileSync(CONFIG_FILE, 'utf-8'))\n }\n catch {\n return {}\n }\n}\n\nfunction parseTOML(content: string): ApesConfig {\n const config: ApesConfig = {}\n let section = ''\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#'))\n continue\n\n const sectionMatch = trimmed.match(/^\\[(.+)\\]$/)\n if (sectionMatch) {\n section = sectionMatch[1]!\n continue\n }\n\n // Accept quoted strings and bare booleans/tokens — the generic section\n // uses `enabled = false` (no quotes) which the quoted-only match would\n // silently drop.\n const kvQuoted = trimmed.match(/^(\\w+)\\s*=\\s*\"(.*)\"$/)\n const kvBare = trimmed.match(/^(\\w+)\\s*=\\s*([^\"\\s]\\S*)$/)\n const kvMatch = kvQuoted ?? kvBare\n if (kvMatch) {\n const [, key, value] = kvMatch\n if (section === 'defaults') {\n config.defaults = config.defaults || {}\n ;(config.defaults as Record<string, string>)[key!] = value!\n }\n else if (section === 'agent') {\n config.agent = config.agent || {}\n ;(config.agent as Record<string, string>)[key!] = value!\n }\n else if (section === 'notifications') {\n config.notifications = config.notifications || {}\n ;(config.notifications as Record<string, string>)[key!] = value!\n }\n else if (section === 'generic') {\n config.generic = config.generic || {}\n ;(config.generic as Record<string, string>)[key!] = value!\n }\n }\n }\n\n return config\n}\n\nexport function saveConfig(config: ApesConfig): void {\n ensureDir()\n const lines: string[] = []\n\n if (config.defaults) {\n lines.push('[defaults]')\n for (const [key, value] of Object.entries(config.defaults)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n if (config.agent) {\n lines.push('[agent]')\n for (const [key, value] of Object.entries(config.agent)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n if (config.notifications) {\n lines.push('[notifications]')\n for (const [key, value] of Object.entries(config.notifications)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n if (config.generic) {\n lines.push('[generic]')\n for (const [key, value] of Object.entries(config.generic)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n writeFileSync(CONFIG_FILE, lines.join('\\n'), { mode: 0o600 })\n}\n\n/**\n * Is generic-fallback enabled? Permissive default: `true` unless the user\n * explicitly sets `[generic] enabled = false`.\n */\nexport function isGenericFallbackEnabled(config?: ApesConfig): boolean {\n const cfg = config ?? loadConfig()\n const raw = cfg.generic?.enabled\n if (raw === undefined) return true\n return raw !== 'false'\n}\n\n/**\n * Resolve the audit-log path for generic calls, expanding `~` to `$HOME`.\n */\nexport function getGenericAuditLogPath(config?: ApesConfig): string {\n const cfg = config ?? loadConfig()\n const raw = cfg.generic?.audit_log\n const path = raw && raw.length > 0\n ? raw\n : join(homedir(), '.config', 'apes', 'generic-calls.log')\n return path.startsWith('~/')\n ? join(homedir(), path.slice(2))\n : path\n}\n\nexport function getIdpUrl(explicit?: string): string | null {\n if (explicit)\n return explicit\n if (process.env.APES_IDP)\n return process.env.APES_IDP\n\n const auth = loadAuth()\n if (auth?.idp)\n return auth.idp\n\n const config = loadConfig()\n if (config.defaults?.idp)\n return config.defaults.idp\n\n return null\n}\n\nexport function getAuthToken(): string | null {\n const auth = loadAuth()\n if (!auth)\n return null\n\n // Check expiry (with 30s buffer)\n if (auth.expires_at && Date.now() / 1000 > auth.expires_at - 30) {\n return null // expired\n }\n\n return auth.access_token\n}\n\nexport function getRequesterIdentity(): string | null {\n return loadAuth()?.email ?? null\n}\n\nexport { CONFIG_DIR, AUTH_FILE }\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  checkSudoRejection,
13
13
  isApesSelfDispatch,
14
14
  notifyGrantPending
15
- } from "./chunk-EDYICCBC.js";
15
+ } from "./chunk-3COOEDPF.js";
16
16
  import {
17
17
  GENERIC_OPERATION_ID,
18
18
  buildGenericResolved,
@@ -40,7 +40,7 @@ import {
40
40
  searchAdapters,
41
41
  verifyAndExecute,
42
42
  waitForGrantStatus
43
- } from "./chunk-IT6T6AKX.js";
43
+ } from "./chunk-DYSFQ26B.js";
44
44
  import {
45
45
  ApiError,
46
46
  apiFetch,
@@ -48,7 +48,7 @@ import {
48
48
  getAgentChallengeEndpoint,
49
49
  getDelegationsEndpoint,
50
50
  getGrantsEndpoint
51
- } from "./chunk-7NUT2PFT.js";
51
+ } from "./chunk-N3THIFIS.js";
52
52
  import {
53
53
  AUTH_FILE,
54
54
  CONFIG_DIR,
@@ -60,7 +60,7 @@ import {
60
60
  loadConfig,
61
61
  saveAuth,
62
62
  saveConfig
63
- } from "./chunk-IDPV5SNB.js";
63
+ } from "./chunk-OBF7IMQ2.js";
64
64
 
65
65
  // src/cli.ts
66
66
  import consola37 from "consola";
@@ -409,13 +409,14 @@ async function loginWithKey(idp, keyPath, agentEmail) {
409
409
  throw new CliError(`Authentication failed: ${await authResp.text()}`);
410
410
  }
411
411
  const { token, expires_in } = await authResp.json();
412
+ const absoluteKeyPath = resolvePath(keyPath.replace(/^~/, homedir2()));
412
413
  saveAuth({
413
414
  idp,
414
415
  access_token: token,
415
416
  email: agentEmail,
416
- expires_at: Math.floor(Date.now() / 1e3) + (expires_in || 3600)
417
+ expires_at: Math.floor(Date.now() / 1e3) + (expires_in || 3600),
418
+ key_path: absoluteKeyPath
417
419
  });
418
- const absoluteKeyPath = resolvePath(keyPath.replace(/^~/, homedir2()));
419
420
  const existingConfig = loadConfig();
420
421
  saveConfig({
421
422
  ...existingConfig,
@@ -2008,6 +2009,7 @@ function buildAgentAuthJson(input) {
2008
2009
  access_token: input.accessToken,
2009
2010
  email: input.email,
2010
2011
  expires_at: input.expiresAt,
2012
+ key_path: input.keyPath,
2011
2013
  owner_email: input.ownerEmail
2012
2014
  }, null, 2)}
2013
2015
  `;
@@ -2611,29 +2613,10 @@ set -euo pipefail
2611
2613
 
2612
2614
  export PATH="$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
2613
2615
 
2614
- # Refresh IdP token. Agents auth via SSH-key signing \u2014 the cached IdP
2615
- # token has no refresh_token and expires after ~1h. Re-running apes
2616
- # login re-signs against the registered key. Soft-fails to leave the
2617
- # bridge usable on cached token if the IdP is briefly unreachable.
2618
- if [ -f "$HOME/.config/apes/auth.json" ] && command -v apes >/dev/null 2>&1; then
2619
- agent_email=$(python3 -c "import json,os,sys
2620
- try: print(json.load(open(os.path.expanduser('~/.config/apes/auth.json')))['email'])
2621
- except Exception: sys.exit(1)" 2>/dev/null || true)
2622
- agent_idp=$(python3 -c "import json,os,sys
2623
- try: print(json.load(open(os.path.expanduser('~/.config/apes/auth.json')))['idp'])
2624
- except Exception: sys.exit(1)" 2>/dev/null || true)
2625
- if [ -n "$agent_email" ] && [ -n "$agent_idp" ]; then
2626
- # Capture full output so the failure mode is debuggable from logs
2627
- # without needing an interactive grant approval. apes-login should
2628
- # succeed with the SSH key under ~/.ssh/id_ed25519 written by spawn;
2629
- # if it doesn't, the cached token carries us until expiry (~1h) and
2630
- # the daemon will retry on the next launchd restart.
2631
- login_output=$(apes login "$agent_email" --idp "$agent_idp" 2>&1) || {
2632
- echo "warn: apes login failed for $agent_email; continuing with cached token" >&2
2633
- echo "$login_output" | sed 's/^/ apes-login: /' >&2
2634
- }
2635
- fi
2636
- fi
2616
+ # Token refresh is in-process via @openape/cli-auth's challenge-response
2617
+ # path (auth.json.key_path -> ~/.ssh/id_ed25519). No "apes login" needed
2618
+ # at boot \u2014 keeping start.sh slim avoids the rate-limit dance the old
2619
+ # refresh hit when KeepAlive crash-restarted the daemon every 1h.
2637
2620
 
2638
2621
  EXT_DIR="$HOME/.pi/agent/extensions"
2639
2622
  mkdir -p "$EXT_DIR"
@@ -2825,6 +2808,7 @@ and try again.`
2825
2808
  accessToken: token,
2826
2809
  email: registration.email,
2827
2810
  expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
2811
+ keyPath: `${homeDir}/.ssh/id_ed25519`,
2828
2812
  ownerEmail: auth.email
2829
2813
  });
2830
2814
  const includeClaudeHook = !args["no-claude-hook"];
@@ -4173,7 +4157,7 @@ var mcpCommand = defineCommand33({
4173
4157
  if (transport !== "stdio" && transport !== "sse") {
4174
4158
  throw new Error('Transport must be "stdio" or "sse"');
4175
4159
  }
4176
- const { startMcpServer } = await import("./server-43TJLWMB.js");
4160
+ const { startMcpServer } = await import("./server-TBXFWNUV.js");
4177
4161
  await startMcpServer(transport, port);
4178
4162
  }
4179
4163
  });
@@ -4811,7 +4795,7 @@ async function bestEffortGrantCount(idp) {
4811
4795
  }
4812
4796
  }
4813
4797
  async function runHealth(args) {
4814
- const version = true ? "0.30.0" : "0.0.0";
4798
+ const version = true ? "0.31.0" : "0.0.0";
4815
4799
  const auth = loadAuth();
4816
4800
  if (!auth) {
4817
4801
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -5084,10 +5068,10 @@ if (shellRewrite) {
5084
5068
  if (shellRewrite.action === "rewrite") {
5085
5069
  process.argv = shellRewrite.argv;
5086
5070
  } else if (shellRewrite.action === "version") {
5087
- console.log(`ape-shell ${"0.30.0"} (OpenApe DDISA shell wrapper)`);
5071
+ console.log(`ape-shell ${"0.31.0"} (OpenApe DDISA shell wrapper)`);
5088
5072
  process.exit(0);
5089
5073
  } else if (shellRewrite.action === "help") {
5090
- console.log(`ape-shell ${"0.30.0"} \u2014 OpenApe DDISA shell wrapper`);
5074
+ console.log(`ape-shell ${"0.31.0"} \u2014 OpenApe DDISA shell wrapper`);
5091
5075
  console.log("");
5092
5076
  console.log("Usage:");
5093
5077
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -5102,7 +5086,7 @@ if (shellRewrite) {
5102
5086
  console.log(" --help, -h Show this help message");
5103
5087
  process.exit(0);
5104
5088
  } else if (shellRewrite.action === "interactive") {
5105
- const { runInteractiveShell } = await import("./orchestrator-RWTALOSA.js");
5089
+ const { runInteractiveShell } = await import("./orchestrator-EH6V5ATG.js");
5106
5090
  await runInteractiveShell();
5107
5091
  process.exit(0);
5108
5092
  } else {
@@ -5145,7 +5129,7 @@ var configCommand = defineCommand45({
5145
5129
  var main = defineCommand45({
5146
5130
  meta: {
5147
5131
  name: "apes",
5148
- version: "0.30.0",
5132
+ version: "0.31.0",
5149
5133
  description: "Unified CLI for OpenApe"
5150
5134
  },
5151
5135
  subCommands: {
@@ -5191,16 +5175,16 @@ var NO_REFRESH_COMMANDS = /* @__PURE__ */ new Set([
5191
5175
  async function maybeRefreshAuth() {
5192
5176
  const sub = process.argv[2];
5193
5177
  if (!sub || NO_REFRESH_COMMANDS.has(sub)) return;
5194
- const { loadAuth: loadAuth2 } = await import("./config-JH2IEPIR.js");
5178
+ const { loadAuth: loadAuth2 } = await import("./config-MOB5DJ6H.js");
5195
5179
  if (!loadAuth2()) return;
5196
5180
  try {
5197
- const { ensureFreshToken } = await import("./http-JZT4XV5I.js");
5181
+ const { ensureFreshToken } = await import("./http-5F7FX4V7.js");
5198
5182
  await ensureFreshToken();
5199
5183
  } catch {
5200
5184
  }
5201
5185
  }
5202
5186
  await maybeRefreshAuth();
5203
- await maybeWarnStaleVersion("0.30.0").catch(() => {
5187
+ await maybeWarnStaleVersion("0.31.0").catch(() => {
5204
5188
  });
5205
5189
  runMain(main).catch((err) => {
5206
5190
  if (err instanceof CliExit) {