@openape/apes 0.10.0 → 0.11.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
@@ -136,6 +136,11 @@ async function resolveLoginInputs(flags) {
136
136
  consola.info(`Using email from ${keyPath}.pub comment: ${email}`);
137
137
  }
138
138
  }
139
+ if (process.env.APES_IDP && process.env.GRAPES_IDP) {
140
+ consola.warn(
141
+ "Both APES_IDP and GRAPES_IDP are set \u2014 using APES_IDP. GRAPES_IDP is deprecated and will be removed in a future release."
142
+ );
143
+ }
139
144
  let idp;
140
145
  if (flags.idp) {
141
146
  idp = flags.idp;
@@ -143,6 +148,9 @@ async function resolveLoginInputs(flags) {
143
148
  idp = process.env.APES_IDP;
144
149
  } else if (process.env.GRAPES_IDP) {
145
150
  idp = process.env.GRAPES_IDP;
151
+ consola.warn(
152
+ "GRAPES_IDP is deprecated, use APES_IDP instead. GRAPES_IDP support will be removed in a future release."
153
+ );
146
154
  } else if (config.defaults?.idp) {
147
155
  idp = config.defaults.idp;
148
156
  } else if (email && email.includes("@")) {
@@ -161,7 +169,7 @@ async function resolveLoginInputs(flags) {
161
169
 
162
170
  // src/commands/auth/login.ts
163
171
  var CALLBACK_PORT = 9876;
164
- var CLIENT_ID = "grapes-cli";
172
+ var CLIENT_ID = "apes-cli";
165
173
  var loginCommand = defineCommand({
166
174
  meta: {
167
175
  name: "login",
@@ -1064,6 +1072,59 @@ var revokeCommand = defineCommand11({
1064
1072
  import { execFileSync } from "child_process";
1065
1073
  import { defineCommand as defineCommand12 } from "citty";
1066
1074
  import consola12 from "consola";
1075
+
1076
+ // src/grant-poll.ts
1077
+ function getPollIntervalSeconds() {
1078
+ const envValue = process.env.APES_GRANT_POLL_INTERVAL;
1079
+ if (envValue) {
1080
+ const n = Number(envValue);
1081
+ if (Number.isFinite(n) && n > 0)
1082
+ return Math.floor(n);
1083
+ }
1084
+ const cfg = loadConfig();
1085
+ const cfgValue = cfg.defaults?.grant_poll_interval_seconds;
1086
+ if (cfgValue) {
1087
+ const n = Number(cfgValue);
1088
+ if (Number.isFinite(n) && n > 0)
1089
+ return Math.floor(n);
1090
+ }
1091
+ return 10;
1092
+ }
1093
+ function getPollMaxMinutes() {
1094
+ const envValue = process.env.APES_GRANT_POLL_MAX_MINUTES;
1095
+ if (envValue) {
1096
+ const n = Number(envValue);
1097
+ if (Number.isFinite(n) && n > 0)
1098
+ return Math.floor(n);
1099
+ }
1100
+ const cfg = loadConfig();
1101
+ const cfgValue = cfg.defaults?.grant_poll_max_minutes;
1102
+ if (cfgValue) {
1103
+ const n = Number(cfgValue);
1104
+ if (Number.isFinite(n) && n > 0)
1105
+ return Math.floor(n);
1106
+ }
1107
+ return 5;
1108
+ }
1109
+ async function pollGrantUntilResolved(idp, grantId) {
1110
+ const grantsEndpoint = await getGrantsEndpoint(idp);
1111
+ const intervalSec = getPollIntervalSeconds();
1112
+ const maxMinutes = getPollMaxMinutes();
1113
+ const maxMs = maxMinutes * 6e4;
1114
+ const intervalMs = intervalSec * 1e3;
1115
+ const start = Date.now();
1116
+ while (Date.now() - start < maxMs) {
1117
+ const grant = await apiFetch(`${grantsEndpoint}/${grantId}`);
1118
+ if (grant.status === "approved")
1119
+ return { kind: "approved" };
1120
+ if (grant.status === "denied" || grant.status === "revoked" || grant.status === "used")
1121
+ return { kind: "terminal", status: grant.status };
1122
+ await new Promise((r) => setTimeout(r, intervalMs));
1123
+ }
1124
+ return { kind: "timeout" };
1125
+ }
1126
+
1127
+ // src/commands/grants/run.ts
1067
1128
  var runGrantCommand = defineCommand12({
1068
1129
  meta: {
1069
1130
  name: "run",
@@ -1079,6 +1140,11 @@ var runGrantCommand = defineCommand12({
1079
1140
  type: "string",
1080
1141
  description: "Path to escapes binary (audience=escapes only)",
1081
1142
  default: "escapes"
1143
+ },
1144
+ wait: {
1145
+ type: "boolean",
1146
+ description: "If the grant is pending, block and poll until approved (or denied/revoked/used/timeout). Reuses APES_GRANT_POLL_INTERVAL / APES_GRANT_POLL_MAX_MINUTES knobs.",
1147
+ default: false
1082
1148
  }
1083
1149
  },
1084
1150
  async run({ args }) {
@@ -1086,9 +1152,29 @@ var runGrantCommand = defineCommand12({
1086
1152
  if (!idp)
1087
1153
  throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1088
1154
  const grantsUrl = await getGrantsEndpoint(idp);
1089
- const grant = await apiFetch(`${grantsUrl}/${args.id}`);
1090
- if (grant.status === "pending")
1091
- throw new CliError(`Grant ${grant.id} is still pending. Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
1155
+ let grant = await apiFetch(`${grantsUrl}/${args.id}`);
1156
+ if (grant.status === "pending") {
1157
+ if (!args.wait) {
1158
+ throw new CliError(
1159
+ `Grant ${grant.id} is still pending. Approve at: ${idp}/grant-approval?grant_id=${grant.id}`
1160
+ );
1161
+ }
1162
+ const maxMinutes = getPollMaxMinutes();
1163
+ consola12.info(`Waiting for grant ${grant.id} approval (up to ${maxMinutes} minute${maxMinutes === 1 ? "" : "s"})...`);
1164
+ const outcome = await pollGrantUntilResolved(idp, grant.id);
1165
+ if (outcome.kind === "timeout") {
1166
+ throw new CliError(
1167
+ `Grant ${grant.id} approval timed out after ${maxMinutes} minute${maxMinutes === 1 ? "" : "s"}. Re-run after approval, or extend the timeout via APES_GRANT_POLL_MAX_MINUTES.`
1168
+ );
1169
+ }
1170
+ if (outcome.kind === "terminal") {
1171
+ throw new CliError(
1172
+ `Grant ${grant.id} resolved to ${outcome.status}. Request a new one.`
1173
+ );
1174
+ }
1175
+ grant = await apiFetch(`${grantsUrl}/${args.id}`);
1176
+ consola12.info(`Grant ${grant.id} approved \u2014 continuing`);
1177
+ }
1092
1178
  if (grant.status === "denied" || grant.status === "revoked")
1093
1179
  throw new CliError(`Grant ${grant.id} is ${grant.status}. Request a new one.`);
1094
1180
  if (grant.status === "used")
@@ -1940,38 +2026,6 @@ function getUserMode() {
1940
2026
  return "human";
1941
2027
  return "agent";
1942
2028
  }
1943
- function getPollIntervalSeconds() {
1944
- const envValue = process.env.APES_GRANT_POLL_INTERVAL;
1945
- if (envValue) {
1946
- const n = Number(envValue);
1947
- if (Number.isFinite(n) && n > 0)
1948
- return Math.floor(n);
1949
- }
1950
- const cfg = loadConfig();
1951
- const cfgValue = cfg.defaults?.grant_poll_interval_seconds;
1952
- if (cfgValue) {
1953
- const n = Number(cfgValue);
1954
- if (Number.isFinite(n) && n > 0)
1955
- return Math.floor(n);
1956
- }
1957
- return 10;
1958
- }
1959
- function getPollMaxMinutes() {
1960
- const envValue = process.env.APES_GRANT_POLL_MAX_MINUTES;
1961
- if (envValue) {
1962
- const n = Number(envValue);
1963
- if (Number.isFinite(n) && n > 0)
1964
- return Math.floor(n);
1965
- }
1966
- const cfg = loadConfig();
1967
- const cfgValue = cfg.defaults?.grant_poll_max_minutes;
1968
- if (cfgValue) {
1969
- const n = Number(cfgValue);
1970
- if (Number.isFinite(n) && n > 0)
1971
- return Math.floor(n);
1972
- }
1973
- return 5;
1974
- }
1975
2029
  function getAsyncExitCode() {
1976
2030
  const envValue = process.env.APES_ASYNC_EXIT_CODE;
1977
2031
  if (envValue !== void 0 && envValue !== "") {
@@ -2003,17 +2057,25 @@ function printPendingGrantInfo(grant, idp) {
2003
2057
  console.log(" this grant without re-approval on the next invocation.");
2004
2058
  return;
2005
2059
  }
2006
- const pollSec = getPollIntervalSeconds();
2007
2060
  const maxMin = getPollMaxMinutes();
2008
2061
  consola19.success(`Grant ${grant.id} created (pending approval)`);
2009
2062
  console.log(` Approve: ${approveUrl}`);
2010
2063
  console.log(` Status: ${statusCmd} [--json]`);
2011
- console.log(` Execute: ${executeCmd}`);
2064
+ console.log(` Execute: ${executeCmd} --wait`);
2065
+ console.log("");
2066
+ console.log(" For agents:");
2067
+ console.log(` 1. Tell the user about the pending grant and the approve URL above.`);
2068
+ console.log(` 2. Run \`${executeCmd} --wait\`. This blocks up to ${maxMin} minutes`);
2069
+ console.log(` until the user approves (or denies/timeout) and then executes`);
2070
+ console.log(` the command in a single step. The CLI handles the polling loop`);
2071
+ console.log(` internally \u2014 you do not need to poll the status yourself.`);
2072
+ console.log(` 3. Exit 0 means approved + executed; stdout is the command output.`);
2073
+ console.log(` Exit 75 (pending) only appears if you accidentally call this`);
2074
+ console.log(` without --wait. Any other non-zero exit means denied, revoked,`);
2075
+ console.log(` used, or timeout \u2014 report the reason to the user.`);
2012
2076
  console.log("");
2013
- console.log(` For agents: poll \`${statusCmd} --json\` every ${pollSec}s, wait up to ${maxMin} minutes.`);
2014
- console.log(` When .status == "approved", run \`${executeCmd}\` to execute.`);
2015
- console.log(` On "denied" or "revoked", stop and report to the user.`);
2016
- console.log(` On timeout, stop and notify the user that approval has not happened.`);
2077
+ console.log(' Note: exit code 75 (EX_TEMPFAIL) from this command means "pending,');
2078
+ console.log(' retry later" \u2014 do not abort your workflow, follow the steps above.');
2017
2079
  console.log("");
2018
2080
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
2019
2081
  console.log(" grant be reused on subsequent invocations without re-approval.");
@@ -2626,7 +2688,7 @@ var mcpCommand = defineCommand26({
2626
2688
  if (transport !== "stdio" && transport !== "sse") {
2627
2689
  throw new Error('Transport must be "stdio" or "sse"');
2628
2690
  }
2629
- const { startMcpServer } = await import("./server-N3DPYYBL.js");
2691
+ const { startMcpServer } = await import("./server-YNJ5SURM.js");
2630
2692
  await startMcpServer(transport, port);
2631
2693
  }
2632
2694
  });
@@ -3118,7 +3180,7 @@ async function bestEffortGrantCount(idp) {
3118
3180
  }
3119
3181
  }
3120
3182
  async function runHealth(args) {
3121
- const version = true ? "0.10.0" : "0.0.0";
3183
+ const version = true ? "0.11.0" : "0.0.0";
3122
3184
  const auth = loadAuth();
3123
3185
  if (!auth) {
3124
3186
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -3320,10 +3382,10 @@ if (shellRewrite) {
3320
3382
  if (shellRewrite.action === "rewrite") {
3321
3383
  process.argv = shellRewrite.argv;
3322
3384
  } else if (shellRewrite.action === "version") {
3323
- console.log(`ape-shell ${"0.10.0"} (OpenApe DDISA shell wrapper)`);
3385
+ console.log(`ape-shell ${"0.11.0"} (OpenApe DDISA shell wrapper)`);
3324
3386
  process.exit(0);
3325
3387
  } else if (shellRewrite.action === "help") {
3326
- console.log(`ape-shell ${"0.10.0"} \u2014 OpenApe DDISA shell wrapper`);
3388
+ console.log(`ape-shell ${"0.11.0"} \u2014 OpenApe DDISA shell wrapper`);
3327
3389
  console.log("");
3328
3390
  console.log("Usage:");
3329
3391
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -3338,7 +3400,7 @@ if (shellRewrite) {
3338
3400
  console.log(" --help, -h Show this help message");
3339
3401
  process.exit(0);
3340
3402
  } else if (shellRewrite.action === "interactive") {
3341
- const { runInteractiveShell } = await import("./orchestrator-QL3AT67U.js");
3403
+ const { runInteractiveShell } = await import("./orchestrator-U5TZ2KXF.js");
3342
3404
  await runInteractiveShell();
3343
3405
  process.exit(0);
3344
3406
  } else {
@@ -3381,7 +3443,7 @@ var configCommand = defineCommand33({
3381
3443
  var main = defineCommand33({
3382
3444
  meta: {
3383
3445
  name: "apes",
3384
- version: "0.10.0",
3446
+ version: "0.11.0",
3385
3447
  description: "Unified CLI for OpenApe"
3386
3448
  },
3387
3449
  subCommands: {