@levr-one/cli 0.11.10 → 0.12.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.
Files changed (26) hide show
  1. package/dist/{addHandler-DR6q5U_R.js → addHandler-QVX0ltyN.js} +27 -15
  2. package/dist/{appendHandler-KVsJMxpX.js → appendHandler-DtxLpPu6.js} +9 -9
  3. package/dist/{artifact-upload-BzLxue32.js → artifact-upload-BvBSevIx.js} +2 -2
  4. package/dist/{attachHandler-DQpK_pVa.js → attachHandler-DXZi2ILT.js} +9 -9
  5. package/dist/cli.js +38 -22
  6. package/dist/{connect-BgQmrL_0.js → connect-DVjgyfls.js} +3 -3
  7. package/dist/{currentHandler-u89l068v.js → currentHandler-QhCywr3S.js} +1 -1
  8. package/dist/{find-workspace-T4xuXwtS.js → find-workspace-Bl4rfr73.js} +1 -1
  9. package/dist/{importHandler-zw5GKb-S.js → importHandler-uFdalKtw.js} +6 -6
  10. package/dist/inspect-B8tG9g0q.js +1122 -0
  11. package/dist/inspect-BzBqSBO3.js +3 -0
  12. package/dist/{listHandler-YA-0RkwS.js → listHandler-BW92JLCl.js} +5 -5
  13. package/dist/{loginHandler-DE0L1ehF.js → loginHandler-C9Oi92IY.js} +15 -11
  14. package/dist/{logoutHandler-S4TFYFwz.js → logoutHandler-Cf5aI1wq.js} +2 -2
  15. package/dist/{pushHandler-DdNyVQ5t.js → pushHandler-CXFZt3SO.js} +8 -8
  16. package/dist/{resolve-token-7xb7kG7h.js → resolve-token-ByZq05Us.js} +2 -2
  17. package/dist/{resolve-workspace-CStHisUb.js → resolve-workspace-BbK3uYQc.js} +2 -2
  18. package/dist/{runHandler-BFDjtxbt.js → runHandler-itaAVn4U.js} +215 -25
  19. package/dist/{sdk-client-DYv-0CfA.js → sdk-client-B4FMd_Fa.js} +1 -1
  20. package/dist/{selectHandler-Cejme5nY.js → selectHandler-D6cvq60S.js} +5 -5
  21. package/dist/{statusHandler-BNiWlC7e.js → statusHandler-BQXn93-r.js} +3 -3
  22. package/dist/{token-refresh-DeusLK8H.js → token-refresh-CJB7Qlxy.js} +1 -1
  23. package/package.json +1 -1
  24. /package/dist/{env-CdwyPHGV.js → env-CvjMSRd7.js} +0 -0
  25. /package/dist/{sleep-BpJ39Ypm.js → sleep-CGBHjsj5.js} +0 -0
  26. /package/dist/{workspace-store-CnrxyYRB.js → workspace-store-Bmw104Qa.js} +0 -0
@@ -0,0 +1,3 @@
1
+ import { executeInspect, gatesInspectCommand, gatesInspectHandler } from "./inspect-B8tG9g0q.js";
2
+
3
+ export { gatesInspectHandler };
@@ -1,8 +1,8 @@
1
- import { authGetSitesV1, configureClient } from "./sdk-client-DYv-0CfA.js";
2
- import "./env-CdwyPHGV.js";
3
- import "./token-refresh-DeusLK8H.js";
4
- import { resolveToken } from "./resolve-token-7xb7kG7h.js";
5
- import { loadWorkspace } from "./workspace-store-CnrxyYRB.js";
1
+ import { authGetSitesV1, configureClient } from "./sdk-client-B4FMd_Fa.js";
2
+ import "./env-CvjMSRd7.js";
3
+ import "./token-refresh-CJB7Qlxy.js";
4
+ import { resolveToken } from "./resolve-token-ByZq05Us.js";
5
+ import { loadWorkspace } from "./workspace-store-Bmw104Qa.js";
6
6
 
