@indigoai-us/hq-cli 5.345.159 → 5.345.161

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 (104) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/assets/scaffold/core/scripts/session-journal.sh +116 -24
  3. package/dist/command-catalog.generated.d.ts +222 -9
  4. package/dist/command-catalog.generated.js +273 -9
  5. package/dist/commands/access.d.ts +6 -0
  6. package/dist/commands/access.js +18 -1
  7. package/dist/commands/acl-lock.d.ts +41 -0
  8. package/dist/commands/acl-lock.js +28 -0
  9. package/dist/commands/agent-enroll.d.ts +2 -0
  10. package/dist/commands/agent-enroll.js +9 -4
  11. package/dist/commands/agents.js +5 -11
  12. package/dist/commands/auth.js +6 -5
  13. package/dist/commands/billing-sponsor.d.ts +4 -4
  14. package/dist/commands/billing-sponsor.js +11 -11
  15. package/dist/commands/billing-usage.d.ts +22 -8
  16. package/dist/commands/billing-usage.js +57 -22
  17. package/dist/commands/billing.d.ts +7 -7
  18. package/dist/commands/billing.js +13 -13
  19. package/dist/commands/cloud-provision.js +2 -2
  20. package/dist/commands/cloud.js +7 -7
  21. package/dist/commands/core-session-helper-runtime.d.ts +1 -0
  22. package/dist/commands/core-session-journal-native.d.ts +1 -0
  23. package/dist/commands/core-session-journal-native.js +158 -34
  24. package/dist/commands/db-remote-deps.js +12 -1
  25. package/dist/commands/feedback.d.ts +12 -0
  26. package/dist/commands/feedback.js +52 -0
  27. package/dist/commands/files-browse.d.ts +8 -0
  28. package/dist/commands/files-browse.js +83 -1
  29. package/dist/commands/files.js +19 -7
  30. package/dist/commands/install-global.d.ts +2 -0
  31. package/dist/commands/install-global.js +24 -1
  32. package/dist/commands/integrations-connect.js +1 -0
  33. package/dist/commands/integrations-manage.d.ts +2 -0
  34. package/dist/commands/integrations-manage.js +6 -4
  35. package/dist/commands/lanes-link.js +5 -3
  36. package/dist/commands/lanes-session-start.d.ts +12 -0
  37. package/dist/commands/lanes-session-start.js +93 -12
  38. package/dist/commands/meetings.d.ts +15 -4
  39. package/dist/commands/meetings.js +58 -19
  40. package/dist/commands/skill.js +6 -6
  41. package/dist/commands/whoami.js +1 -1
  42. package/dist/hook-engine-path.js +13 -2
  43. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +3236 -1988
  44. package/dist/hq-cloud-sync-runner/package.json +1 -1
  45. package/dist/index.js +21 -0
  46. package/dist/lanes-session-start-argv.d.ts +7 -0
  47. package/dist/lanes-session-start-argv.js +9 -0
  48. package/dist/lib/billing/plan-limit-refusals.d.ts +13 -3
  49. package/dist/lib/billing/plan-limit-refusals.js +26 -3
  50. package/dist/lib/billing/plan-limit-upgrade-url.d.ts +8 -0
  51. package/dist/lib/billing/plan-limit-upgrade-url.js +18 -0
  52. package/dist/lib/billing/plan-lock.d.ts +22 -6
  53. package/dist/lib/billing/plan-lock.js +58 -30
  54. package/dist/lib/billing/usage-cost.d.ts +24 -2
  55. package/dist/lib/billing/usage-cost.js +103 -3
  56. package/dist/lib/billing/usage-only.d.ts +37 -1
  57. package/dist/lib/billing/usage-only.js +89 -4
  58. package/dist/lib/billing/usage-pause.d.ts +27 -0
  59. package/dist/lib/billing/usage-pause.js +50 -0
  60. package/dist/lib/bot/log.d.ts +3 -2
  61. package/dist/lib/bot/progress.d.ts +23 -1
  62. package/dist/lib/bot/progress.js +43 -5
  63. package/dist/lib/bot/run.js +2 -0
  64. package/dist/lib/bot/runtime/claude.d.ts +12 -0
  65. package/dist/lib/bot/runtime/claude.js +20 -0
  66. package/dist/lib/bot/runtime/index.d.ts +10 -1
  67. package/dist/lib/bot/runtime/index.js +14 -2
  68. package/dist/lib/bot/runtime/types.d.ts +8 -0
  69. package/dist/lib/db/control-plane.d.ts +5 -0
  70. package/dist/lib/db/control-plane.js +10 -0
  71. package/dist/lib/doctor/checks/ps1-shims.d.ts +21 -0
  72. package/dist/lib/doctor/checks/ps1-shims.js +82 -0
  73. package/dist/lib/doctor/fix/apply.d.ts +13 -1
  74. package/dist/lib/doctor/fix/apply.js +38 -3
  75. package/dist/lib/doctor/fix/remediation.d.ts +9 -1
  76. package/dist/lib/doctor/fix/remediation.js +22 -0
  77. package/dist/lib/doctor/registry.js +2 -0
  78. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  79. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  80. package/dist/lib/hook-engine/resolve.d.ts +14 -0
  81. package/dist/lib/hook-engine/resolve.js +55 -1
  82. package/dist/lib/lanes/links.d.ts +2 -1
  83. package/dist/lib/lanes/links.js +235 -17
  84. package/dist/lib/lanes/registry-flag.d.ts +24 -0
  85. package/dist/lib/lanes/registry-flag.js +72 -6
  86. package/dist/lib/lanes/store.d.ts +4 -2
  87. package/dist/lib/lanes/store.js +8 -8
  88. package/dist/lib/plan-limit-nag.d.ts +0 -16
  89. package/dist/lib/plan-limit-nag.js +17 -18
  90. package/dist/lib/windows/ps1-shims.d.ts +60 -0
  91. package/dist/lib/windows/ps1-shims.js +152 -0
  92. package/dist/main.d.ts +0 -2
  93. package/dist/main.js +19 -57
  94. package/dist/utils/plan-gate-error.d.ts +30 -14
  95. package/dist/utils/plan-gate-error.js +60 -46
  96. package/dist/utils/self-update.d.ts +2 -0
  97. package/dist/utils/self-update.js +8 -0
  98. package/dist/utils/staged-npm-install.js +13 -2
  99. package/dist/utils/team-upgrade.d.ts +19 -6
  100. package/dist/utils/team-upgrade.js +37 -39
  101. package/dist/utils/token-keychain.d.ts +11 -8
  102. package/dist/utils/token-keychain.js +36 -15
  103. package/package.json +8 -8
  104. package/scripts/install-hook-engine.mjs +12 -0
