@indigoai-us/hq-cli 5.345.170 → 5.345.172

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 (142) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/assets/scaffold/core/scripts/hq-delegate-send.sh +1 -1
  3. package/dist/command-catalog.generated.d.ts +206 -7
  4. package/dist/command-catalog.generated.js +252 -8
  5. package/dist/command-registration-plan.d.ts +7 -0
  6. package/dist/command-registration-plan.js +1 -0
  7. package/dist/commands/access.d.ts +32 -1
  8. package/dist/commands/access.js +112 -3
  9. package/dist/commands/agent-kit.d.ts +1 -1
  10. package/dist/commands/agent-kit.js +3 -1
  11. package/dist/commands/agents.js +2 -1
  12. package/dist/commands/bot.d.ts +3 -0
  13. package/dist/commands/bot.js +179 -18
  14. package/dist/commands/cloud.d.ts +58 -1
  15. package/dist/commands/cloud.js +228 -10
  16. package/dist/commands/core-company-index-scope-flag.d.ts +8 -1
  17. package/dist/commands/core-company-index-scope-flag.js +9 -2
  18. package/dist/commands/core-delegate-w1b.js +5 -2
  19. package/dist/commands/core-dm-bind-native.js +19 -4
  20. package/dist/commands/core-rebuild-index-fast.js +24 -6
  21. package/dist/commands/core.js +18 -6
  22. package/dist/commands/daemon.js +16 -1
  23. package/dist/commands/dm.d.ts +17 -3
  24. package/dist/commands/dm.js +121 -26
  25. package/dist/commands/files.d.ts +1 -0
  26. package/dist/commands/files.js +22 -4
  27. package/dist/commands/integrations-api.d.ts +4 -0
  28. package/dist/commands/integrations-api.js +18 -2
  29. package/dist/commands/integrations-connect.d.ts +10 -3
  30. package/dist/commands/integrations-connect.js +70 -6
  31. package/dist/commands/integrations-core.d.ts +23 -0
  32. package/dist/commands/integrations-core.js +100 -0
  33. package/dist/commands/integrations-manage.js +8 -1
  34. package/dist/commands/integrations.js +49 -5
  35. package/dist/commands/lanes-loop.d.ts +20 -0
  36. package/dist/commands/lanes-loop.js +69 -11
  37. package/dist/commands/lanes.js +9 -1
  38. package/dist/commands/rescue.d.ts +9 -0
  39. package/dist/commands/rescue.js +31 -0
  40. package/dist/commands/run.js +115 -40
  41. package/dist/commands/scan.d.ts +5 -1
  42. package/dist/commands/scan.js +16 -4
  43. package/dist/commands/secrets.js +50 -9
  44. package/dist/commands/sync-core.d.ts +22 -0
  45. package/dist/commands/sync-core.js +124 -0
  46. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2447 -2135
  47. package/dist/hq-cloud-sync-runner/package.json +1 -1
  48. package/dist/lib/agent-kit/inbox-state.d.ts +17 -0
  49. package/dist/lib/agent-kit/inbox-state.js +41 -20
  50. package/dist/lib/agent-kit/kit-config.d.ts +2 -0
  51. package/dist/lib/agent-kit/kit-config.js +6 -0
  52. package/dist/lib/agent-kit/mcp/tools.js +12 -5
  53. package/dist/lib/agent-kit/run/heartbeat.d.ts +6 -3
  54. package/dist/lib/agent-kit/run/heartbeat.js +6 -3
  55. package/dist/lib/agent-kit/run/inbox.d.ts +8 -3
  56. package/dist/lib/agent-kit/run/inbox.js +42 -10
  57. package/dist/lib/agent-kit/run/request-timeout.d.ts +26 -0
  58. package/dist/lib/agent-kit/run/request-timeout.js +51 -0
  59. package/dist/lib/bot/api.d.ts +1 -1
  60. package/dist/lib/bot/config.d.ts +10 -0
  61. package/dist/lib/bot/config.js +17 -1
  62. package/dist/lib/bot/fleet-local.d.ts +142 -0
  63. package/dist/lib/bot/fleet-local.js +689 -0
  64. package/dist/lib/bot/fleet-python.d.ts +47 -0
  65. package/dist/lib/bot/fleet-python.js +218 -0
  66. package/dist/lib/bot/index.d.ts +2 -0
  67. package/dist/lib/bot/index.js +2 -0
  68. package/dist/lib/bot/local-config.d.ts +3 -2
  69. package/dist/lib/bot/local-config.js +5 -2
  70. package/dist/lib/bot/promote.d.ts +10 -0
  71. package/dist/lib/bot/promote.js +3 -2
  72. package/dist/lib/bot/prompt.d.ts +6 -1
  73. package/dist/lib/bot/prompt.js +8 -3
  74. package/dist/lib/bot/runtime/index.js +8 -4
  75. package/dist/lib/bot/runtime/scratch-dir.d.ts +24 -0
  76. package/dist/lib/bot/runtime/scratch-dir.js +41 -0
  77. package/dist/lib/caller-principal.d.ts +4 -0
  78. package/dist/lib/caller-principal.js +40 -0
  79. package/dist/lib/capacity/collectors/grok.js +8 -2
  80. package/dist/lib/cli-flag-keys.d.ts +2 -0
  81. package/dist/lib/cli-flag-keys.js +2 -0
  82. package/dist/lib/daemon/catalog.d.ts +12 -4
  83. package/dist/lib/daemon/catalog.js +70 -24
  84. package/dist/lib/daemon/config.d.ts +0 -7
  85. package/dist/lib/daemon/config.js +0 -32
  86. package/dist/lib/daemon/run.d.ts +5 -1
  87. package/dist/lib/daemon/run.js +48 -11
  88. package/dist/lib/daemon/supervisor.js +3 -1
  89. package/dist/lib/doctor/checks/release-links.d.ts +62 -1
  90. package/dist/lib/doctor/checks/release-links.js +188 -2
  91. package/dist/lib/doctor/fix/apply.js +54 -0
  92. package/dist/lib/doctor/fix/remediation.d.ts +8 -1
  93. package/dist/lib/doctor/fix/remediation.js +22 -0
  94. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  95. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  96. package/dist/lib/index-render/index.d.ts +9 -0
  97. package/dist/lib/index-render/index.js +65 -5
  98. package/dist/lib/index-render/session-scope.d.ts +69 -0
  99. package/dist/lib/index-render/session-scope.js +279 -0
  100. package/dist/lib/lanes/admission.js +7 -20
  101. package/dist/lib/lanes/reconcile.js +13 -15
  102. package/dist/lib/lanes/stale-lock.d.ts +50 -0
  103. package/dist/lib/lanes/stale-lock.js +105 -0
  104. package/dist/lib/lanes/store.js +11 -76
  105. package/dist/lib/notify-recipient.d.ts +10 -4
  106. package/dist/lib/notify-recipient.js +16 -4
  107. package/dist/lib/secret-input-link.d.ts +42 -0
  108. package/dist/lib/secret-input-link.js +93 -0
  109. package/dist/lib/secret-scan/range.d.ts +57 -0
  110. package/dist/lib/secret-scan/range.js +266 -0
  111. package/dist/lib/secret-scan/repo-config.d.ts +18 -3
  112. package/dist/lib/secret-scan/repo-config.js +135 -2
  113. package/dist/lib/sync-core/engine-home.d.ts +44 -0
  114. package/dist/lib/sync-core/engine-home.js +193 -0
  115. package/dist/lib/sync-core/flag.d.ts +17 -0
  116. package/dist/lib/sync-core/flag.js +47 -0
  117. package/dist/lib/sync-core/reconcile.d.ts +31 -0
  118. package/dist/lib/sync-core/reconcile.js +196 -0
  119. package/dist/lib/sync-core/release.d.ts +32 -0
  120. package/dist/lib/sync-core/release.js +140 -0
  121. package/dist/lib/sync-core/rollout-state.d.ts +24 -0
  122. package/dist/lib/sync-core/rollout-state.js +94 -0
  123. package/dist/lib/vault-sync-switches.d.ts +5 -0
  124. package/dist/lib/vault-sync-switches.js +43 -0
  125. package/dist/run/script-bound-schema.d.ts +27 -0
  126. package/dist/run/script-bound-schema.js +97 -0
  127. package/dist/secrets-env-action.d.ts +2 -0
  128. package/dist/secrets-env-action.js +7 -2
  129. package/dist/utils/cognito-session.d.ts +3 -30
  130. package/dist/utils/cognito-session.js +2 -100
  131. package/dist/utils/machine-principal-state.d.ts +38 -0
  132. package/dist/utils/machine-principal-state.js +110 -0
  133. package/dist/utils/remote-list-not-converging.d.ts +21 -0
  134. package/dist/utils/remote-list-not-converging.js +40 -0
  135. package/dist/utils/safe-link.js +9 -0
  136. package/dist/utils/script-bound-exec.d.ts +158 -0
  137. package/dist/utils/script-bound-exec.js +568 -0
  138. package/dist/utils/sync-watchdog.d.ts +92 -0
  139. package/dist/utils/sync-watchdog.js +250 -0
  140. package/package.json +8 -8
  141. package/dist/lib/daemon/sync-core-canary.d.ts +0 -9
  142. package/dist/lib/daemon/sync-core-canary.js +0 -56