7
7
  //#region src/commands/workspace/listHandler.ts
8
8
  async function listHandler() {
@@ -1,9 +1,9 @@
1
- import { configureClient } from "./sdk-client-DYv-0CfA.js";
2
- import { CLI_CLIENT_ID, getApiUrl, getAuthUrl, setSessionApiUrl, writeCredentials } from "./env-CdwyPHGV.js";
3
- import { saveWorkspace } from "./workspace-store-CnrxyYRB.js";
4
- import { autoSelectWorkspace, fetchSites } from "./resolve-workspace-CStHisUb.js";
5
- import { sleep } from "./sleep-BpJ39Ypm.js";
6
- import { findWorkspaceByKeyOrName, looksLikeUrlKey, resolveMcpUrl, scopedMcpUrl } from "./find-workspace-T4xuXwtS.js";
1
+ import { configureClient } from "./sdk-client-B4FMd_Fa.js";
2
+ import { CLI_CLIENT_ID, getApiUrl, getAuthUrl, setSessionApiUrl, writeCredentials } from "./env-CvjMSRd7.js";
3
+ import { saveWorkspace } from "./workspace-store-Bmw104Qa.js";
4
+ import { autoSelectWorkspace, fetchSites } from "./resolve-workspace-BbK3uYQc.js";
5
+ import { sleep } from "./sleep-CGBHjsj5.js";
6
+ import { findWorkspaceByKeyOrName, looksLikeUrlKey, resolveMcpUrl, scopedMcpUrl } from "./find-workspace-Bl4rfr73.js";
7
7
  import chalk from "chalk";
8
8
  import { createHash, randomBytes } from "node:crypto";
9
9
  import { createServer } from "node:http";
@@ -103,6 +103,10 @@ function startCallbackServer(options = {}) {
103
103
  resolveCode = resolve;
104
104
  rejectCode = reject;
105
105
  });
