@mnemonik/cli 7.111.5 → 7.111.7

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 (49) hide show
  1. package/dist/auth/device.d.ts +5 -0
  2. package/dist/auth/device.d.ts.map +1 -1
  3. package/dist/auth/device.js +2 -1
  4. package/dist/auth/device.js.map +1 -1
  5. package/dist/auth/index.d.ts +2 -0
  6. package/dist/auth/index.d.ts.map +1 -1
  7. package/dist/auth/index.js +1 -0
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/digests.json +47 -47
  10. package/dist/help.d.ts.map +1 -1
  11. package/dist/help.js +7 -2
  12. package/dist/help.js.map +1 -1
  13. package/dist/humanReason.d.ts +22 -0
  14. package/dist/humanReason.d.ts.map +1 -1
  15. package/dist/humanReason.js +22 -3
  16. package/dist/humanReason.js.map +1 -1
  17. package/dist/install/journey.d.ts.map +1 -1
  18. package/dist/install/journey.js +8 -2
  19. package/dist/install/journey.js.map +1 -1
  20. package/dist/installSession.js +2 -0
  21. package/dist/router.d.ts.map +1 -1
  22. package/dist/router.js +229 -63
  23. package/dist/router.js.map +1 -1
  24. package/dist/runtime/updateCheck.d.ts +1 -1
  25. package/dist/runtime/updateCheck.d.ts.map +1 -1
  26. package/dist/runtime/updateCheck.js +4 -1
  27. package/dist/runtime/updateCheck.js.map +1 -1
  28. package/dist/sbom.json +21 -21
  29. package/dist/scanner/enable.d.ts +2 -0
  30. package/dist/scanner/enable.d.ts.map +1 -1
  31. package/dist/scanner/enable.js +13 -2
  32. package/dist/scanner/enable.js.map +1 -1
  33. package/dist/scanner/picker.d.ts +7 -0
  34. package/dist/scanner/picker.d.ts.map +1 -1
  35. package/dist/scanner/picker.js +12 -1
  36. package/dist/scanner/picker.js.map +1 -1
  37. package/dist/scanner/service.d.ts.map +1 -1
  38. package/dist/scanner/service.js +3 -2
  39. package/dist/scanner/service.js.map +1 -1
  40. package/dist/scanner/update.d.ts +3 -1
  41. package/dist/scanner/update.d.ts.map +1 -1
  42. package/dist/scanner/update.js +13 -2
  43. package/dist/scanner/update.js.map +1 -1
  44. package/dist/scanner-release.json +28 -28
  45. package/dist/status.d.ts +4 -1
  46. package/dist/status.d.ts.map +1 -1
  47. package/dist/status.js +52 -14
  48. package/dist/status.js.map +1 -1
  49. package/package.json +31 -31
package/dist/router.js CHANGED
@@ -2516,7 +2516,12 @@ import {
2516
2516
  humanIdentityState,
2517
2517
  INTERRUPTED_INSTALL,
2518
2518
  INTERRUPTED_INSTALL_MESSAGE,
2519
+ approvalMessage,
2520
+ knownReason,
2521
+ SCANNER_APPROVAL_ACTION,
2522
+ SCANNER_APPROVAL_NOTE,
2519
2523
  SCANNER_CONSENT_MESSAGE,
2524
+ SCANNER_ENABLE_ACTION,
2520
2525
  SCANNER_UPDATE_CONSENT_MESSAGE
2521
2526
  } from "./humanReason.js";
2522
2527
  import { consentDecision, consentLines } from "./consent.js";
@@ -2533,6 +2538,7 @@ import { deleteScannerIndex } from "./scanner/data.js";
2533
2538
  import { devReadiness } from "./runtime/releaseSource.js";
2534
2539
  import {
2535
2540
  scannerService,
2541
+ SCANNER_LIMITED_SENTENCE,
2536
2542
  ScannerServiceLimited
2537
2543
  } from "./scanner/service.js";
2538
2544
 