@@ -1252,6 +1252,16 @@ export const COMMAND_CATALOG = [
1252
1252
  "flags": "--force-scope-shrink",
1253
1253
  "description": "When a scope shrink would un-track locally-modified files, proceed anyway. Dirty files are KEPT on disk (only un-tracked); clean out-of-scope files are quarantined under .hq/scope-quarantine/.",
1254
1254
  "hidden": false
1255
+ },
1256
+ {
1257
+ "flags": "--max-idle <seconds>",
1258
+ "description": "For scheduled or unattended runs: stop with exit code 124 after this many seconds with no file planned, transferred or skipped. The remote listing and planning phase of a leg reports no progress, so set this longer than your slowest planning phase. Laptop sleep does not count. (default: HQ_SYNC_MAX_IDLE, else off)",
1259
+ "hidden": false
1260
+ },
1261
+ {
1262
+ "flags": "--max-runtime <seconds>",
1263
+ "description": "For scheduled or unattended runs: stop with exit code 124 if the run has not finished after this many seconds, even while it is making progress. Laptop sleep does not count. (default: HQ_SYNC_MAX_RUNTIME, else off)",
1264
+ "hidden": false
1255
1265
  }
1256
1266
  ],
1257
1267
  "subcommands": []
@@ -2750,6 +2760,16 @@ export const COMMAND_CATALOG = [
2750
2760
  "description": "Token expiry duration (e.g. 24h, 2d, 30m)",
2751
2761
  "hidden": false,
2752
2762
  "defaultValue": "24h"
2763
+ },
2764
+ {
2765
+ "flags": "--format <format>",
2766
+ "description": "Output format: text (default; OSC-8 hyperlink on a supporting TTY, plain URL otherwise; FORCE_HYPERLINK=1/0 overrides detection on a TTY only), markdown (one Markdown link line on stdout, details on stderr), json",
2767
+ "hidden": false
2768
+ },
2769
+ {
2770
+ "flags": "--json",
2771
+ "description": "Print { url, expiresAt, secretName } as JSON (same as --format json)",
2772
+ "hidden": false
2753
2773
  }
