@openape/apes 0.10.0 → 0.10.1

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
@@ -1064,6 +1064,59 @@ var revokeCommand = defineCommand11({
1064
1064
  import { execFileSync } from "child_process";
1065
1065
  import { defineCommand as defineCommand12 } from "citty";
1066
1066
  import consola12 from "consola";
1067
+
1068
+ // src/grant-poll.ts
1069
+ function getPollIntervalSeconds() {
1070
+ const envValue = process.env.APES_GRANT_POLL_INTERVAL;
1071
+ if (envValue) {
1072
+ const n = Number(envValue);
1073
+ if (Number.isFinite(n) && n > 0)
1074
+ return Math.floor(n);
1075
+ }
1076
+ const cfg = loadConfig();
1077
+ const cfgValue = cfg.defaults?.grant_poll_interval_seconds;
1078
+ if (cfgValue) {
1079
+ const n = Number(cfgValue);
1080
+ if (Number.isFinite(n) && n > 0)
1081
+ return Math.floor(n);
1082
+ }
1083
+ return 10;
1084
+ }
1085
+ function getPollMaxMinutes() {
1086
+ const envValue = process.env.APES_GRANT_POLL_MAX_MINUTES;
1087
+ if (envValue) {
1088
+ const n = Number(envValue);
1089
+ if (Number.isFinite(n) && n > 0)
1090
+ return Math.floor(n);
1091
+ }
1092
+ const cfg = loadConfig();
1093
+ const cfgValue = cfg.defaults?.grant_poll_max_minutes;
1094
+ if (cfgValue) {
1095
+ const n = Number(cfgValue);
1096
+ if (Number.isFinite(n) && n > 0)
1097
+ return Math.floor(n);
1098
+ }
1099
+ return 5;
1100
+ }
1101
+ async function pollGrantUntilResolved(idp, grantId) {
1102
+ const grantsEndpoint = await getGrantsEndpoint(idp);
1103
+ const intervalSec = getPollIntervalSeconds();
1104
+ const maxMinutes = getPollMaxMinutes();
1105
+ const maxMs = maxMinutes * 6e4;
1106
+ const intervalMs = intervalSec * 1e3;
1107
+ const start = Date.now();
1108
+ while (Date.now() - start < maxMs) {
1109
+ const grant = await apiFetch(`${grantsEndpoint}/${grantId}`);
1110
+ if (grant.status === "approved")
1111
+ return { kind: "approved" };
1112
+ if (grant.status === "denied" || grant.status === "revoked" || grant.status === "used")
1113
+ return { kind: "terminal", status: grant.status };
1114
+ await new Promise((r) => setTimeout(r, intervalMs));
1115
+ }
1116
+ return { kind: "timeout" };
1117
+ }
1118
+
1119
+ // src/commands/grants/run.ts
1067
1120
  var runGrantCommand = defineCommand12({
1068
1121
  meta: {
1069
1122
  name: "run",
@@ -1079,6 +1132,11 @@ var runGrantCommand = defineCommand12({
1079
1132
  type: "string",
1080
1133
  description: "Path to escapes binary (audience=escapes only)",
1081
1134
  default: "escapes"
1135
+ },
1136
+ wait: {
1137
+ type: "boolean",
1138
+ 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.",
1139
+ default: false
1082
1140
  }
1083
1141
  },
1084
1142
  async run({ args }) {
@@ -1086,9 +1144,29 @@ var runGrantCommand = defineCommand12({
1086
1144
  if (!idp)
1087
1145
  throw new CliError("No IdP URL configured. Run `apes login` first or pass --idp.");
1088
1146
  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}`);
1147
+ let grant = await apiFetch(`${grantsUrl}/${args.id}`);
1148
+ if (grant.status === "pending") {
1149
+ if (!args.wait) {
1150
+ throw new CliError(
1151
+ `Grant ${grant.id} is still pending. Approve at: ${idp}/grant-approval?grant_id=${grant.id}`
1152
+ );
1153
+ }
1154
+ const maxMinutes = getPollMaxMinutes();
1155
+ consola12.info(`Waiting for grant ${grant.id} approval (up to ${maxMinutes} minute${maxMinutes === 1 ? "" : "s"})...`);
1156
+ const outcome = await pollGrantUntilResolved(idp, grant.id);
1157
+ if (outcome.kind === "timeout") {
1158
+ throw new CliError(
1159
+ `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.`
1160
+ );
1161
+ }
1162
+ if (outcome.kind === "terminal") {
1163
+ throw new CliError(
1164
+ `Grant ${grant.id} resolved to ${outcome.status}. Request a new one.`
1165
+ );
1166
+ }
1167
+ grant = await apiFetch(`${grantsUrl}/${args.id}`);
1168
+ consola12.info(`Grant ${grant.id} approved \u2014 continuing`);
1169
+ }
1092
1170
  if (grant.status === "denied" || grant.status === "revoked")
1093
1171
  throw new CliError(`Grant ${grant.id} is ${grant.status}. Request a new one.`);
1094
1172
  if (grant.status === "used")
@@ -1940,38 +2018,6 @@ function getUserMode() {
1940
2018
  return "human";
1941
2019
  return "agent";
1942
2020
  }
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
2021
  function getAsyncExitCode() {
1976
2022
  const envValue = process.env.APES_ASYNC_EXIT_CODE;
1977
2023
  if (envValue !== void 0 && envValue !== "") {
@@ -2003,17 +2049,25 @@ function printPendingGrantInfo(grant, idp) {
2003
2049
  console.log(" this grant without re-approval on the next invocation.");
2004
2050
  return;
2005
2051
  }
2006
- const pollSec = getPollIntervalSeconds();
2007
2052
  const maxMin = getPollMaxMinutes();
2008
2053
  consola19.success(`Grant ${grant.id} created (pending approval)`);
2009
2054
  console.log(` Approve: ${approveUrl}`);
2010
2055
  console.log(` Status: ${statusCmd} [--json]`);
2011
- console.log(` Execute: ${executeCmd}`);
2056
+ console.log(` Execute: ${executeCmd} --wait`);
2057
+ console.log("");
2058
+ console.log(" For agents:");
2059
+ console.log(` 1. Tell the user about the pending grant and the approve URL above.`);
2060
+ console.log(` 2. Run \`${executeCmd} --wait\`. This blocks up to ${maxMin} minutes`);
2061
+ console.log(` until the user approves (or denies/timeout) and then executes`);
2062
+ console.log(` the command in a single step. The CLI handles the polling loop`);
2063
+ console.log(` internally \u2014 you do not need to poll the status yourself.`);
2064
+ console.log(` 3. Exit 0 means approved + executed; stdout is the command output.`);
2065
+ console.log(` Exit 75 (pending) only appears if you accidentally call this`);
2066
+ console.log(` without --wait. Any other non-zero exit means denied, revoked,`);
2067
+ console.log(` used, or timeout \u2014 report the reason to the user.`);
2012
2068
  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.`);
2069
+ console.log(' Note: exit code 75 (EX_TEMPFAIL) from this command means "pending,');
2070
+ console.log(' retry later" \u2014 do not abort your workflow, follow the steps above.');
2017
2071
  console.log("");
2018
2072
  console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
2019
2073
  console.log(" grant be reused on subsequent invocations without re-approval.");
@@ -2626,7 +2680,7 @@ var mcpCommand = defineCommand26({
2626
2680
  if (transport !== "stdio" && transport !== "sse") {
2627
2681
  throw new Error('Transport must be "stdio" or "sse"');
2628
2682
  }
2629
- const { startMcpServer } = await import("./server-N3DPYYBL.js");
2683
+ const { startMcpServer } = await import("./server-GTATJDYX.js");
2630
2684
  await startMcpServer(transport, port);
2631
2685
  }
2632
2686
  });
@@ -3118,7 +3172,7 @@ async function bestEffortGrantCount(idp) {
3118
3172
  }
3119
3173
  }
3120
3174
  async function runHealth(args) {
3121
- const version = true ? "0.10.0" : "0.0.0";
3175
+ const version = true ? "0.10.1" : "0.0.0";
3122
3176
  const auth = loadAuth();
3123
3177
  if (!auth) {
3124
3178
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -3320,10 +3374,10 @@ if (shellRewrite) {
3320
3374
  if (shellRewrite.action === "rewrite") {
3321
3375
  process.argv = shellRewrite.argv;
3322
3376
  } else if (shellRewrite.action === "version") {
3323
- console.log(`ape-shell ${"0.10.0"} (OpenApe DDISA shell wrapper)`);
3377
+ console.log(`ape-shell ${"0.10.1"} (OpenApe DDISA shell wrapper)`);
3324
3378
  process.exit(0);
3325
3379
  } else if (shellRewrite.action === "help") {
3326
- console.log(`ape-shell ${"0.10.0"} \u2014 OpenApe DDISA shell wrapper`);
3380
+ console.log(`ape-shell ${"0.10.1"} \u2014 OpenApe DDISA shell wrapper`);
3327
3381
  console.log("");
3328
3382
  console.log("Usage:");
3329
3383
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -3381,7 +3435,7 @@ var configCommand = defineCommand33({
3381
3435
  var main = defineCommand33({
3382
3436
  meta: {
3383
3437
  name: "apes",
3384
- version: "0.10.0",
3438
+ version: "0.10.1",
3385
3439
  description: "Unified CLI for OpenApe"
3386
3440
  },
3387
3441
  subCommands: {