@@ -6733,6 +6739,8 @@ var stateFor = {
6733
6739
  host_trust_pending: "ACTION_REQUIRED",
6734
6740
  vendor_policy_pending: "ACTION_REQUIRED",
6735
6741
  login_pending: "ACTION_REQUIRED",
6742
+ consent_pending: "ACTION_REQUIRED",
6743
+ update_pending: "READY",
6736
6744
  restart_pending: "ACTION_REQUIRED",
6737
6745
  project_identity_choice_pending: "ACTION_REQUIRED",
6738
6746
  scanner_not_verified: "LIMITED",
@@ -7191,10 +7199,10 @@ import {
7191
7199
  validGrant
7192
7200
  } from "./auth/status.js";
7193
7201
  import { createCliAuth } from "./auth/index.js";
7194
- import { runEditorLogin } from "./auth/pkce.js";
7202
+ import { OAuthProtocolError, runEditorLogin } from "./auth/pkce.js";
7195
7203
  import { currentInstallSession, ensureInstallSession } from "./auth/installSession.js";
7196
7204
  import { runIdentityMigration } from "./identity/migrate.js";
7197
- import { renderScannerStatus } from "./scanner/picker.js";
7205
+ import { folderList, renderScannerStatus } from "./scanner/picker.js";
7198
7206
  import { abandonInterrupted, closeUntouchedScannerRun, interrupted } from "./install/journal.js";
7199
7207
  import {
7200
7208
  installFailureReason,
@@ -7302,8 +7310,35 @@ function personPresent(parsed, deps) {
7302
7310
  if (deps.input) return deps.input.isTTY !== false;
7303
7311
  return process.stdin.isTTY === true;
7304
7312
  }
7305
- async function approveScannerUpdate(parsed, deps, output, stateDir) {
7306
- if (parsed.flags.has("automatic") || !personPresent(parsed, deps)) return "pending";
7313
+ function approvalStopped(error, held) {
7314
+ if (error instanceof OAuthProtocolError) {
7315
+ if (error.code === "expired_token") return "scanner_approval_expired";
7316
+ if (error.code === "access_denied") return "scanner_approval_declined";
7317
+ }
7318
+ const message = error.message;
7319
+ if (message === "consent_declined" || message === "browser_consent_required")
7320
+ return "scanner_approval_declined";
7321
+ return held;
7322
+ }
7323
+ function relayApprovalLink(output, json, reason) {
7324
+ return (url, expiresAt) => {
7325
+ if (json)
7326
+ output.json({
7327
+ status: "awaiting_approval",
7328
+ reason,
7329
+ approvalUrl: url,
7330
+ expiresAt: new Date(expiresAt).toISOString()
7331
+ });
7332
+ else {
7333
+ output.line(APPROVAL_LINK_LINE);
7334
+ output.line(url);
7335
+ }
7336
+ };
7337
+ }
7338
+ var APPROVAL_LINK_LINE = "Give the person this link to approve the updated notice:";
7339
+ async function approveScannerUpdate(parsed, deps, output, stateDir, held = "scanner_update_consent_required") {
7340
+ if (parsed.flags.has("automatic")) return { status: "pending", reason: held };
7341
+ const present = personPresent(parsed, deps);
7307
7342
  const saved = JSON.parse(await readFile4(join7(stateDir, "scanner/state.json"), "utf8"));
7308
7343
  try {
7309
7344
  const document = await enableScanner({
@@ -7312,30 +7347,43 @@ async function approveScannerUpdate(parsed, deps, output, stateDir) {
7312
7347
  home: deps.home,
7313
7348
  input: deps.input ?? process.stdin,
7314
7349
  output,
7350
+ nonInteractive: !present,
7315
7351
  noBrowser: parsed.flags.has("no-browser"),
7316
7352
  roots: saved.config.roots,
7317
7353
  exclusions: saved.config.exclusions ?? [],
7354
+ ...present ? {} : { onApprovalLink: relayApprovalLink(output, parsed.flags.has("json"), held) },
7318
7355
  ...deps.scannerService,
7319
7356
  ...deps.scannerEnable,
7320
7357
  projectExecutor: deps.projectExecutor,
7321
7358
  projectStateDir: deps.projectStateDir
7322
7359
  });
7323
- return document.installation.state === "READY" ? "approved" : "pending";
7324
- } catch {
7325
- return "pending";
7360
+ return document.installation.state === "READY" ? { status: "approved" } : { status: "pending", reason: held };
7361
+ } catch (error) {
7362
+ return { status: "pending", reason: approvalStopped(error, held) };
7326
7363
  }
7327
7364
  }
7328
- function scannerConsentRequired(output, json) {
7365
+ function approvalLines(reason, agent) {
7366
+ if (reason === "scanner_approval_expired" || reason === "scanner_approval_declined") {
7367
+ const sentence2 = reason === "scanner_approval_expired" ? "The approval request for the updated notice expired." : "The updated notice was not approved.";
7368
+ if (agent)
7369
+ return [sentence2, "Run mnemonik update again and give the person the new approval link."];
7370
+ return reason === "scanner_approval_expired" ? [sentence2, SCANNER_UPDATE_CONSENT_MESSAGE.nextStep] : [sentence2];
7371
+ }
7372
+ const message = approvalMessage(
7373
+ reason === "scanner_consent_required" ? SCANNER_CONSENT_MESSAGE : SCANNER_UPDATE_CONSENT_MESSAGE,
7374
+ agent
7375
+ );
7376
+ return [message.sentence, message.nextStep];
7377
+ }
7378
+ function scannerConsentRequired(output, json, agent) {
7329
7379
  if (json)
7330
7380
  output.json({
7331
7381
  status: "ACTION_REQUIRED",
7332
7382
  reason: "scanner_consent_required",
7333
- action: "mnemonik scanner enable"
7383
+ action: SCANNER_APPROVAL_ACTION,
7384
+ approval: SCANNER_APPROVAL_NOTE
7334
7385
  });
7335
- else {
7336
- output.line(SCANNER_CONSENT_MESSAGE.sentence);
7337
- output.line(SCANNER_CONSENT_MESSAGE.nextStep);
7338
- }
7386
+ else for (const line of approvalLines("scanner_consent_required", agent)) output.line(line);
7339
7387
  return 3;
7340
7388
  }
7341
7389
  function hostUpdateFailed(result) {
@@ -7694,7 +7742,9 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
7694
7742
  () => true,
7695
7743
  () => false
7696
7744
  )) {
7745
+ const pausedForApproval = pausedForConsent(await scannerReceipt(state));
7697
7746
  try {
7747
+ if (pausedForApproval) throw new ScannerConsentRequired();
7698
7748
  const before = await store.verifyRuntime("scanner").catch(() => void 0);
7699
7749
  const runtime = await retryUpdateOnce(
7700
7750
  () => updateScanner(
@@ -7716,13 +7766,21 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
7716
7766
  version: runtime.manifest.version
7717
7767
  };
7718
7768
  } catch (error) {
7719
- if (error instanceof ScannerConsentRequired)
7720
- scanner = await approveScannerUpdate(parsed, deps, output, state) === "approved" ? { status: "UPDATED" } : {
7769
+ if (error instanceof ScannerConsentRequired) {
7770
+ const approval = await approveScannerUpdate(
7771
+ parsed,
7772
+ deps,
7773
+ output,
7774
+ state,
7775
+ pausedForApproval ? "scanner_consent_required" : "scanner_update_consent_required"
7776
+ );
7777
+ scanner = approval.status === "approved" ? { status: "UPDATED" } : {
7721
7778
  status: "ACTION_REQUIRED",
7722
- reason: "scanner_update_consent_required",
7723
- action: "mnemonik scanner enable"
7779
+ reason: approval.reason,
7780
+ action: SCANNER_APPROVAL_ACTION,
7781
+ approval: SCANNER_APPROVAL_NOTE
7724
7782
  };
7725
- else scanner = { status: "FAILED", reason: error.message };
7783
+ } else scanner = { status: "FAILED", reason: error.message };
7726
7784
  if (!json && scanner.status === "FAILED")
7727
7785
  scannerRecovery = await scannerRecoveryAction(error, {
7728
7786
  stateDir: state,
@@ -7742,7 +7800,7 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
7742
7800
  if (all)
7743
7801
  await recordUpdateCheck(
7744
7802
  state,
7745
- failed || hostsFailed ? "failed" : cli?.status === "UPDATED" || result.reports.length > 0 || scanner?.status === "UPDATED" ? "updated" : "current"
7803
+ failed || hostsFailed ? "failed" : scannerConsentPending ? "consent_pending" : cli?.status === "UPDATED" || result.reports.length > 0 || scanner?.status === "UPDATED" ? "updated" : "current"
7746
7804
  );
7747
7805
  if (fullUninstall && hostExit === 0) {
7748
7806
  const installed = await Promise.all(
@@ -7818,15 +7876,18 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
7818
7876
  } else if (cli?.status === "UPDATED" || result.reports.length > 0 || scanner?.status === "UPDATED")
7819
7877
  output.line("Mnemonik updated.");
7820
7878
  else output.line("Mnemonik is up to date.");
7821
- if (scannerConsentPending) {
7822
- output.line(SCANNER_UPDATE_CONSENT_MESSAGE.sentence);
7823
- output.line(SCANNER_UPDATE_CONSENT_MESSAGE.nextStep);
7824
- }
7879
+ if (scannerConsentPending)
7880
+ for (const line of approvalLines(
7881
+ scanner?.reason ?? "scanner_update_consent_required",
7882
+ !personPresent(parsed, deps)
7883
+ ))
7884
+ output.line(line);
7825
7885
  } else {
7826
7886
  for (const target of result.results)
7827
7887
  output.line(target.status === "READY" ? "Done." : humanReason(target.reason));
7828
7888
  for (const report of result.reports) output.line(humanReport(report));
7829
- if (remaining) renderStatusSummaries(remaining, output);
7889
+ if (remaining)
7890
+ renderStatusSummaries(remaining, output, { agent: !personPresent(parsed, deps) });
7830
7891
  if (!selections.length && !fullUninstall && !remaining)
7831
7892
  output.line("No recorded host targets.");
7832
7893
  if (fullUninstall && !failed && hostExit === 0)
@@ -7888,28 +7949,133 @@ async function scannerRecoveryAction(error, options) {
7888
7949
  action: failure?.action ?? SCANNER_RETRY_MESSAGE
7889
7950
  };
7890
7951
  }
7952
+ var FOLDER_REFUSED = /^(?:filesystem_root|home_directory|temporary_directory|mnemonik_state_directory|user_data_directory|host_config_directory|broad_workspace_parent)$/u;
7953
+ var ENABLE_FAILURES = {
7954
+ browser_consent_required: {
7955
+ summary: "The folders were not approved in your browser.",
7956
+ action: SCANNER_ENABLE_ACTION
7957
+ },
7958
+ consent_declined: {
7959
+ summary: "Background indexing was not approved.",
7960
+ action: SCANNER_ENABLE_ACTION
7961
+ },
7962
+ scanner_approval_declined: {
7963
+ summary: "Background indexing was not approved.",
7964
+ action: SCANNER_ENABLE_ACTION
7965
+ },
7966
+ scanner_approval_expired: {
7967
+ summary: "The approval request expired before it was approved.",
7968
+ action: SCANNER_ENABLE_ACTION
7969
+ },
7970
+ // The scanner this CLI carries names a notice the account's approval does not cover.
7971
+ release_consent_required: {
7972
+ summary: "This version of Mnemonik cannot install the newer scanner yet.",
7973
+ action: "mnemonik update"
7974
+ },
7975
+ scan_roots_required: {
7976
+ summary: "No folder was chosen for background indexing.",
7977
+ action: `${SCANNER_ENABLE_ACTION} --scan-roots <folder>`
7978
+ }
7979
+ };
7980
+ function folderFailure(code, folder) {
7981
+ if (code === "ENOENT")
7982
+ return {
7983
+ reason: "folder_missing",
7984
+ folder,
7985
+ summary: `This folder does not exist: ${folder}`,
7986
+ action: "Pass folders that exist in --scan-roots, or leave --scan-roots out to keep the folders already approved."
7987
+ };
7988
+ if (code === "EACCES" || code === "EPERM")
7989
+ return {
7990
+ reason: "folder_unreadable",
7991
+ folder,
7992
+ summary: `Mnemonik cannot open this folder: ${folder}`,
7993
+ action: "Give the user read access to the folder, or choose another, then run the command again."
7994
+ };
7995
+ return void 0;
7996
+ }
7997
+ function enableFailure(error) {
7998
+ const message = error.message;
7999
+ const limited = scannerFailure(error);
8000
+ if (limited) return { reason: message, summary: limited.summary, step: limited.action };
8001
+ const { code, path } = error;
8002
+ const folder = path ? folderFailure(code, path) : void 0;
8003
+ if (folder) return folder;
8004
+ if (FOLDER_REFUSED.test(message)) return { reason: message, summary: "" };
8005
+ const stopped = error instanceof OAuthProtocolError ? approvalStopped(error, message) : message;
8006
+ const known = ENABLE_FAILURES[stopped];
8007
+ if (known) return { reason: stopped, ...known };
8008
+ if (message === "lock_held") return { reason: message, summary: humanReason(message) };
8009
+ const words = knownReason(message);
8010
+ if (words) return { reason: message, summary: words.sentence, step: words.nextStep };
8011
+ return {
8012
+ reason: message,
8013
+ summary: SCANNER_LIMITED_SENTENCE,
8014
+ action: SCANNER_ENABLE_ACTION
8015
+ };
8016
+ }
8017
+ function enableFailed(output, json, agent, failure) {
8018
+ const action = failure.action ?? failure.step;
8019
+ if (json)
8020
+ output.json({
8021
+ status: "ACTION_REQUIRED",
8022
+ reason: failure.reason,
8023
+ ...failure.folder ? { folder: failure.folder } : {},
8024
+ ...action ? { action } : {}
8025
+ });
8026
+ else {
8027
+ if (failure.summary) output.error(failure.summary);
8028
+ const next = agent ? action : failure.step;
8029
+ if (next) output.error(/^mnemonik /u.test(next) ? `Run ${next}.` : next);
8030
+ }
8031
+ return 3;
8032
+ }
8033
+ async function approvedSelection(stateDir) {
8034
+ const saved = JSON.parse(
8035
+ await readFile4(join7(stateDir, "scanner/state.json"), "utf8").catch(() => "null")
8036
+ );
8037
+ const roots = saved?.config?.roots;
8038
+ if (!Array.isArray(roots) || !roots.length || !roots.every((r) => typeof r === "string"))
8039
+ return void 0;
8040
+ const exclusions = saved?.config?.exclusions;
8041
+ return {
8042
+ roots,
8043
+ exclusions: Array.isArray(exclusions) ? exclusions.filter((e) => typeof e === "string") : []
8044
+ };
8045
+ }
7891
8046
  async function enableCommand(parsed, deps, output) {
7892
8047
  const json = parsed.flags.has("json");
7893
8048
  const present = personPresent(parsed, deps);
8049
+ const stateDir = deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home);
8050
+ const home = deps.home ?? homedir4();
7894
8051
  if (!present) {
7895
8052
  const missing = requireConsent(parsed, output, ["accept-indexing", "apply"]);
7896
8053
  if (missing !== void 0) return missing;
7897
- if (!parsed.flags.has("scan-roots"))
7898
- return actionRequired2(
7899
- output,
7900
- json,
7901
- "Supply --scan-roots with approved roots",
7902
- "--scan-roots"
7903
- );
8054
+ }
8055
+ const rootsFlag = parsed.flags.get("scan-roots");
8056
+ const exclusionsFlag = parsed.flags.get("exclusions");
8057
+ let roots = typeof rootsFlag === "string" ? folderList(rootsFlag, home) : void 0;
8058
+ let exclusions = typeof exclusionsFlag === "string" ? folderList(exclusionsFlag, home) : void 0;
8059
+ if (!roots && (!present || parsed.flags.has("apply"))) {
8060
+ const approved = await approvedSelection(stateDir);
8061
+ if (approved) {
8062
+ roots = approved.roots;
8063
+ exclusions ??= approved.exclusions;
8064
+ }
8065
+ }
8066
+ if (!present && !roots)
8067
+ return actionRequired2(output, json, "Supply --scan-roots with approved roots", "--scan-roots");
8068
+ for (const folder of [...roots ?? [], ...exclusions ?? []]) {
8069
+ const failure = await realpath(folder).then(
8070
+ () => void 0,
8071
+ (error) => folderFailure(error.code, folder)
8072
+ );
8073
+ if (failure) return enableFailed(output, json, !present, failure);
7904
8074
  }
7905
8075
  try {
7906
- await closeUntouchedScannerRun(
7907
- deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home)
7908
- ).catch(() => false);
7909
- const roots = parsed.flags.get("scan-roots");
7910
- const exclusions = parsed.flags.get("exclusions");
8076
+ await closeUntouchedScannerRun(stateDir).catch(() => false);
7911
8077
  const result = await enableScanner({
7912
- stateDir: deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home),
8078
+ stateDir,
7913
8079
  cwd: deps.cwd ?? process.cwd(),
7914
8080
  home: deps.home,
7915
8081
  input: deps.input ?? process.stdin,
@@ -7917,29 +8083,20 @@ async function enableCommand(parsed, deps, output) {
7917
8083
  nonInteractive: !present,
7918
8084
  noBrowser: parsed.flags.has("no-browser"),
7919
8085
  timeout: retryOnce(parsed.flags),
7920
- ...typeof roots === "string" ? { roots: roots.split(",").filter(Boolean) } : {},
7921
- ...typeof exclusions === "string" ? { exclusions: exclusions.split(",").filter(Boolean) } : {},
8086
+ // An agent's run hands over the browser approval link as soon as it exists.
8087
+ ...present ? {} : { onApprovalLink: relayApprovalLink(output, json, "scanner_consent_required") },
8088
+ ...roots ? { roots } : {},
8089
+ ...exclusions ? { exclusions } : {},
7922
8090
  ...deps.scannerService,
7923
8091
  ...deps.scannerEnable,
7924
8092
  projectExecutor: deps.projectExecutor,
7925
8093
  projectStateDir: deps.projectStateDir
7926
8094
  });
7927
8095
  if (json) output.json(result);
7928
- else renderStatusSummaries(result, output);
8096
+ else renderStatusSummaries(result, output, { agent: !present });
7929
8097
  return result.installation.state === "READY" ? 0 : 3;
7930
8098
  } catch (error) {
7931
- const failure = scannerFailure(error) ?? new ScannerServiceLimited("scanner_service_unavailable", error.message);
7932
- const result = {
7933
- status: "ACTION_REQUIRED",
7934
- reason: error.message,
7935
- action: failure.action
7936
- };
7937
- if (json) output.json(result);
7938
- else {
7939
- output.error(failure.summary);
7940
- if (failure.action) output.error(failure.action);
7941
- }
7942
- return 3;
8099
+ return enableFailed(output, json, !present, enableFailure(error));
7943
8100
  }
7944
8101
  }
7945
8102
  async function installCommand(parsed, deps, output) {
@@ -8305,7 +8462,7 @@ async function runCli(args2, deps = {}) {
8305
8462
  );
8306
8463
  if (!saved) return actionRequired2(output, parsed.flags.has("json"), "mnemonik install");
8307
8464
  if ((action === "add" || action === "remove") && pausedForConsent(await scannerReceipt(stateDir)))
8308
- return scannerConsentRequired(output, parsed.flags.has("json"));
8465
+ return scannerConsentRequired(output, parsed.flags.has("json"), !personPresent(parsed, deps));
8309
8466
  if (action === "list") {
8310
8467
  if (parsed.flags.has("json")) output.json(saved.config.roots);
8311
8468
  else for (const root of saved.config.roots) output.line(root);
@@ -8395,7 +8552,7 @@ async function runCli(args2, deps = {}) {
8395
8552
  status: "revoked",
8396
8553
  verbs: ["revoke access"],
8397
8554
  retained: ["local software", "cloud data"],
8398
- action: "mnemonik scanner enable"
8555
+ action: SCANNER_ENABLE_ACTION
8399
8556
  };
8400
8557
  if (parsed.flags.has("json")) output.json(result);
8401
8558
  else
@@ -8458,6 +8615,8 @@ async function runCli(args2, deps = {}) {
8458
8615
  ...deps.scannerService
8459
8616
  };
8460
8617
  if (command === "update") {
8618
+ if (pausedForConsent(await scannerReceipt(options.stateDir)))
8619
+ throw new ScannerConsentRequired(true);
8461
8620
  const store = new RuntimeStore(options.stateDir);
8462
8621
  const before = await store.verifyRuntime("scanner").catch(() => void 0);
8463
8622
  const runtime = await retryUpdateOnce(
@@ -8496,7 +8655,14 @@ async function runCli(args2, deps = {}) {
8496
8655
  } catch (error) {
8497
8656
  if (command === "update" && error instanceof ScannerConsentRequired) {
8498
8657
  const stateDir = deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home);
8499
- if (await approveScannerUpdate(parsed, deps, output, stateDir) === "approved") {
8658
+ const approval = await approveScannerUpdate(
8659
+ parsed,
8660
+ deps,
8661
+ output,
8662
+ stateDir,
8663
+ error.paused ? "scanner_consent_required" : "scanner_update_consent_required"
8664
+ );
8665
+ if (approval.status === "approved") {
8500
8666
  if (parsed.flags.has("json")) output.json({ status: "updated" });
8501
8667
  else output.line("Mnemonik updated.");
8502
8668
  return 0;
@@ -8504,13 +8670,13 @@ async function runCli(args2, deps = {}) {
8504
8670
  if (parsed.flags.has("json"))
8505
8671
  output.json({
8506
8672
  status: "ACTION_REQUIRED",
8507
- reason: "scanner_update_consent_required",
8508
- action: "mnemonik scanner enable"
8673
+ reason: approval.reason,
8674
+ action: SCANNER_APPROVAL_ACTION,
8675
+ approval: SCANNER_APPROVAL_NOTE
8509
8676
  });
8510
- else {
8511
- output.line(SCANNER_UPDATE_CONSENT_MESSAGE.sentence);
8512
- output.line(SCANNER_UPDATE_CONSENT_MESSAGE.nextStep);
8513
- }
8677
+ else
8678
+ for (const line of approvalLines(approval.reason, !personPresent(parsed, deps)))
8679
+ output.line(line);
8514
8680
  return 3;
8515
8681
  }
8516
8682
  const failure = scannerFailure(error);
@@ -8665,7 +8831,7 @@ Preview: ${preview.path}
8665
8831
  deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home)
8666
8832
  );
8667
8833
  if (!parsed.flags.has("json")) {
8668
- renderStatusSummaries(shown, output);
8834
+ renderStatusSummaries(shown, output, { agent: !personPresent(parsed, deps) });
8669
8835
  for (const line of codingToolLines(shown.codingTools)) output.line(line);
8670
8836
  await renderRefusals(refusalStateDir(deps), output);
8671
8837
  }
@@ -8960,7 +9126,7 @@ Preview: ${preview.path}
8960
9126
  ...deps.scannerService
8961
9127
  };
8962
9128
  if (subcommand === "resume" && pausedForConsent(await scannerReceipt(options.stateDir)))
8963
- return scannerConsentRequired(output, json);
9129
+ return scannerConsentRequired(output, json, !personPresent(parsed, deps));
8964
9130
  if (subcommand === "pause" || subcommand === "resume")
8965
9131
  await controlScanner(subcommand, options);
8966
9132
  else if (subcommand === "export-preview") {