2754
2774
  ],
2755
2775
  "subcommands": []
@@ -3654,7 +3674,7 @@ export const COMMAND_CATALOG = [
3654
3674
  },
3655
3675
  {
3656
3676
  "name": "files",
3657
- "description": "Manage file access controls in HQ vault. A trailing folder slash (foo/) is a private folder; the quoted glob 'foo/*' shares all children.",
3677
+ "description": "Manage file access controls in HQ vault. A trailing folder slash (foo/) is a private folder; the quoted glob 'foo/*' shares all children, and a quoted 'clients/*/reports' shares that folder under every client.",
3658
3678
  "aliases": [],
3659
3679
  "hidden": false,
3660
3680
  "helpOption": true,
@@ -3672,7 +3692,7 @@ export const COMMAND_CATALOG = [
3672
3692
  "subcommands": [
3673
3693
  {
3674
3694
  "name": "share",
3675
- "description": "Share file paths. foo/ is a private folder (children stay creator-only); quote the shared-folder glob 'foo/*' so the shell passes it literally. Without --with: mint a share-session URL and open it in the browser. With --with: grant access directly to a person, group, or @all.",
3695
+ "description": "Share file paths. foo/ is a private folder (children stay creator-only); quote the shared-folder glob 'foo/*', or a one-folder wildcard such as 'clients/*/reports', so the shell passes it literally. Without --with: mint a share-session URL and open it in the browser. With --with: grant access directly to a person, group, or @all.",
3676
3696
  "aliases": [],
3677
3697
  "hidden": false,
3678
3698
  "helpOption": true,
@@ -4095,7 +4115,7 @@ export const COMMAND_CATALOG = [
4095
4115
  "helpText": [
4096
4116
  {
4097
4117
  "position": "after",
4098
- "text": "\nExit codes:\n 0 local or not-synced (file exists and you can read it)\n 2 never-existed (the path was never created)\n 3 no-access (the file exists but you cannot read it)\n 4 ambiguous (several keys matched; pick one)\n\nWith several paths, each gets its own result block and the exit code is the\nhighest one across the paths.\n"
4118
+ "text": "\nExit codes:\n 0 local or not-synced (file exists and you can read it)\n 2 never-existed (the path was never created)\n 3 no-access (the file exists but you cannot read it), or\n no-write-access (the file is on disk, not in the vault, and you cannot\n write to it, so a push cannot upload it)\n 4 ambiguous (several keys matched; pick one)\n\nWith several paths, each gets its own result block and the exit code is the\nhighest one across the paths.\n"
4099
4119
  }
4100
4120
  ],
4101
4121
  "arguments": [
@@ -4842,7 +4862,7 @@ export const COMMAND_CATALOG = [
4842
4862
  },
4843
4863
  {
4844
4864
  "name": "dm",
4845
- "description": "Send and read direct messages, and manage connection requests. Messages to a person send to them; messages to an agent, a channel or a group are tagged for agents and folded away from people by default. Pass --for-human or --audience human|agent|both to override.",
4865
+ "description": "Send and read direct messages, and manage connection requests. Messages sent from a person login are always tagged for people. When an agent or bot sends, messages to an agent, a channel or a group are tagged for agents and folded away from people by default. Pass --for-human or --audience human to tag a message for people; --audience agent|both is for agents and bots only.",
4846
4866
  "aliases": [],
4847
4867
  "hidden": false,
4848
4868
  "helpOption": true,
@@ -4923,7 +4943,7 @@ export const COMMAND_CATALOG = [
4923
4943
  },
4924
4944
  {
4925
4945
  "flags": "--audience <human|agent|both>",
4926
- "description": "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both. Default: human for a person, agent for an agent, a channel or a group. Wins over --for-human.",
4946
+ "description": "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both. agent and both are for agents and bots only; a person login always sends human. Default for agents and bots: human for a person, agent for an agent, a channel or a group. Wins over --for-human.",
4927
4947
  "hidden": false
4928
4948
  },
4929
4949
  {
@@ -6478,6 +6498,11 @@ export const COMMAND_CATALOG = [
6478
6498
  "description": "Company slug, e.g. indigo",
6479
6499
  "hidden": false
6480
6500
  },
6501
+ {
6502
+ "flags": "--account <id-or-label>",
6503
+ "description": "Which account of the app: its connection id or account label",
6504
+ "hidden": false
6505
+ },
6481
6506
  {
6482
6507
  "flags": "--describe <tool>",
6483
6508
  "description": "Print the full input schema for one tool",
@@ -6519,6 +6544,11 @@ export const COMMAND_CATALOG = [
6519
6544
  "description": "Connection id (acct_…)",
6520
6545
  "hidden": false
6521
6546
  },
6547
+ {
6548
+ "flags": "--account <id-or-label>",
6549
+ "description": "Which account of the app: its connection id or account label",
6550
+ "hidden": false
6551
+ },
6522
6552
  {
6523
6553
  "flags": "--company <slug>",
6524
6554
  "description": "Company slug, e.g. indigo",
@@ -6840,6 +6870,16 @@ export const COMMAND_CATALOG = [
6840
6870
  "description": "Connect again even if this app is already connected",
6841
6871
  "hidden": false
6842
6872
  },
6873
+ {
6874
+ "flags": "--personal",
6875
+ "description": "Connect it as your own personal connection (not --company <slug>, which picks the company)",
6876
+ "hidden": false
6877
+ },
6878
+ {
6879
+ "flags": "--company-owned",
6880
+ "description": "Connect it as a connection the whole company owns (needs permission to add company apps)",
6881
+ "hidden": false
6882
+ },
6843
6883
  {
6844
6884
  "flags": "--json",
6845
6885
  "description": "Machine-readable output",
@@ -6926,6 +6966,16 @@ export const COMMAND_CATALOG = [
6926
6966
  "description": "For a revoked row, run `connect <domain>` to re-add and revive it",
6927
6967
  "hidden": false
6928
6968
  },
6969
+ {
6970
+ "flags": "--personal",
6971
+ "description": "With --connect: re-add it as your own personal connection",
6972
+ "hidden": false
6973
+ },
6974
+ {
6975
+ "flags": "--company-owned",
6976
+ "description": "With --connect: re-add it as a connection the whole company owns",
6977
+ "hidden": false
6978
+ },
6929
6979
  {
6930
6980
  "flags": "--json",
6931
6981
  "description": "Machine-readable output",
@@ -7698,7 +7748,7 @@ export const COMMAND_CATALOG = [
7698
7748
  },
7699
7749
  {
7700
7750
  "flags": "--repo <dir>",
7701
- "description": "With files: apply this repo's committed .gitleaks.toml allowlists, matching repo-relative paths",
7751
+ "description": "With files: apply this repo's committed .gitleaks.toml allowlists, matching repo-relative paths. With --range: the repo to scan (default: the current directory)",
7702
7752
  "hidden": false
7703
7753
  },
7704
7754
  {
@@ -7710,6 +7760,11 @@ export const COMMAND_CATALOG = [
7710
7760
  "flags": "--allowlist-rev <rev>",
7711
7761
  "description": "With --repo: read .gitleaks.toml from this commit instead of HEAD",
7712
7762
  "hidden": false
7763
+ },
7764
+ {
7765
+ "flags": "--range <range>",
7766
+ "description": "Scan the lines added in <base>..<head> (head defaults to HEAD), every commit included, with the base's .gitleaks.toml allowlists and the repo's [[rules]]",
7767
+ "hidden": false
7713
7768
  }
7714
7769
  ],
7715
7770
  "subcommands": []
@@ -10370,6 +10425,49 @@ export const COMMAND_CATALOG = [
10370
10425
  ],
10371
10426
  "subcommands": []
10372
10427
  },
10428
+ {
10429
+ "name": "pull",
10430
+ "description": "Download one file from an Outpost through a short-lived download link (for files too large for exec output). <outpost> is the Outpost id (`primary` for your first box). The remote path must be inside the Outpost user's home or HQ dir; relative and ~ paths start at the home dir. The file is saved to [local-path], or to the remote file name in the current directory.",
10431
+ "aliases": [],
10432
+ "hidden": false,
10433
+ "helpOption": true,
10434
+ "helpCommand": false,
10435
+ "usage": "[options] <outpost> <remote-path> [local-path]",
10436
+ "helpText": [],
10437
+ "arguments": [
10438
+ {
10439
+ "name": "outpost",
10440
+ "required": true,
10441
+ "variadic": false,
10442
+ "description": ""
10443
+ },
10444
+ {
10445
+ "name": "remote-path",
10446
+ "required": true,
10447
+ "variadic": false,
10448
+ "description": ""
10449
+ },
10450
+ {
10451
+ "name": "local-path",
10452
+ "required": false,
10453
+ "variadic": false,
10454
+ "description": ""
10455
+ }
10456
+ ],
10457
+ "options": [
10458
+ {
10459
+ "flags": "--force",
10460
+ "description": "Replace the local file if it already exists",
10461
+ "hidden": false
10462
+ },
10463
+ {
10464
+ "flags": "--json",
10465
+ "description": "Emit raw JSON",
10466
+ "hidden": false
10467
+ }
10468
+ ],
10469
+ "subcommands": []
10470
+ },
10373
10471
  {
10374
10472
  "name": "terminal",
10375
10473
  "description": "Open an interactive terminal on an Outpost — a real TTY over SSM Session Manager (no SSH, no local AWS credentials). Requires the AWS session-manager-plugin installed locally. With --forward, tunnels a port on the box to your machine instead of opening a shell.",
@@ -22251,6 +22349,25 @@ export const COMMAND_CATALOG = [
22251
22349
  "options": [],
22252
22350
  "subcommands": []
22253
22351
  },
22352
+ {
22353
+ "name": "sync-core-reconcile",
22354
+ "description": "Read each company's new-sync-engine switch and write this machine's engine homes and legacy off-switches",
22355
+ "aliases": [],
22356
+ "hidden": false,
22357
+ "helpOption": true,
22358
+ "helpCommand": false,
22359
+ "usage": "[options]",
22360
+ "helpText": [],
22361
+ "arguments": [],
22362
+ "options": [
22363
+ {
22364
+ "flags": "--hq-root <path>",
22365
+ "description": "HQ folder the engine works against",
22366
+ "hidden": false
22367
+ }
22368
+ ],
22369
+ "subcommands": []
22370
+ },
22254
22371
  {
22255
22372
  "name": "remote-control",
22256
22373
  "description": "Keep `claude remote-control` running in a tmux session in the HQ folder",
@@ -22399,6 +22516,73 @@ export const COMMAND_CATALOG = [
22399
22516
  }
22400
22517
  ]
22401
22518
  },
22519
+ {
22520
+ "name": "sync-core",
22521
+ "description": "Move this machine's vaults between the current sync and the new sync engine, following each company's switch",
22522
+ "aliases": [],
22523
+ "hidden": true,
22524
+ "helpOption": true,
22525
+ "helpCommand": true,
22526
+ "usage": "[options] [command]",
22527
+ "helpText": [],
22528
+ "arguments": [],
22529
+ "options": [],
22530
+ "subcommands": [
22531
+ {
22532
+ "name": "reconcile",
22533
+ "description": "Read each company's switch, install the engine when needed, and write the engine homes and legacy off-switches",
22534
+ "aliases": [],
22535
+ "hidden": false,
22536
+ "helpOption": true,
22537
+ "helpCommand": false,
22538
+ "usage": "[options]",
22539
+ "helpText": [],
22540
+ "arguments": [],
22541
+ "options": [
22542
+ {
22543
+ "flags": "--hq-root <path>",
22544
+ "description": "HQ folder (default: the daemon's)",
22545
+ "hidden": false
22546
+ },
22547
+ {
22548
+ "flags": "--companies-only",
22549
+ "description": "Never move the personal vault (agent boxes)",
22550
+ "hidden": false
22551
+ },
22552
+ {
22553
+ "flags": "--token-file <path>",
22554
+ "description": "Token file the engine reads (default: this user's hq sign-in)",
22555
+ "hidden": false
22556
+ },
22557
+ {
22558
+ "flags": "--json",
22559
+ "description": "Print the written rollout state as JSON",
22560
+ "hidden": false
22561
+ }
22562
+ ],
22563
+ "subcommands": []
22564
+ },
22565
+ {
22566
+ "name": "status",
22567
+ "description": "Show which vaults are on the new sync engine on this machine",
22568
+ "aliases": [],
22569
+ "hidden": false,
22570
+ "helpOption": true,
22571
+ "helpCommand": false,
22572
+ "usage": "[options]",
22573
+ "helpText": [],
22574
+ "arguments": [],
22575
+ "options": [
22576
+ {
22577
+ "flags": "--json",
22578
+ "description": "Print JSON",
22579
+ "hidden": false
22580
+ }
22581
+ ],
22582
+ "subcommands": []
22583
+ }
22584
+ ]
22585
+ },
22402
22586
  {
22403
22587
  "name": "bot",
22404
22588
  "description": "Personal local bots that run on this computer with your own model login",
@@ -22596,8 +22780,12 @@ export const COMMAND_CATALOG = [
22596
22780
  {
22597
22781
  "flags": "--runtime <runtime>",
22598
22782
  "description": "Model CLI to run the bot with: claude|codex|grok",
22599
- "hidden": false,
22600
- "defaultValue": "claude"
22783
+ "hidden": false
22784
+ },
22785
+ {
22786
+ "flags": "--hq-fleet-runtime",
22787
+ "description": "Create this personal bot with the local HQ Fleet Runtime",
22788
+ "hidden": false
22601
22789
  },
22602
22790
  {
22603
22791
  "flags": "--model <model>",
@@ -22933,6 +23121,62 @@ export const COMMAND_CATALOG = [
22933
23121
  ],
22934
23122
  "subcommands": []
22935
23123
  },
23124
+ {
23125
+ "name": "auth",
23126
+ "description": "Sign a runtime-backed personal bot in to ChatGPT, Grok, Claude, or another supported provider",
23127
+ "aliases": [],
23128
+ "hidden": false,
23129
+ "helpOption": true,
23130
+ "helpCommand": false,
23131
+ "usage": "[options] <name>",
23132
+ "helpText": [],
23133
+ "arguments": [
23134
+ {
23135
+ "name": "name",
23136
+ "required": true,
23137
+ "variadic": false,
23138
+ "description": ""
23139
+ }
23140
+ ],
23141
+ "options": [
23142
+ {
23143
+ "flags": "--provider <provider>",
23144
+ "description": "Provider: chatgpt, grok, claude, or a runtime provider id",
23145
+ "hidden": false
23146
+ },
23147
+ {
23148
+ "flags": "--model <model>",
23149
+ "description": "Model id; required for providers without a built-in personal-bot default",
23150
+ "hidden": false
23151
+ },
23152
+ {
23153
+ "flags": "--api-key",
23154
+ "description": "Read an API key from stdin instead of starting device authorization",
23155
+ "hidden": false
23156
+ },
23157
+ {
23158
+ "flags": "--base-url <url>",
23159
+ "description": "Provider API base URL; required for custom runtime providers",
23160
+ "hidden": false
23161
+ },
23162
+ {
23163
+ "flags": "--transport <transport>",
23164
+ "description": "Provider transport: chat_completions, anthropic_messages, or codex_responses",
23165
+ "hidden": false
23166
+ },
23167
+ {
23168
+ "flags": "--no-browser",
23169
+ "description": "Do not open a browser during device authorization",
23170
+ "hidden": false
23171
+ },
23172
+ {
23173
+ "flags": "--json",
23174
+ "description": "Print machine-readable JSON",
23175
+ "hidden": false
23176
+ }
23177
+ ],
23178
+ "subcommands": []
23179
+ },
22936
23180
  {
22937
23181
  "name": "logs",
22938
23182
  "description": "Show the bot's log",
@@ -515,6 +515,13 @@ export declare const REGISTRATION_PLAN: readonly [{
515
515
  readonly module: "./commands/daemon.js";
516
516
  readonly exportName: "registerDaemonCommand";
517
517
  readonly hidden: true;
518
+ }, {
519
+ readonly type: "registrar";
520
+ readonly root: "sync-core";
521
+ readonly parent: "program";
522
+ readonly module: "./commands/sync-core.js";
523
+ readonly exportName: "registerSyncCoreCommand";
524
+ readonly hidden: true;
518
525
  }, {
519
526
  readonly type: "registrar";
520
527
  readonly root: "bot";
@@ -88,6 +88,7 @@ export const REGISTRATION_PLAN = [
88
88
  { type: "registrar", root: "lanes", parent: "program", module: "./commands/lanes.js", exportName: "registerLanesCommand" },
89
89
  { type: "registrar", root: "monitor", parent: "program", module: "./commands/monitor.js", exportName: "registerMonitorCommand" },
90
90
  { type: "registrar", root: "daemon", parent: "program", module: "./commands/daemon.js", exportName: "registerDaemonCommand", hidden: true },
91
+ { type: "registrar", root: "sync-core", parent: "program", module: "./commands/sync-core.js", exportName: "registerSyncCoreCommand", hidden: true },
91
92
  { type: "registrar", root: "bot", parent: "program", module: "./commands/bot.js", exportName: "registerBotCommand" },
92
93
  { type: "registrar", root: "report", parent: "program", module: "./commands/report.js", exportName: "registerReportCommand" },
93
94
  { type: "group", root: "deploy", id: "deploy", name: "deploy", description: "Manage deployments in hq-deploy" },
@@ -16,7 +16,7 @@
16
16
  import { Command } from "commander";
17
17
  import { type CompanyBrowseClientFactory, type FilesBrowseVaultClient, type RunGetResult } from "./files-browse.js";
18
18
  import { type AclLock } from "./acl-lock.js";
19
- export type AccessOutcome = "never-existed" | "local" | "not-synced" | "no-access" | "ambiguous" | "pending-confirmation";
19
+ export type AccessOutcome = "never-existed" | "local" | "not-synced" | "no-access" | "no-write-access" | "ambiguous" | "pending-confirmation";
20
20
  export interface AccessAclTree {
21
21
  prefix: string;
22
22
  direct: Array<{
@@ -151,6 +151,15 @@ export interface RunAccessResult {
151
151
  * grants (including a root `*` company-wide grant) do not apply.
152
152
  */
153
153
  lockedBy?: AclLock;
154
+ /**
155
+ * Set with outcome "no-write-access": the ACL row that blocks writes to a
156
+ * path that is on disk but not in the vault, so a push cannot upload it.
157
+ * Anchored as companies/<slug>/<prefix>; absent when only the effective
158
+ * permission is known.
159
+ */
160
+ writeBlockedBy?: string;
161
+ /** Effective permission on the path when outcome is "no-write-access". */
162
+ permission?: string | null;
154
163
  }
155
164
  export interface AccessAsResult {
156
165
  personUid: string;
@@ -164,6 +173,28 @@ export interface AccessAsResult {
164
173
  export declare function outcomeExitCode(outcome: AccessOutcome): 0 | 2 | 3 | 4;
165
174
  export declare function formatAccessOutcome(result: RunAccessResult): string;
166
175
  export declare function resolveGrantor(tree: AccessAclTree | null, members: AccessMember[]): AccessGrantor | null;
176
+ /**
177
+ * ACL evidence for one key. `tree` is the full acl/tree response (which keeps
178
+ * the deny / no-record distinction and the ACL creator for grantor lookup).
179
+ * `resolved` is a non-null permission from the batch acl/resolve route.
180
+ */
181
+ export type AclVerdict = {
182
+ kind: "tree";
183
+ tree: AccessAclTree | null;
184
+ } | {
185
+ kind: "resolved";
186
+ permission: string;
187
+ };
188
+ /**
189
+ * Why the caller cannot write `relative`, or null when it can or the ACL says
190
+ * nothing (no record: membership-distributed access decides, which this
191
+ * command cannot see). The tree is fetched when the fast resolve path gave
192
+ * only a permission, so the blocking row can be named.
193
+ */
194
+ export declare function writeBlocker(verdict: AclVerdict, relative: string, deps: Pick<AccessDeps, "fetchAclTree" | "companySlug">): Promise<{
195
+ permission: string | null;
196
+ blockedBy?: string;
197
+ } | null>;
167
198
  export declare function runAccess(input: RunAccessInput): Promise<RunAccessResult>;
168
199
  export interface RunAccessBatchInput {
169
200
  targets: string[];
@@ -26,6 +26,7 @@ import { listActiveMembers } from "./members.js";
26
26
  import { aclLockedBy } from "./acl-lock.js";
27
27
  import { buildDmBody } from "./dm.js";
28
28
  import { defaultAudienceFor, recipientKindOf } from "../lib/notify-recipient.js";
29
+ import { callerIsMachinePrincipal } from "../lib/caller-principal.js";
29
30
  import { peekIdToken } from "../utils/id-token.js";
30
31
  import { findRecentAccessRequest, formatTimeAgo, recordAccessRequest, } from "../utils/access-requests.js";
31
32
  import { recordAccessOutcome } from "../utils/access-outcomes.js";
@@ -34,8 +35,11 @@ export function outcomeExitCode(outcome) {
34
35
  return 0;
35
36
  if (outcome === "never-existed")
36
37
  return 2;
37
- if (outcome === "no-access" || outcome === "pending-confirmation")
38
+ if (outcome === "no-access" ||
39
+ outcome === "no-write-access" ||
40
+ outcome === "pending-confirmation") {
38
41
  return 3;
42
+ }
39
43
  return 4;
40
44
  }
41
45
  export function formatAccessOutcome(result) {
@@ -57,6 +61,16 @@ export function formatAccessOutcome(result) {
57
61
  lines.push(`You do not have read access to this path.`);
58
62
  lines.push(result.path);
59
63
  break;
64
+ case "no-write-access":
65
+ lines.push(`The file is on this computer but not in the ${result.company} vault, and you cannot write to it, so a push cannot upload it.`);
66
+ lines.push(result.path);
67
+ if (result.writeBlockedBy) {
68
+ lines.push(`Write is blocked by the ACL on ${result.writeBlockedBy} (your permission: ${result.permission ?? "none"}). Ask its creator or a company admin for write access.`);
69
+ }
70
+ else {
71
+ lines.push(`Your permission on this path is ${result.permission ?? "none"}. Ask a company admin for write access.`);
72
+ }
73
+ break;
60
74
  case "pending-confirmation":
61
75
  lines.push(`Ask the grantor for read access to this path.`);
62
76
  lines.push(result.path);
@@ -243,6 +257,69 @@ function startEarly(promise) {
243
257
  promise.catch(() => undefined);
244
258
  return promise;
245
259
  }
260
+ const WRITE_PERMISSIONS = new Set(["write", "admin", "owner"]);
261
+ /**
262
+ * Why the caller cannot write `relative`, or null when it can or the ACL says
263
+ * nothing (no record: membership-distributed access decides, which this
264
+ * command cannot see). The tree is fetched when the fast resolve path gave
265
+ * only a permission, so the blocking row can be named.
266
+ */
267
+ export async function writeBlocker(verdict, relative, deps) {
268
+ const resolvedPermission = verdict.kind === "resolved" ? verdict.permission : undefined;
269
+ if (resolvedPermission && WRITE_PERMISSIONS.has(resolvedPermission))
270
+ return null;
271
+ let tree = verdict.kind === "tree" ? verdict.tree : null;
272
+ if (verdict.kind === "resolved") {
273
+ try {
274
+ tree = await deps.fetchAclTree(relative);
275
+ }
276
+ catch {
277
+ tree = null;
278
+ }
279
+ }
280
+ const permission = resolvedPermission ?? tree?.effectivePermission ?? null;
281
+ if (permission && WRITE_PERMISSIONS.has(permission))
282
+ return null;
283
+ const covering = tree?.coveringAclPath;
284
+ if (typeof covering === "string" && covering !== "") {
285
+ return {
286
+ permission,
287
+ blockedBy: toBucketRelative(covering, deps.companySlug),
288
+ };
289
+ }
290
+ // A row on the path itself with a known non-write permission blocks it.
291
+ // With no permission reported, nothing proves the path is blocked.
292
+ if (permission && tree?.directRow) {
293
+ return { permission, blockedBy: toBucketRelative(tree.prefix, deps.companySlug) };
294
+ }
295
+ // No ACL row covers the path. A known non-write permission still blocks.
296
+ if (resolvedPermission)
297
+ return { permission };
298
+ return null;
299
+ }
300
+ /**
301
+ * Owners and admins write past ACL rows on the server (role bypass), which
302
+ * the ACL tree does not always show. Treat a lookup failure as no bypass only
303
+ * when the member list loads; if it cannot be read, assume a bypass so the
304
+ * command keeps its old "never-existed" answer instead of a wrong blocker.
305
+ */
306
+ async function callerHasRoleBypass(deps) {
307
+ try {
308
+ const [members, requester] = await Promise.all([
309
+ deps.listMembers(),
310
+ resolveRequester(deps.requester),
311
+ ]);
312
+ const email = (requester.email ?? "").trim().toLowerCase();
313
+ // An unknown caller cannot be matched; keep the old answer.
314
+ if (email === "")
315
+ return true;
316
+ return members.some((m) => (m.personEmail ?? "").trim().toLowerCase() === email &&
317
+ (m.role === "owner" || m.role === "admin"));
318
+ }
319
+ catch {
320
+ return true;
321
+ }
322
+ }
246
323
  function verdictDenied(verdict) {
247
324
  return verdict.kind === "tree" ? aclDenied(verdict.tree) : false;
248
325
  }
@@ -388,6 +465,31 @@ async function locateTarget(target, deps, ctx) {
388
465
  },
389
466
  };
390
467
  }
468
+ // feedback_5baccb9e: a file that is on disk but absent from the vault
469
+ // is usually one a push could not upload. When the ACL blocks writes,
470
+ // say so instead of "never existed".
471
+ if (relative !== "" &&
472
+ fs.existsSync(path.join(deps.hqRoot, "companies", slug, relative))) {
473
+ steps.push("disk: present");
474
+ const blocker = await writeBlocker(verdict, relative, deps);
475
+ if (blocker && !(await callerHasRoleBypass(deps))) {
476
+ steps.push(blocker.blockedBy
477
+ ? `acl: write blocked by ${anchored(blocker.blockedBy)} (${blocker.permission ?? "none"})`
478
+ : `acl: no write (${blocker.permission ?? "none"})`);
479
+ return {
480
+ kind: "terminal",
481
+ outcome: "no-write-access",
482
+ fields: {
483
+ path: anchored(relative),
484
+ company: slug,
485
+ exists: false,
486
+ steps,
487
+ permission: blocker.permission,
488
+ ...(blocker.blockedBy ? { writeBlockedBy: anchored(blocker.blockedBy) } : {}),
489
+ },
490
+ };
491
+ }
492
+ }
391
493
  return {
392
494
  kind: "terminal",
393
495
  outcome: "never-existed",
@@ -857,6 +959,11 @@ function jsonPayload(result) {
857
959
  payload.alreadyAskedAt = result.alreadyAskedAt;
858
960
  if (result.lockedBy)
859
961
  payload.lockedBy = result.lockedBy;
962
+ if (result.outcome === "no-write-access") {
963
+ payload.permission = result.permission ?? null;
964
+ if (result.writeBlockedBy)
965
+ payload.writeBlockedBy = result.writeBlockedBy;
966
+ }
860
967
  return payload;
861
968
  }
862
969
  function emit(input, result, log) {
@@ -933,7 +1040,9 @@ export function registerAccessCommand(program) {
933
1040
  Exit codes:
934
1041
  0 local or not-synced (file exists and you can read it)
935
1042
  2 never-existed (the path was never created)
936
- 3 no-access (the file exists but you cannot read it)
1043
+ 3 no-access (the file exists but you cannot read it), or
1044
+ no-write-access (the file is on disk, not in the vault, and you cannot
1045
+ write to it, so a push cannot upload it)
937
1046
  4 ambiguous (several keys matched; pick one)
938
1047
 
939
1048
  With several paths, each gets its own result block and the exit code is the
@@ -1161,7 +1270,7 @@ async function buildLiveAccessDeps(slug, hqRoot) {
1161
1270
  prompt: msg.prompt,
1162
1271
  details: msg.details,
1163
1272
  now: Date.now(),
1164
- audience: defaultAudienceFor(recipientKindOf(msg.recipient)),
1273
+ audience: defaultAudienceFor(recipientKindOf(msg.recipient), callerIsMachinePrincipal()),
1165
1274
  });
1166
1275
  const res = await vaultApiFetch({
1167
1276
  token,
@@ -75,7 +75,7 @@ export interface KitRuntime {
75
75
  paths: AgentKitPaths;
76
76
  creds: ExternalMachineCreds;
77
77
  config: KitConfig;
78
- getToken: () => Promise<string>;
78
+ getToken: (signal?: AbortSignal) => Promise<string>;
79
79
  log: KitLogger;
80
80
  credsSource?: HeartbeatCredsSource;
81
81
  }