@@ -1674,7 +1674,7 @@ export const COMMAND_CATALOG = [
1674
1674
  },
1675
1675
  {
1676
1676
  "flags": "--company <slug>",
1677
- "description": "Also report this company's workspace plan state (Starter nag)",
1677
+ "description": "Also report this company's billing and usage notice",
1678
1678
  "hidden": false
1679
1679
  }
1680
1680
  ],
@@ -5517,6 +5517,32 @@ export const COMMAND_CATALOG = [
5517
5517
  }
5518
5518
  ],
5519
5519
  "subcommands": []
5520
+ },
5521
+ {
5522
+ "name": "assign",
5523
+ "description": "Reassign a ticket to a person, including one another person has claimed",
5524
+ "aliases": [],
5525
+ "hidden": false,
5526
+ "helpOption": true,
5527
+ "helpCommand": false,
5528
+ "usage": "[options] <id>",
5529
+ "helpText": [],
5530
+ "arguments": [
5531
+ {
5532
+ "name": "id",
5533
+ "required": true,
5534
+ "variadic": false,
5535
+ "description": "Feedback id (feedback_<uuid>)"
5536
+ }
5537
+ ],
5538
+ "options": [
5539
+ {
5540
+ "flags": "--to <assignee>",
5541
+ "description": "New assignee: an email address or a prs_ uid",
5542
+ "hidden": false
5543
+ }
5544
+ ],
5545
+ "subcommands": []
5520
5546
  }
5521
5547
  ]
5522
5548
  },