106
+ const shutdown = () => {
107
+ server.close();
108
+ server.closeAllConnections();
109
+ };
106
110
  const server = createServer((req, res) => {
107
111
  const url = new URL$1(req.url ?? "/", `http://127.0.0.1`);
108
112
  if (url.pathname === "/callback") {
@@ -113,7 +117,7 @@ function startCallbackServer(options = {}) {
113
117
  res.end(renderPage("error", "Authentication failed", "You can close this window.", error));
114
118
  clearTimeout(timer);
115
119
  rejectCode(/* @__PURE__ */ new Error(`OAuth error: ${error}`));
116
- server.close();
120
+ shutdown();
117
121
  return;
118
122
  }
119
123
  if (code) {
@@ -123,14 +127,14 @@ function startCallbackServer(options = {}) {
123
127
  res.end(renderPage("error", "Authentication failed", "You can close this window.", "Invalid state parameter."));
124
128
  clearTimeout(timer);
125
129
  rejectCode(/* @__PURE__ */ new Error("OAuth state mismatch (possible CSRF)"));
126
- server.close();
130
+ shutdown();
127
131
  return;
128
132
  }
129
133
  res.writeHead(200, { "Content-Type": "text/html" });
130
134
  res.end(renderPage("success", "Authentication successful!", "You can close this window and return to the terminal."));
131
135
  clearTimeout(timer);
132
136
  resolveCode({ code });
133
- server.close();
137
+ shutdown();
134
138
  return;
135
139
  }
136
140
  }
@@ -139,7 +143,7 @@ function startCallbackServer(options = {}) {
139
143
  });
140
144
  const timer = setTimeout(() => {
141
145
  rejectCode(/* @__PURE__ */ new Error("Authentication timed out. No callback received."));
142
- server.close();
146
+ shutdown();
143
147
  }, timeout);
144
148
  server.listen(0, "127.0.0.1", () => {
145
149
  const addr = server.address();
@@ -150,7 +154,7 @@ function startCallbackServer(options = {}) {
150
154
  code: codePromise,
151
155
  close: () => {
152
156
  clearTimeout(timer);
153
- server.close();
157
+ shutdown();
154
158
  }
155
159
  };
156
160
  }
@@ -1,5 +1,5 @@
1
- import { deleteCredentials, getPatToken } from "./env-CdwyPHGV.js";
2
- import { clearWorkspace } from "./workspace-store-CnrxyYRB.js";
1
+ import { deleteCredentials, getPatToken } from "./env-CvjMSRd7.js";
2
+ import { clearWorkspace } from "./workspace-store-Bmw104Qa.js";
3
3
 
4
4
  //#region src/commands/auth/logoutHandler.ts
5
5
  function logoutHandler() {
@@ -1,11 +1,11 @@
1
- import { client, configureClient, uploadImport } from "./sdk-client-DYv-0CfA.js";
2
- import { getApiUrl, getAutomationSourceIdOverride, getSourceOverride, getTeamId } from "./env-CdwyPHGV.js";
3
- import "./token-refresh-DeusLK8H.js";
4
- import { resolveToken } from "./resolve-token-7xb7kG7h.js";
5
- import "./workspace-store-CnrxyYRB.js";
6
- import { resolveWorkspace } from "./resolve-workspace-CStHisUb.js";
7
- import "./sleep-BpJ39Ypm.js";
8
- import { resolveArtifactPath, uploadArtifactFile } from "./artifact-upload-BzLxue32.js";
1
+ import { client, configureClient, uploadImport } from "./sdk-client-B4FMd_Fa.js";
2
+ import { getApiUrl, getAutomationSourceIdOverride, getSourceOverride, getTeamId } from "./env-CvjMSRd7.js";
3
+ import "./token-refresh-CJB7Qlxy.js";
4
+ import { resolveToken } from "./resolve-token-ByZq05Us.js";
5
+ import "./workspace-store-Bmw104Qa.js";
6
+ import { resolveWorkspace } from "./resolve-workspace-BbK3uYQc.js";
7
+ import "./sleep-CGBHjsj5.js";
8
+ import { resolveArtifactPath, uploadArtifactFile } from "./artifact-upload-BvBSevIx.js";
9
9
  import { readFileSync, statSync, writeFileSync } from "node:fs";
10
10
  import { basename, dirname, resolve } from "node:path";
11
11
  import { execSync } from "node:child_process";
@@ -1,5 +1,5 @@
1
- import { getApiUrl, getPatToken, readCredentials } from "./env-CdwyPHGV.js";
2
- import { isTokenExpired, refreshToken } from "./token-refresh-DeusLK8H.js";
1
+ import { getApiUrl, getPatToken, readCredentials } from "./env-CvjMSRd7.js";
2
+ import { isTokenExpired, refreshToken } from "./token-refresh-CJB7Qlxy.js";
3
3
 
4
4
  //#region src/auth/resolve-token.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { authGetSitesV1 } from "./sdk-client-DYv-0CfA.js";
2
- import { clearWorkspace, loadWorkspace, saveWorkspace } from "./workspace-store-CnrxyYRB.js";
1
+ import { authGetSitesV1 } from "./sdk-client-B4FMd_Fa.js";
2
+ import { clearWorkspace, loadWorkspace, saveWorkspace } from "./workspace-store-Bmw104Qa.js";
3
3
 
4
4
  //#region src/workspace/resolve-workspace.ts
5
5
  async function fetchSites() {
@@ -1,9 +1,10 @@
1
- import { client, configureClient } from "./sdk-client-DYv-0CfA.js";
2
- import "./env-CdwyPHGV.js";
3
- import "./token-refresh-DeusLK8H.js";
4
- import { resolveToken } from "./resolve-token-7xb7kG7h.js";
5
- import "./workspace-store-CnrxyYRB.js";
6
- import { resolveWorkspace } from "./resolve-workspace-CStHisUb.js";
1
+ import { executeInspect } from "./inspect-B8tG9g0q.js";
2
+ import { client, configureClient } from "./sdk-client-B4FMd_Fa.js";
3
+ import "./env-CvjMSRd7.js";
4
+ import "./token-refresh-CJB7Qlxy.js";
5
+ import { resolveToken } from "./resolve-token-ByZq05Us.js";
6
+ import "./workspace-store-Bmw104Qa.js";
7
+ import { resolveWorkspace } from "./resolve-workspace-BbK3uYQc.js";
7
8
  import { existsSync } from "node:fs";
8
9
  import { constants as constants$1 } from "node:os";
9
10
  import { spawnSync } from "node:child_process";
@@ -56,8 +57,7 @@ const issueGateVerificationReportGateResultsV1 = (options) => {
56
57
  * one the `report_gate_results` MCP tool takes):
57
58
  *
58
59
  * - each `local_commands[]` entry is run EXACTLY as returned, through
59
- * `/bin/bash -c` (the shell the qinetic-mcp executor uses — see
60
- * `chooseShell`), from the current directory — no rewriting and no
60
+ * `/bin/bash -c` (`chooseShell`), from the current directory — no rewriting and no
61
61
  * splitting. The server decided what to run; this reports what happened.
62
62
  * `levr gates run` prints each command first; there is no local
63
63
  * allowlist — the server validates every command it hands out
@@ -76,13 +76,18 @@ const issueGateVerificationReportGateResultsV1 = (options) => {
76
76
  * FAIL (0)`) hides the signals the server reads. Spawning the commands
77
77
  * from this process means no proxy ever sees them.
78
78
  *
79
- * Why not the qinetic-mcp executor: it splits `&&` chains and rewrites paths
80
- * for the Levr monorepo — the opposite of "exactly as returned". The CLI
81
- * shares its wire contract, not its code, and imports
82
- * no `@levr/*` value module (so the published bundle's inlined set is
83
- * unchanged). The documented no-install helper
84
- * (`packages/help-content/docs/gates/running-gates.mdx`) implements this same
85
- * contract; `gate-runner.helper.test.ts` holds the two to identical output.
79
+ * The qinetic-mcp executor used to split `&&` chains and rewrite paths for
80
+ * the Levr monorepo — the opposite of "exactly as returned" — and was
81
+ * retired (ENG-6175). The CLI reports each command whole.
82
+ *
83
+ * An INSPECT op (ENG-6179) is the one exception to "spawn it": its display
84
+ * command is `npx '@levr-one/cli@<spec>' gates inspect '<op json>'`, and
85
+ * spawning that would run whatever CLI version npm resolves for the pin,
86
+ * which may not have `gates inspect` at all. This CLI has it, so an inspect
87
+ * op runs IN PROCESS (`inspectOpOf` → `executeInspect`, through
88
+ * `@levr/shared`'s `GateOpSchema`, which the bundle inlines). The step
89
+ * still reports the command exactly as issued, with this run's exit code
90
+ * and output, so the server judges it the same way.
86
91
  */
87
92
  /** The server's `GATE_STEP_TAIL_LIMIT` (`@levr/shared`), in chars. */
88
93
  const GATE_STEP_TAIL_LIMIT = 4e3;
@@ -196,19 +201,168 @@ function toReportResult(command, outcome) {
196
201
  };
197
202
  }
198
203
  /**
204
+ * The display form the server renders for an inspect op:
205
+ * `npx '@levr-one/cli@<spec>' gates inspect '<json>'` (POSIX quoting, where a
206
+ * `'` inside is written `'\''`). Also accepts a bare `levr gates inspect`.
207
+ */
208
+ const INSPECT_DISPLAY_RE = /^\s*(?:npx(?:\s+--?[A-Za-z][\w-]*)*\s+(?:'@levr-one\/cli(?:@[^']*)?'|@levr-one\/cli(?:@\S*)?)|levr)\s+gates\s+inspect\s+'((?:[^']|'\\'')*)'\s*$/;
209
+ /**
210
+ * The inspect op JSON a local command carries, or `null` when it is not an
211
+ * inspect op. The envelope's `op` is authoritative; the display string is
212
+ * parsed only when the envelope is absent (an older server).
213
+ */
214
+ function inspectOpOf(command) {
215
+ const op = command.op?.op;
216
+ if (op && typeof op === "object") return op.op === "inspect" ? JSON.stringify(op) : null;
217
+ const match = INSPECT_DISPLAY_RE.exec(command.command);
218
+ if (!match) return null;
219
+ return (match[1] ?? "").replace(/'\\''/g, "'");
220
+ }
221
+ /**
222
+ * Where a command runs: inspect ops in this process, everything else in the
223
+ * shell `chooseShell` picks. Shown by `--dry-run` so a reader knows which
224
+ * printed `npx … gates inspect` lines never spawn anything.
225
+ */
226
+ function runsWhere(command) {
227
+ return inspectOpOf(command) === null ? "shell" : "in-process";
228
+ }
229
+ /**
230
+ * The server's approval for a command (`server_approved`, `confirm`,
231
+ * `denied`) and why, from the op envelope; `undefined` from an older server.
232
+ * Display only: nothing here asks for a confirmation yet (that is D10,
233
+ * ENG-6183), so a `confirm` command still runs.
234
+ */
235
+ function approvalOf(command) {
236
+ const approval = command.op?.["approval"];
237
+ if (typeof approval !== "string") return void 0;
238
+ const basis = command.op?.["approval_basis"];
239
+ return typeof basis === "string" ? {
240
+ approval,
241
+ approval_basis: basis
242
+ } : { approval };
243
+ }
244
+ /**
245
+ * True when at least one command must be spawned in a shell (anything that
246
+ * is not an inspect op), so a missing shell matters.
247
+ */
248
+ function needsShell(commands) {
249
+ return commands.some((command) => inspectOpOf(command) === null);
250
+ }
251
+ /**
199
252
  * Run every command, in order, and build the `results[]` payload.
200
253
  * `onBefore` is called BEFORE each command runs (to print it), `onEach`
201
254
  * after, with its result.
202
255
  */
203
- function runLocalCommands(commands, run = runInShell, onEach, onBefore) {
256
+ function runLocalCommands(commands, run = runInShell, onEach, onBefore, inspect = executeInspect) {
204
257
  return commands.map((command, index) => {
205
258
  onBefore?.(command, index);
206
- const result = toReportResult(command, run(command.command, commandTimeoutMs(command)));
259
+ const opJson = inspectOpOf(command);
260
+ const result = toReportResult(command, opJson !== null ? inspect(opJson) : run(command.command, commandTimeoutMs(command)));
207
261
  onEach?.(command, result);
208
262
  return result;
209
263
  });
210
264
  }
211
265
 
266
+ //#endregion
267
+ //#region src/commands/gates/ci-oidc.ts
268
+ /**
269
+ * GitHub Actions OIDC for `levr gates run --ci` (ENG-6177).
270
+ *
271
+ * The workflow needs `permissions: id-token: write` (documented with the
272
+ * pin in D13). When the runner env vars are absent, the CLI still declares
273
+ * CI and the server records `unverified` — it never invents a token.
274
+ *
275
+ * `fail_retry`: a report or verify that answers `OIDC_JWKS_UNAVAILABLE`
276
+ * (503) is retried twice with backoff, then the command exits non-zero.
277
+ * Nothing was recorded for those attempts.
278
+ *
279
+ * ENG-5987 review H7: the token is minted for ONE workspace
280
+ * (`levr:<workspace_id>`, matching the server's `ciOidcAudience`), is
281
+ * requested fresh right before each verify and report (an Actions token lives
282
+ * minutes, and a long suite outlived the one fetched at start-up), and the
283
+ * request is bounded by a timeout so a hung token endpoint cannot hang CI.
284
+ * The run's commit (`GITHUB_SHA`) is sent as `commit_sha`, which the server
285
+ * now requires before it will stamp `oidc_verified`.
286
+ */
287
+ const CI_OIDC_HEADER = "X-Levr-CI-OIDC";
288
+ const CI_DECLARED_HEADER = "X-Levr-CI-Declared";
289
+ const OIDC_JWKS_UNAVAILABLE = "OIDC_JWKS_UNAVAILABLE";
290
+ const RETRY_DELAYS_MS = [250, 750];
291
+ const TOKEN_REQUEST_TIMEOUT_MS = 1e4;
292
+ const COMMIT_SHA = /^[0-9a-f]{40}$/i;
293
+ /** The audience the server expects — `levr:<workspace_id>`. */
294
+ function ciOidcAudience(workspaceId) {
295
+ return `levr:${workspaceId}`;
296
+ }
297
+ /** The commit this Actions run checked out, when the runner says so. */
298
+ function ciCommitSha(env = process.env) {
299
+ const sha = env["GITHUB_SHA"]?.trim();
300
+ return sha && COMMIT_SHA.test(sha) ? sha : void 0;
301
+ }
302
+ function actionsOidcEnvFrom(env = process.env) {
303
+ return {
304
+ ACTIONS_ID_TOKEN_REQUEST_URL: env["ACTIONS_ID_TOKEN_REQUEST_URL"],
305
+ ACTIONS_ID_TOKEN_REQUEST_TOKEN: env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
306
+ };
307
+ }
308
+ /**
309
+ * Request an Actions OIDC token for `audience`. Returns null when the runner
310
+ * did not expose the env vars, the request failed, or it did not answer
311
+ * within `timeoutMs` — the caller still sends the declaration header, and
312
+ * the server records `unverified`.
313
+ */
314
+ async function requestActionsOidcToken(audience, env = actionsOidcEnvFrom(), fetchImpl = globalThis.fetch, timeoutMs = TOKEN_REQUEST_TIMEOUT_MS) {
315
+ const url = env.ACTIONS_ID_TOKEN_REQUEST_URL;
316
+ const requestToken = env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
317
+ if (!url || !requestToken) return null;
318
+ let endpoint;
319
+ try {
320
+ endpoint = new URL(url);
321
+ } catch {
322
+ return null;
323
+ }
324
+ endpoint.searchParams.set("audience", audience);
325
+ try {
326
+ const response = await fetchImpl(endpoint, {
327
+ headers: { Authorization: `bearer ${requestToken}` },
328
+ signal: AbortSignal.timeout(timeoutMs)
329
+ });
330
+ if (!response.ok) return null;
331
+ const body = await response.json();
332
+ return typeof body.value === "string" && body.value.length > 0 ? body.value : null;
333
+ } catch {
334
+ return null;
335
+ }
336
+ }
337
+ /** Headers for verify and report. Declaration is `1` whenever `--ci` is set. */
338
+ function ciRequestHeaders(token) {
339
+ const headers = { [CI_DECLARED_HEADER]: "1" };
340
+ if (token) headers[CI_OIDC_HEADER] = token;
341
+ return headers;
342
+ }
343
+ function isOidcJwksUnavailable(result) {
344
+ if (result.response?.status !== 503) return false;
345
+ return JSON.stringify(result.error ?? "").includes(OIDC_JWKS_UNAVAILABLE);
346
+ }
347
+ /**
348
+ * Run `fn` and, on `OIDC_JWKS_UNAVAILABLE`, retry twice. Any other result
349
+ * is returned as-is. `fail_retry(2)`.
350
+ */
351
+ async function withOidcJwksRetry(fn, sleep = delay) {
352
+ let result = await fn();
353
+ for (const wait of RETRY_DELAYS_MS) {
354
+ if (!isOidcJwksUnavailable(result)) return result;
355
+ await sleep(wait);
356
+ result = await fn();
357
+ }
358
+ return result;
359
+ }
360
+ function delay(ms) {
361
+ return new Promise((resolve) => {
362
+ setTimeout(resolve, ms);
363
+ });
364
+ }
365
+
212
366
  //#endregion
213
367
  //#region src/commands/gates/runHandler.ts
214
368
  /** ENG-6070 review R5 M3 — dead gates are named, with their remedy. */
@@ -225,11 +379,13 @@ function exitCodeOf(summary) {
225
379
  }
226
380
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
227
381
  /**
228
- * Seams for tests: the shell runner (production: `chooseShell`'s bash), and
229
- * the shell resolution checked BEFORE anything runs (F-020).
382
+ * Seams for tests: the shell runner (production: `chooseShell`'s bash), the
383
+ * in-process inspect runner (ENG-6180 review C1), and the shell resolution
384
+ * checked BEFORE anything runs (F-020).
230
385
  */
231
386
  const gatesRunDeps = {
232
387
  run: runInShell,
388
+ inspect: (opJson) => executeInspect(opJson),
233
389
  resolveShell: () => chooseShell()
234
390
  };
235
391
  /**
@@ -263,8 +419,21 @@ async function gatesRunHandler(flags, issue) {
263
419
  return;
264
420
  }
265
421
  configureClient(auth);
422
+ const setCiHeaders = (oidc) => {
423
+ const current = client.getConfig();
424
+ client.setConfig({
425
+ ...current,
426
+ headers: {
427
+ ...current.headers,
428
+ ...ciRequestHeaders(oidc)
429
+ }
430
+ });
431
+ };
432
+ if (flags.ci) setCiHeaders(null);
433
+ let workspaceId;
266
434
  if (auth.type === "jwt") try {
267
435
  const ws = await resolveWorkspace(flags["workspace-id"]);
436
+ workspaceId = ws.workspaceId;
268
437
  client.setConfig({
269
438
  ...client.getConfig(),
270
439
  workspaceId: ws.workspaceId
@@ -287,8 +456,23 @@ async function gatesRunHandler(flags, issue) {
287
456
  return;
288
457
  }
289
458
  issueId = row.id;
459
+ workspaceId ??= row.workspace_id;
290
460
  }
291
- const verified = await issueGateVerificationVerifyGatesV1({ path: { id: issueId } });
461
+ const commitSha = flags.ci ? ciCommitSha() : void 0;
462
+ const refreshCiToken = async () => {
463
+ if (!flags.ci) return;
464
+ if (!workspaceId) workspaceId = (await issueFindAllV1({ query: {
465
+ "filter.id": [`$eq:${issueId}`],
466
+ limit: 1
467
+ } })).data?.data?.[0]?.workspace_id;
468
+ setCiHeaders(workspaceId ? await requestActionsOidcToken(ciOidcAudience(workspaceId)) : null);
469
+ };
470
+ const ciBody = commitSha ? { commit_sha: commitSha } : {};
471
+ await refreshCiToken();
472
+ const verified = await withOidcJwksRetry(() => issueGateVerificationVerifyGatesV1({
473
+ path: { id: issueId },
474
+ ...commitSha ? { body: ciBody } : {}
475
+ }));
292
476
  if (verified.error) {
293
477
  this.logger.error(`verify-gates failed (${String(verified.response?.status ?? "unknown")}): ${JSON.stringify(verified.error)}`);
294
478
  this.process.exitCode = 1;
@@ -316,6 +500,8 @@ async function gatesRunHandler(flags, issue) {
316
500
  gate_type: c.gate_type,
317
501
  link_id: c.link_id,
318
502
  command: c.command,
503
+ runs: runsWhere(c),
504
+ ...approvalOf(c),
319
505
  ...c.expected !== void 0 ? { expected: c.expected } : {},
320
506
  ...c.step_id ? { step_id: c.step_id } : {},
321
507
  ...c.rule_hash ? { rule_hash: c.rule_hash } : {}
@@ -338,7 +524,7 @@ async function gatesRunHandler(flags, issue) {
338
524
  if (!flags.json) printManualTasks(out, tasks);
339
525
  return;
340
526
  }
341
- try {
527
+ if (needsShell(commands)) try {
342
528
  gatesRunDeps.resolveShell();
343
529
  } catch (error) {
344
530
  if (!(error instanceof NoPosixShellError)) throw error;
@@ -352,11 +538,15 @@ async function gatesRunHandler(flags, issue) {
352
538
  out(` [exit ${result.steps[0].exit_code}]`);
353
539
  }, (command, index) => {
354
540
  announce(`[${index + 1}/${commands.length}] ${command.test_name ?? command.gate_type}: ${command.command}`);
355
- });
356
- const reported = await issueGateVerificationReportGateResultsV1({
541
+ }, gatesRunDeps.inspect);
542
+ await refreshCiToken();
543
+ const reported = await withOidcJwksRetry(() => issueGateVerificationReportGateResultsV1({
357
544
  path: { id: issueId },
358
- body: { results }
359
- });
545
+ body: {
546
+ results,
547
+ ...ciBody
548
+ }
549
+ }));
360
550
  if (reported.error) {
361
551
  this.logger.error(`gate-results failed (${String(reported.response?.status ?? "unknown")}): ${JSON.stringify(reported.error)}`);
362
552
  this.process.exitCode = 1;
@@ -1,4 +1,4 @@
1
- import { getApiUrl } from "./env-CdwyPHGV.js";
1
+ import { getApiUrl } from "./env-CvjMSRd7.js";
2
2
 
3
3
  //#region ../sdk-public/dist/runtime/client.js
4
4
  function createInterceptorManager() {
@@ -1,8 +1,8 @@
1
- import { authGetSitesV1, configureClient } from "./sdk-client-DYv-0CfA.js";
2
- import "./env-CdwyPHGV.js";
3
- import "./token-refresh-DeusLK8H.js";
4
- import { resolveToken } from "./resolve-token-7xb7kG7h.js";
5
- import { saveWorkspace } from "./workspace-store-CnrxyYRB.js";
1
+ import { authGetSitesV1, configureClient } from "./sdk-client-B4FMd_Fa.js";
2
+ import "./env-CvjMSRd7.js";
3
+ import "./token-refresh-CJB7Qlxy.js";
4
+ import { resolveToken } from "./resolve-token-ByZq05Us.js";
5
+ import { saveWorkspace } from "./workspace-store-Bmw104Qa.js";
6
6
 
7
7
  //#region src/commands/workspace/selectHandler.ts
8
8
  async function selectHandler(_flags, workspaceId) {
@@ -1,6 +1,6 @@
1
- import { authGetProfileV1, configureClient } from "./sdk-client-DYv-0CfA.js";
2
- import { getApiUrl, getPatToken, readCredentials } from "./env-CdwyPHGV.js";
3
- import { isTokenExpired } from "./token-refresh-DeusLK8H.js";
1
+ import { authGetProfileV1, configureClient } from "./sdk-client-B4FMd_Fa.js";
2
+ import { getApiUrl, getPatToken, readCredentials } from "./env-CvjMSRd7.js";
3
+ import { isTokenExpired } from "./token-refresh-CJB7Qlxy.js";
4
4
  import chalk from "chalk";
5
5
 
6
6
  //#region src/commands/auth/statusHandler.ts
@@ -1,4 +1,4 @@
1
- import { CLI_CLIENT_ID, deleteCredentials, getApiUrl, writeCredentials } from "./env-CdwyPHGV.js";
1
+ import { CLI_CLIENT_ID, deleteCredentials, getApiUrl, writeCredentials } from "./env-CvjMSRd7.js";
2
2
 
3
3
  //#region src/auth/token-refresh.ts
4
4
  const REFRESH_BUFFER_MS = 300 * 1e3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levr-one/cli",
3
- "version": "0.11.10",
3
+ "version": "0.12.1",
4
4
  "description": "The command-line interface for Levr",
5
5
  "type": "module",
6
6
  "license": "MIT",
File without changes
File without changes