@@ -5533,7 +5559,7 @@ export const COMMAND_CATALOG = [
5533
5559
  "options": [
5534
5560
  {
5535
5561
  "flags": "--company <slug>",
5536
- "description": "Company slug (for multi-company users)",
5562
+ "description": "Company slug (defaults to the company this HQ session is bound to)",
5537
5563
  "hidden": false
5538
5564
  },
5539
5565
  {
@@ -6624,6 +6650,11 @@ export const COMMAND_CATALOG = [
6624
6650
  "description": "Read that app's client secret from stdin",
6625
6651
  "hidden": false
6626
6652
  },
6653
+ {
6654
+ "flags": "--scopes <list>",
6655
+ "description": "Permissions to request, space- or comma-separated (default: the permissions the app was approved for last time)",
6656
+ "hidden": false
6657
+ },
6627
6658
  {
6628
6659
  "flags": "--no-browser",
6629
6660
  "description": "Print the sign-in URL instead of opening a browser",
@@ -6866,7 +6897,7 @@ export const COMMAND_CATALOG = [
6866
6897
  },
6867
6898
  {
6868
6899
  "flags": "--to <principal>",
6869
- "description": "Email, uid, or `everyone`",
6900
+ "description": "Email, uid, vault group (grp_…), or `everyone`",
6870
6901
  "hidden": false
6871
6902
  },
6872
6903
  {
@@ -6923,7 +6954,7 @@ export const COMMAND_CATALOG = [
6923
6954
  },
6924
6955
  {
6925
6956
  "flags": "--from <principal>",
6926
- "description": "Email, uid, or `everyone`",
6957
+ "description": "Email, uid, vault group (grp_…), or `everyone`",
6927
6958
  "hidden": false
6928
6959
  },
6929
6960
  {
@@ -7010,7 +7041,7 @@ export const COMMAND_CATALOG = [
7010
7041
  },
7011
7042
  {
7012
7043
  "flags": "--with <principal>",
7013
- "description": "Email, uid, or `everyone`",
7044
+ "description": "Email, uid, vault group (grp_…), or `everyone`",
7014
7045
  "hidden": false
7015
7046
  },
7016
7047
  {
@@ -7062,7 +7093,7 @@ export const COMMAND_CATALOG = [
7062
7093
  },
7063
7094
  {
7064
7095
  "flags": "--from <principal>",
7065
- "description": "Email, uid, or `everyone`",
7096
+ "description": "Email, uid, vault group (grp_…), or `everyone`",
7066
7097
  "hidden": false
7067
7098
  },
7068
7099
  {
@@ -10339,7 +10370,7 @@ export const COMMAND_CATALOG = [
10339
10370
  },
10340
10371
  {
10341
10372
  "name": "upgrade",
10342
- "description": "Open Stripe Checkout to upgrade a company on an earlier plan (usage-pricing companies have no plan to upgrade)",
10373
+ "description": "Open the switch to usage pricing for a company on an earlier plan (usage-pricing companies have nothing to change)",
10343
10374
  "aliases": [],
10344
10375
  "hidden": false,
10345
10376
  "helpOption": true,
@@ -10355,12 +10386,12 @@ export const COMMAND_CATALOG = [
10355
10386
  },
10356
10387
  {
10357
10388
  "flags": "--no-browser",
10358
- "description": "Print the Stripe Checkout URL instead of opening it",
10389
+ "description": "Print the URL instead of opening it",
10359
10390
  "hidden": false
10360
10391
  },
10361
10392
  {
10362
10393
  "flags": "--json",
10363
- "description": "Emit the Checkout URL as JSON",
10394
+ "description": "Emit the URL as JSON",
10364
10395
  "hidden": false
10365
10396
  }
10366
10397
  ],
@@ -16486,6 +16517,11 @@ export const COMMAND_CATALOG = [
16486
16517
  "helpText": [],
16487
16518
  "arguments": [],
16488
16519
  "options": [
16520
+ {
16521
+ "flags": "--refresh-flags",
16522
+ "description": "Refresh the offline session-start flag values from the registry",
16523
+ "hidden": false
16524
+ },
16489
16525
  {
16490
16526
  "flags": "--json",
16491
16527
  "description": "JSON on stdout (always)",
@@ -16557,6 +16593,11 @@ export const COMMAND_CATALOG = [
16557
16593
  "description": "Host session id",
16558
16594
  "hidden": false
16559
16595
  },
16596
+ {
16597
+ "flags": "--claims <ids>",
16598
+ "description": "Exact comma-separated recipient receipt IDs",
16599
+ "hidden": false
16600
+ },
16560
16601
  {
16561
16602
  "flags": "--text <message>",
16562
16603
  "description": "Message text",
@@ -16636,6 +16677,11 @@ export const COMMAND_CATALOG = [
16636
16677
  "description": "Host session id",
16637
16678
  "hidden": false
16638
16679
  },
16680
+ {
16681
+ "flags": "--claims <ids>",
16682
+ "description": "Exact comma-separated recipient receipt IDs",
16683
+ "hidden": false
16684
+ },
16639
16685
  {
16640
16686
  "flags": "--text <message>",
16641
16687
  "description": "Message text",
@@ -16715,6 +16761,179 @@ export const COMMAND_CATALOG = [
16715
16761
  "description": "Host session id",
16716
16762
  "hidden": false
16717
16763
  },
16764
+ {
16765
+ "flags": "--claims <ids>",
16766
+ "description": "Exact comma-separated recipient receipt IDs",
16767
+ "hidden": false
16768
+ },
16769
+ {
16770
+ "flags": "--text <message>",
16771
+ "description": "Message text",
16772
+ "hidden": false
16773
+ },
16774
+ {
16775
+ "flags": "--text-file <path>",
16776
+ "description": "Read message text from a file",
16777
+ "hidden": false
16778
+ },
16779
+ {
16780
+ "flags": "--state <state>",
16781
+ "description": "Child state",
16782
+ "hidden": false
16783
+ },
16784
+ {
16785
+ "flags": "--note <note>",
16786
+ "description": "State note",
16787
+ "hidden": false
16788
+ },
16789
+ {
16790
+ "flags": "--timeout <seconds>",
16791
+ "description": "Wait timeout in seconds",
16792
+ "hidden": false
16793
+ }
16794
+ ],
16795
+ "subcommands": []
16796
+ },
16797
+ {
16798
+ "name": "ack",
16799
+ "description": "",
16800
+ "aliases": [],
16801
+ "hidden": false,
16802
+ "helpOption": true,
16803
+ "helpCommand": false,
16804
+ "usage": "[options]",
16805
+ "helpText": [],
16806
+ "arguments": [],
16807
+ "options": [
16808
+ {
16809
+ "flags": "--json",
16810
+ "description": "Print JSON output (the default)",
16811
+ "hidden": false
16812
+ },
16813
+ {
16814
+ "flags": "--session-id <id>",
16815
+ "description": "Act as this session",
16816
+ "hidden": false
16817
+ },
16818
+ {
16819
+ "flags": "--link <id>",
16820
+ "description": "Conduct link id",
16821
+ "hidden": false
16822
+ },
16823
+ {
16824
+ "flags": "--child <name|all>",
16825
+ "description": "Child name or all children",
16826
+ "hidden": false
16827
+ },
16828
+ {
16829
+ "flags": "--name <name>",
16830
+ "description": "Child name",
16831
+ "hidden": false
16832
+ },
16833
+ {
16834
+ "flags": "--engine <engine>",
16835
+ "description": "Session engine",
16836
+ "hidden": false
16837
+ },
16838
+ {
16839
+ "flags": "--title <title>",
16840
+ "description": "Session title",
16841
+ "hidden": false
16842
+ },
16843
+ {
16844
+ "flags": "--host-session <id>",
16845
+ "description": "Host session id",
16846
+ "hidden": false
16847
+ },
16848
+ {
16849
+ "flags": "--claims <ids>",
16850
+ "description": "Exact comma-separated recipient receipt IDs",
16851
+ "hidden": false
16852
+ },
16853
+ {
16854
+ "flags": "--text <message>",
16855
+ "description": "Message text",
16856
+ "hidden": false
16857
+ },
16858
+ {
16859
+ "flags": "--text-file <path>",
16860
+ "description": "Read message text from a file",
16861
+ "hidden": false
16862
+ },
16863
+ {
16864
+ "flags": "--state <state>",
16865
+ "description": "Child state",
16866
+ "hidden": false
16867
+ },
16868
+ {
16869
+ "flags": "--note <note>",
16870
+ "description": "State note",
16871
+ "hidden": false
16872
+ },
16873
+ {
16874
+ "flags": "--timeout <seconds>",
16875
+ "description": "Wait timeout in seconds",
16876
+ "hidden": false
16877
+ }
16878
+ ],
16879
+ "subcommands": []
16880
+ },
16881
+ {
16882
+ "name": "capabilities",
16883
+ "description": "",
16884
+ "aliases": [],
16885
+ "hidden": false,
16886
+ "helpOption": true,
16887
+ "helpCommand": false,
16888
+ "usage": "[options]",
16889
+ "helpText": [],
16890
+ "arguments": [],
16891
+ "options": [
16892
+ {
16893
+ "flags": "--json",
16894
+ "description": "Print JSON output (the default)",
16895
+ "hidden": false
16896
+ },
16897
+ {
16898
+ "flags": "--session-id <id>",
16899
+ "description": "Act as this session",
16900
+ "hidden": false
16901
+ },
16902
+ {
16903
+ "flags": "--link <id>",
16904
+ "description": "Conduct link id",
16905
+ "hidden": false
16906
+ },
16907
+ {
16908
+ "flags": "--child <name|all>",
16909
+ "description": "Child name or all children",
16910
+ "hidden": false
16911
+ },
16912
+ {
16913
+ "flags": "--name <name>",
16914
+ "description": "Child name",
16915
+ "hidden": false
16916
+ },
16917
+ {
16918
+ "flags": "--engine <engine>",
16919
+ "description": "Session engine",
16920
+ "hidden": false
16921
+ },
16922
+ {
16923
+ "flags": "--title <title>",
16924
+ "description": "Session title",
16925
+ "hidden": false
16926
+ },
16927
+ {
16928
+ "flags": "--host-session <id>",
16929
+ "description": "Host session id",
16930
+ "hidden": false
16931
+ },
16932
+ {
16933
+ "flags": "--claims <ids>",
16934
+ "description": "Exact comma-separated recipient receipt IDs",
16935
+ "hidden": false
16936
+ },
16718
16937
  {
16719
16938
  "flags": "--text <message>",
16720
16939
  "description": "Message text",
@@ -16794,6 +17013,11 @@ export const COMMAND_CATALOG = [
16794
17013
  "description": "Host session id",
16795
17014
  "hidden": false
16796
17015
  },
17016
+ {
17017
+ "flags": "--claims <ids>",
17018
+ "description": "Exact comma-separated recipient receipt IDs",
17019
+ "hidden": false
17020
+ },
16797
17021
  {
16798
17022
  "flags": "--text <message>",
16799
17023
  "description": "Message text",
@@ -16873,6 +17097,11 @@ export const COMMAND_CATALOG = [
16873
17097
  "description": "Host session id",
16874
17098
  "hidden": false
16875
17099
  },
17100
+ {
17101
+ "flags": "--claims <ids>",
17102
+ "description": "Exact comma-separated recipient receipt IDs",
17103
+ "hidden": false
17104
+ },
16876
17105
  {
16877
17106
  "flags": "--text <message>",
16878
17107
  "description": "Message text",
@@ -16952,6 +17181,11 @@ export const COMMAND_CATALOG = [
16952
17181
  "description": "Host session id",
16953
17182
  "hidden": false
16954
17183
  },
17184
+ {
17185
+ "flags": "--claims <ids>",
17186
+ "description": "Exact comma-separated recipient receipt IDs",
17187
+ "hidden": false
17188
+ },
16955
17189
  {
16956
17190
  "flags": "--text <message>",
16957
17191
  "description": "Message text",
@@ -17031,6 +17265,11 @@ export const COMMAND_CATALOG = [
17031
17265
  "description": "Host session id",
17032
17266
  "hidden": false
17033
17267
  },
17268
+ {
17269
+ "flags": "--claims <ids>",
17270
+ "description": "Exact comma-separated recipient receipt IDs",
17271
+ "hidden": false
17272
+ },
17034
17273
  {
17035
17274
  "flags": "--text <message>",
17036
17275
  "description": "Message text",
@@ -17110,6 +17349,11 @@ export const COMMAND_CATALOG = [
17110
17349
  "description": "Host session id",
17111
17350
  "hidden": false
17112
17351
  },
17352
+ {
17353
+ "flags": "--claims <ids>",
17354
+ "description": "Exact comma-separated recipient receipt IDs",
17355
+ "hidden": false
17356
+ },
17113
17357
  {
17114
17358
  "flags": "--text <message>",
17115
17359
  "description": "Message text",
@@ -17189,6 +17433,11 @@ export const COMMAND_CATALOG = [
17189
17433
  "description": "Host session id",
17190
17434
  "hidden": false
17191
17435
  },
17436
+ {
17437
+ "flags": "--claims <ids>",
17438
+ "description": "Exact comma-separated recipient receipt IDs",
17439
+ "hidden": false
17440
+ },
17192
17441
  {
17193
17442
  "flags": "--text <message>",
17194
17443
  "description": "Message text",
@@ -17268,6 +17517,11 @@ export const COMMAND_CATALOG = [
17268
17517
  "description": "Host session id",
17269
17518
  "hidden": false
17270
17519
  },
17520
+ {
17521
+ "flags": "--claims <ids>",
17522
+ "description": "Exact comma-separated recipient receipt IDs",
17523
+ "hidden": false
17524
+ },
17271
17525
  {
17272
17526
  "flags": "--text <message>",
17273
17527
  "description": "Message text",
@@ -17347,6 +17601,11 @@ export const COMMAND_CATALOG = [
17347
17601
  "description": "Host session id",
17348
17602
  "hidden": false
17349
17603
  },
17604
+ {
17605
+ "flags": "--claims <ids>",
17606
+ "description": "Exact comma-separated recipient receipt IDs",
17607
+ "hidden": false
17608
+ },
17350
17609
  {
17351
17610
  "flags": "--text <message>",
17352
17611
  "description": "Message text",
@@ -17391,6 +17650,11 @@ export const COMMAND_CATALOG = [
17391
17650
  "description": "Hook event",
17392
17651
  "hidden": false
17393
17652
  },
17653
+ {
17654
+ "flags": "--ack-mode <mode>",
17655
+ "description": "Internal explicit recipient acknowledgement",
17656
+ "hidden": false
17657
+ },
17394
17658
  {
17395
17659
  "flags": "--json",
17396
17660
  "description": "JSON on stdout (always)",
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { Command } from "commander";
17
17
  import { type CompanyBrowseClientFactory, type FilesBrowseVaultClient, type RunGetResult } from "./files-browse.js";
18
+ import { type AclLock } from "./acl-lock.js";
18
19
  export type AccessOutcome = "never-existed" | "local" | "not-synced" | "no-access" | "ambiguous" | "pending-confirmation";
19
20
  export interface AccessAclTree {
20
21
  prefix: string;
@@ -145,6 +146,11 @@ export interface RunAccessResult {
145
146
  requestSentAt?: string;
146
147
  alreadyAskedAt?: string;
147
148
  requestNote?: string;
149
+ /**
150
+ * Most-specific ACL row covering the path when it has no grants. Broader
151
+ * grants (including a root `*` company-wide grant) do not apply.
152
+ */
153
+ lockedBy?: AclLock;
148
154
  }
149
155
  export interface AccessAsResult {
150
156
  personUid: string;
@@ -23,6 +23,7 @@ import { getCompanyUid, vaultApiFetch, resolveCallerUid } from "../utils/vault-a
23
23
  import { parseCompanySlugFromPath, classifyAclSource, resolveBrowseSession, makeCompanyPresignFactory, runGet, toBucketRelative, } from "./files-browse.js";
24
24
  import { listJournals, syncDoctor, } from "@indigoai-us/hq-cloud";
25
25
  import { listActiveMembers } from "./members.js";
26
+ import { aclLockedBy } from "./acl-lock.js";
26
27
  import { buildDmBody } from "./dm.js";
27
28
  import { peekIdToken } from "../utils/id-token.js";
28
29
  import { findRecentAccessRequest, formatTimeAgo, recordAccessRequest, } from "../utils/access-requests.js";
@@ -458,10 +459,23 @@ async function completeTarget(input, deps, located) {
458
459
  const { resolvedKey, directory, vendDenied, listDenied, verdict, steps } = located;
459
460
  const resolvedPath = `companies/${slug}/${resolvedKey}`;
460
461
  let noAccess = vendDenied || listDenied;
462
+ // Name the empty fencing row only when the tree denies access. A role
463
+ // bypass or creator grant still reads the same as the acl/resolve fast
464
+ // path, which never fetches the tree.
465
+ const lockRow = verdict.kind === "tree" && aclDenied(verdict.tree) ? aclLockedBy(verdict.tree) : null;
466
+ const lockedBy = lockRow
467
+ ? { ...lockRow, path: `companies/${slug}/${toBucketRelative(lockRow.path, slug)}` }
468
+ : undefined;
469
+ if (lockedBy) {
470
+ steps.push(lockedBy.readUnknown
471
+ ? `acl: locked by ${lockedBy.path} unless that row is open (it has no grants; broader grants such as * apply only if it is open)`
472
+ : `acl: locked by ${lockedBy.path} (that row has no grants and is not open; broader grants such as * do not apply)`);
473
+ }
461
474
  if (verdict.kind === "resolved") {
462
475
  steps.push(`acl: ${verdict.permission}`);
463
476
  }
464
- else if (verdict.tree == null || treeIsEmpty(verdict.tree)) {
477
+ else if (verdict.tree == null ||
478
+ (treeIsEmpty(verdict.tree) && !verdict.tree.coveringAclPath)) {
465
479
  steps.push("acl: no record (membership-distributed)");
466
480
  }
467
481
  else if (aclDenied(verdict.tree)) {
@@ -687,6 +701,7 @@ async function completeTarget(input, deps, located) {
687
701
  ...(requestSentAt ? { requestSentAt } : {}),
688
702
  ...(alreadyAskedAt ? { alreadyAskedAt } : {}),
689
703
  ...(requestNote ? { requestNote } : {}),
704
+ ...(lockedBy ? { lockedBy } : {}),
690
705
  }, finishCtx);
691
706
  return result;
692
707
  }
@@ -834,6 +849,8 @@ function jsonPayload(result) {
834
849
  payload.requestSentAt = result.requestSentAt;
835
850
  if (result.alreadyAskedAt)
836
851
  payload.alreadyAskedAt = result.alreadyAskedAt;
852
+ if (result.lockedBy)
853
+ payload.lockedBy = result.lockedBy;
837
854
  return payload;
838
855
  }
839
856
  function emit(input, result, log) {
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Empty-row lock detection for ACL tree responses (feedback dd287b55).
3
+ *
4
+ * The server resolves a path against its most-specific matching FILE_ACL row
5
+ * (`coveringAclPath` on the ACL tree response). Enforcement (files-presign,
6
+ * files-list, content) runs resolveEffectivePermission in hq-pro-core
7
+ * src/tenant-contracts/acl-permission.ts with `unionGrants: true`: grants on
8
+ * every matching row are unioned, except that an EMPTY, NON-OPEN winning row
9
+ * fences the path and grants nothing (only the row's creator and the
10
+ * owner/admin role bypass keep access). An OPEN empty winning row is not a
11
+ * fence: it floors every member at read and broader grants still apply.
12
+ */
13
+ export interface AclLockTree {
14
+ prefix: string;
15
+ direct: unknown[];
16
+ inherited: Array<{
17
+ sourcePrefix: string;
18
+ }>;
19
+ directRow: {
20
+ open: boolean;
21
+ } | null;
22
+ coveringAclPath?: string | null;
23
+ }
24
+ export interface AclLock {
25
+ /** Path of the empty most-specific row. */
26
+ path: string;
27
+ /**
28
+ * True when the row's `open` flag is unknown. The tree reports `open` only
29
+ * for the exact prefix row (directRow), not for ancestor rows. Such a row is
30
+ * a lock if it is not open; if it is open, broader grants still apply.
31
+ */
32
+ readUnknown: boolean;
33
+ }
34
+ /**
35
+ * The empty, non-open row that fences `tree.prefix`, or null when the
36
+ * most-specific row grants something, is open, or no row covers the path.
37
+ */
38
+ export declare function aclLockedBy(tree: AclLockTree | null | undefined): AclLock | null;
39
+ /** One-line description of the lock for human output. */
40
+ export declare function describeAclLock(lock: AclLock, display: string): string;
41
+ //# sourceMappingURL=acl-lock.d.ts.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The empty, non-open row that fences `tree.prefix`, or null when the
3
+ * most-specific row grants something, is open, or no row covers the path.
4
+ */
5
+ export function aclLockedBy(tree) {
6
+ const covering = tree?.coveringAclPath;
7
+ if (!tree || typeof covering !== "string" || covering === "")
8
+ return null;
9
+ if (covering === tree.prefix) {
10
+ if (!tree.directRow || tree.directRow.open === true)
11
+ return null;
12
+ if (!Array.isArray(tree.direct) || tree.direct.length > 0)
13
+ return null;
14
+ return { path: covering, readUnknown: false };
15
+ }
16
+ const inherited = Array.isArray(tree.inherited) ? tree.inherited : [];
17
+ if (inherited.some((entry) => entry.sourcePrefix === covering))
18
+ return null;
19
+ return { path: covering, readUnknown: true };
20
+ }
21
+ /** One-line description of the lock for human output. */
22
+ export function describeAclLock(lock, display) {
23
+ if (lock.readUnknown) {
24
+ return `Locked by ${display} unless that row is open: it has no grants, so grants on broader prefixes apply only if it is open.`;
25
+ }
26
+ return `Locked by ${display}: that ACL row has no grants and is not open, so grants on broader prefixes do not apply. Only the row's creator and the owner/admin role bypass have access.`;
27
+ }
28
+ //# sourceMappingURL=acl-lock.js.map
@@ -60,6 +60,8 @@ export declare function mapEnrollFailure(status: number, body: {
60
60
  attemptsRemaining?: unknown;
61
61
  upgradeUrl?: unknown;
62
62
  pricingPlan?: unknown;
63
+ usagePause?: unknown;
64
+ billingState?: unknown;
63
65
  }, headers?: {
64
66
  retryAfter?: string | null;
65
67
  }): EnrollError;
@@ -21,7 +21,7 @@ import * as path from "node:path";
21
21
  import { CLI_VERSION } from "../cli-version.js";
22
22
  import { DEFAULT_VAULT_API_URL, personTokenCacheFile, } from "../utils/cognito-session.js";
23
23
  import { HQ_CLIENT_NAME } from "../utils/vault-api.js";
24
- import { isUsageOnly, pricingPlanFromBody, usageOnlyCardRequiredLines, } from "../lib/billing/usage-only.js";
24
+ import { isUsageOnly, pricingPlanFromBody, usageOnlyCardRequiredLines, usagePauseFromBody, } from "../lib/billing/usage-only.js";
25
25
  import { queueCliLimitNoticeEngagement, queueCliLimitNoticeExposure, } from "../utils/cli-telemetry.js";
26
26
  import { networkTransportErrorCode } from "../utils/network-transport-error.js";
27
27
  import { canOfferTeamUpgrade, formatUpgradeRefusal, isPlanGateError, offerUpgradeLink, planGateErrorFromPayload, } from "../utils/plan-gate-error.js";
@@ -139,6 +139,11 @@ export function mapEnrollFailure(status, body, headers) {
139
139
  return new EnrollError(`This agent was revoked; its enrollment code no longer works. An admin must ` +
140
140
  `create a new external agent or rotate this one.${suffix}`, status, code);
141
141
  }
142
+ // US-024: a usage pause is the server's own answer: its message and link.
143
+ const usagePause = status === 402 ? usagePauseFromBody(body) : null;
144
+ if (usagePause) {
145
+ return new EnrollError(usagePause.message, status, code, usagePause.actionUrl);
146
+ }
142
147
  if ((status === 402 || code === "billing_required") &&
143
148
  isUsageOnly(pricingPlanFromBody(body))) {
144
149
  // Usage pricing (US-012): no paid plan to upgrade to. A company admin adds
@@ -146,8 +151,8 @@ export function mapEnrollFailure(status, body, headers) {
146
151
  return new EnrollError(usageOnlyCardRequiredLines("An external agent").join(" ") + suffix, status, code);
147
152
  }
148
153
  if (status === 402 || code === "billing_required") {
149
- return new EnrollError(`External agents are available on paid HQ plans only. A company admin must ` +
150
- `upgrade the plan (\`hq billing\` or the console billing page) before enrolling.${suffix}`, status, code, typeof body.upgradeUrl === "string" ? body.upgradeUrl : undefined);
154
+ return new EnrollError(`External agents need billing set up for this company. A company admin can ` +
155
+ `add a card with \`hq billing checkout\` or on the console Billing page, then enroll again.${suffix}`, status, code, typeof body.upgradeUrl === "string" ? body.upgradeUrl : undefined);
151
156
  }
152
157
  if (status === 429 || code === "rate_limited") {
153
158
  const retryAfter = headers?.retryAfter?.trim();
@@ -425,7 +430,7 @@ export function registerAgentEnrollCommand(agent) {
425
430
  }
426
431
  catch (openErr) {
427
432
  const message = openErr instanceof Error ? openErr.message : String(openErr);
428
- console.error(chalk.dim(`Could not open the upgrade link automatically: ${message}`));
433
+ console.error(chalk.dim(`Could not open the billing link automatically: ${message}`));
429
434
  }
430
435
  }
431
436
  }