@indigoai-us/hq-cli 5.300.0 → 5.301.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/assets/scaffold/core/scripts/token-usage-report.sh +9 -0
- package/dist/command-catalog.generated.d.ts +28 -2
- package/dist/command-catalog.generated.js +34 -2
- package/dist/commands/__fixtures__/access-vault.d.ts +2 -0
- package/dist/commands/__fixtures__/access-vault.js +1 -1
- package/dist/commands/access.d.ts +2 -1
- package/dist/commands/access.js +58 -49
- package/dist/commands/cloud-provision.js +51 -2
- package/dist/commands/core-company-index-scope-flag.d.ts +3 -0
- package/dist/commands/core-company-index-scope-flag.js +6 -0
- package/dist/commands/core-job-native-utils.d.ts +6 -0
- package/dist/commands/core-job-native-utils.js +80 -0
- package/dist/commands/core-job-native.d.ts +2 -4
- package/dist/commands/core-job-native.js +6 -10
- package/dist/commands/core-job-notify-native.d.ts +36 -0
- package/dist/commands/core-job-notify-native.js +359 -0
- package/dist/commands/core-job-remediate-native.d.ts +26 -0
- package/dist/commands/core-job-remediate-native.js +374 -0
- package/dist/commands/core.js +26 -8
- package/dist/commands/files.js +31 -14
- package/dist/commands/integrations-api.d.ts +6 -6
- package/dist/commands/integrations-api.js +4 -13
- package/dist/commands/integrations-manage.js +1 -4
- package/dist/commands/lanes-message.js +10 -2
- package/dist/commands/lanes-shared.d.ts +2 -0
- package/dist/commands/lanes.d.ts +2 -0
- package/dist/commands/lanes.js +1 -0
- package/dist/commands/meetings.js +35 -16
- package/dist/commands/safe-extract.d.ts +4 -4
- package/dist/commands/safe-extract.js +6 -7
- package/dist/lib/core-utils/token-usage-report.js +17 -3
- package/dist/lib/index-render/companies.d.ts +4 -1
- package/dist/lib/index-render/companies.js +66 -3
- package/dist/lib/index-render/index.d.ts +1 -0
- package/dist/lib/index-render/index.js +2 -1
- package/dist/lib/index-render/shared.d.ts +1 -1
- package/dist/lib/index-render/shared.js +4 -2
- package/dist/lib/mesh/live/daemon/mqtt-events.d.ts +7 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.js +2 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.js +10 -0
- package/dist/lib/mesh/live/daemon/presence.d.ts +2 -2
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +2 -2
- package/dist/utils/self-update.js +51 -4
- package/dist/utils/staged-npm-install.d.ts +16 -0
- package/dist/utils/staged-npm-install.js +264 -0
- package/dist/utils/update-command-supervisor.cjs +53 -1
- package/dist/utils/version-gate.d.ts +8 -0
- package/dist/utils/version-gate.js +82 -15
- package/package.json +2 -2
- package/dist/lib/install-path-compatibility-flag.d.ts +0 -9
- package/dist/lib/install-path-compatibility-flag.js +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.301.0] — 2026-10-01
|
|
6
|
+
- `hq meetings transcript` and `hq meetings notes` stop a stalled document download after two minutes and say so, instead of waiting forever.
|
|
7
|
+
|
|
8
|
+
- Keep `hq access` from reporting a denied vault path as missing or exposing matching paths.
|
|
9
|
+
- Fix self-updates for npm installs whose global prefix contains spaces.
|
|
10
|
+
- `hq files acl companies/<slug>` and `hq files unshare companies/<slug> --with ...` now handle ACL rows at that exact company path instead of treating it as the vault root.
|
|
11
|
+
- Add native hq core job notify and remediate commands behind core-native-jobs, retaining bundled shell companions.
|
|
12
|
+
- Allow owners to disconnect custom Factory connections without installation records, and send the company UID required by the revoked-connection purge endpoint.
|
|
13
|
+
- Fail company provisioning when its initial sync reports per-file upload failures, while retaining the successful upload count in failure details.
|
|
14
|
+
- Treat plan-limited files as initial-sync failures and keep provisioning progress snapshots updated.
|
|
15
|
+
- Preserve story attribution after a message is durably queued by lane admission or an account move.
|
|
16
|
+
- The token usage report accepts `--project-dir <path>` to read one Claude activity folder instead of the default folders.
|
|
17
|
+
|
|
5
18
|
## [5.300.0] — 2026-09-30
|
|
19
|
+
- `hq files unshare --with agt_...` removes a grant made to an agent. Before, agent uids were rejected as invalid principals.
|
|
6
20
|
- With the default-off hq-flags gate `files.share-empty-prefix-warning`, `hq files share` warns when a granted prefix has no files. A failed or timed-out list check does not block the grant.
|
|
21
|
+
- Recognize pnpm v11+ global installs and handle Windows drive-path tar archives without waiting for the asynchronous feature-flag snapshot.
|
|
22
|
+
- Self-update failure reports now include the npm error code and a sanitized reason.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `hq core rebuild-index companies --company <slug>` scopes the generated write to that company (behind the default-off `cli.rebuild-index-company-scope` flag), and `--help` exits without reading or writing company indexes. The unscoped run also ignores orchestrator state tied to another company's PRD.
|
|
7
27
|
|
|
8
28
|
- Lane messages and resumes that switch project or story attribution now tell the provider which story is active. Reused completion artifacts no longer close the new story.
|
|
9
29
|
- Make signed-out `hq sync` commands fail fast with `hq login` guidance when stdin or stdout is not a terminal, while keeping browser login in a terminal.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# scripts/token-usage-report.sh --last 14 # last 14 days
|
|
6
6
|
# scripts/token-usage-report.sh --since 2026-05-01
|
|
7
7
|
# scripts/token-usage-report.sh --json # machine-readable
|
|
8
|
+
# scripts/token-usage-report.sh --project-dir <path> # read one activity folder
|
|
8
9
|
#
|
|
9
10
|
# Effective tokens weighting (matches weekly rate-limit volume):
|
|
10
11
|
# input + 5×output + 1.25×cache_creation + 0.1×cache_read
|
|
@@ -24,6 +25,14 @@ while [ $# -gt 0 ]; do
|
|
|
24
25
|
--last) LAST_DAYS="$2"; shift 2 ;;
|
|
25
26
|
--since) SINCE="$2"; shift 2 ;;
|
|
26
27
|
--json) JSON_MODE=1; shift ;;
|
|
28
|
+
--project-dir)
|
|
29
|
+
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
|
30
|
+
echo "token-usage-report: --project-dir requires a path" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
PROJECT_DIR="$2"
|
|
34
|
+
shift 2
|
|
35
|
+
;;
|
|
27
36
|
--compare-windows) shift ;; # flag, presence triggers compare mode via --before/--after
|
|
28
37
|
--before) COMPARE_BEFORE="$2"; shift 2 ;;
|
|
29
38
|
--after) COMPARE_AFTER="$2"; shift 2 ;;
|
|
@@ -1713,7 +1713,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
1713
1713
|
}];
|
|
1714
1714
|
readonly options: readonly [{
|
|
1715
1715
|
readonly flags: "--with <principal>";
|
|
1716
|
-
readonly description: "Person uid, email address, group id, or '@all' to remove the grant";
|
|
1716
|
+
readonly description: "Person uid, agent uid, email address, group id, or '@all' to remove the grant";
|
|
1717
1717
|
}];
|
|
1718
1718
|
readonly subcommands: readonly [];
|
|
1719
1719
|
}, {
|
|
@@ -5620,13 +5620,26 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
5620
5620
|
}];
|
|
5621
5621
|
}, {
|
|
5622
5622
|
readonly name: "job";
|
|
5623
|
-
readonly description: "Run or
|
|
5623
|
+
readonly description: "Run, inspect, notify, or remediate one scheduled job";
|
|
5624
5624
|
readonly aliases: readonly [];
|
|
5625
5625
|
readonly hidden: false;
|
|
5626
5626
|
readonly usage: "[options] [command]";
|
|
5627
5627
|
readonly arguments: readonly [];
|
|
5628
5628
|
readonly options: readonly [];
|
|
5629
5629
|
readonly subcommands: readonly [{
|
|
5630
|
+
readonly name: "notify";
|
|
5631
|
+
readonly description: "Send a scheduled-job status alert to its owner";
|
|
5632
|
+
readonly aliases: readonly [];
|
|
5633
|
+
readonly hidden: false;
|
|
5634
|
+
readonly usage: "[args...]";
|
|
5635
|
+
readonly arguments: readonly [{
|
|
5636
|
+
readonly name: "args";
|
|
5637
|
+
readonly required: false;
|
|
5638
|
+
readonly variadic: true;
|
|
5639
|
+
}];
|
|
5640
|
+
readonly options: readonly [];
|
|
5641
|
+
readonly subcommands: readonly [];
|
|
5642
|
+
}, {
|
|
5630
5643
|
readonly name: "probe";
|
|
5631
5644
|
readonly description: "Check scheduled-job requirements and report readiness";
|
|
5632
5645
|
readonly aliases: readonly [];
|
|
@@ -5639,6 +5652,19 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
5639
5652
|
}];
|
|
5640
5653
|
readonly options: readonly [];
|
|
5641
5654
|
readonly subcommands: readonly [];
|
|
5655
|
+
}, {
|
|
5656
|
+
readonly name: "remediate";
|
|
5657
|
+
readonly description: "Run bounded remediation for a scheduled-job failure";
|
|
5658
|
+
readonly aliases: readonly [];
|
|
5659
|
+
readonly hidden: false;
|
|
5660
|
+
readonly usage: "[args...]";
|
|
5661
|
+
readonly arguments: readonly [{
|
|
5662
|
+
readonly name: "args";
|
|
5663
|
+
readonly required: false;
|
|
5664
|
+
readonly variadic: true;
|
|
5665
|
+
}];
|
|
5666
|
+
readonly options: readonly [];
|
|
5667
|
+
readonly subcommands: readonly [];
|
|
5642
5668
|
}, {
|
|
5643
5669
|
readonly name: "run";
|
|
5644
5670
|
readonly description: "Run a scheduled job";
|
|
@@ -2185,7 +2185,7 @@ export const COMMAND_CATALOG = [
|
|
|
2185
2185
|
"options": [
|
|
2186
2186
|
{
|
|
2187
2187
|
"flags": "--with <principal>",
|
|
2188
|
-
"description": "Person uid, email address, group id, or '@all' to remove the grant"
|
|
2188
|
+
"description": "Person uid, agent uid, email address, group id, or '@all' to remove the grant"
|
|
2189
2189
|
}
|
|
2190
2190
|
],
|
|
2191
2191
|
"subcommands": []
|
|
@@ -7246,13 +7246,29 @@ export const COMMAND_CATALOG = [
|
|
|
7246
7246
|
},
|
|
7247
7247
|
{
|
|
7248
7248
|
"name": "job",
|
|
7249
|
-
"description": "Run or
|
|
7249
|
+
"description": "Run, inspect, notify, or remediate one scheduled job",
|
|
7250
7250
|
"aliases": [],
|
|
7251
7251
|
"hidden": false,
|
|
7252
7252
|
"usage": "[options] [command]",
|
|
7253
7253
|
"arguments": [],
|
|
7254
7254
|
"options": [],
|
|
7255
7255
|
"subcommands": [
|
|
7256
|
+
{
|
|
7257
|
+
"name": "notify",
|
|
7258
|
+
"description": "Send a scheduled-job status alert to its owner",
|
|
7259
|
+
"aliases": [],
|
|
7260
|
+
"hidden": false,
|
|
7261
|
+
"usage": "[args...]",
|
|
7262
|
+
"arguments": [
|
|
7263
|
+
{
|
|
7264
|
+
"name": "args",
|
|
7265
|
+
"required": false,
|
|
7266
|
+
"variadic": true
|
|
7267
|
+
}
|
|
7268
|
+
],
|
|
7269
|
+
"options": [],
|
|
7270
|
+
"subcommands": []
|
|
7271
|
+
},
|
|
7256
7272
|
{
|
|
7257
7273
|
"name": "probe",
|
|
7258
7274
|
"description": "Check scheduled-job requirements and report readiness",
|
|
@@ -7269,6 +7285,22 @@ export const COMMAND_CATALOG = [
|
|
|
7269
7285
|
"options": [],
|
|
7270
7286
|
"subcommands": []
|
|
7271
7287
|
},
|
|
7288
|
+
{
|
|
7289
|
+
"name": "remediate",
|
|
7290
|
+
"description": "Run bounded remediation for a scheduled-job failure",
|
|
7291
|
+
"aliases": [],
|
|
7292
|
+
"hidden": false,
|
|
7293
|
+
"usage": "[args...]",
|
|
7294
|
+
"arguments": [
|
|
7295
|
+
{
|
|
7296
|
+
"name": "args",
|
|
7297
|
+
"required": false,
|
|
7298
|
+
"variadic": true
|
|
7299
|
+
}
|
|
7300
|
+
],
|
|
7301
|
+
"options": [],
|
|
7302
|
+
"subcommands": []
|
|
7303
|
+
},
|
|
7272
7304
|
{
|
|
7273
7305
|
"name": "run",
|
|
7274
7306
|
"description": "Run a scheduled job",
|
|
@@ -82,6 +82,8 @@ export interface MakeAccessFixtureOptions {
|
|
|
82
82
|
* without read on the prefix — the server refuses to enumerate.
|
|
83
83
|
*/
|
|
84
84
|
listDenied?: boolean;
|
|
85
|
+
/** present-denied only: let a test isolate list denial from vend denial. */
|
|
86
|
+
vendDenied?: boolean;
|
|
85
87
|
/** Deny only the ROOT (prefix "") list — the caller can read the prefix but not enumerate the bucket. */
|
|
86
88
|
rootListDenied?: boolean;
|
|
87
89
|
}
|
|
@@ -93,7 +93,7 @@ export function makeAccessFixture(opts) {
|
|
|
93
93
|
if (opts.state === "missing") {
|
|
94
94
|
throw attachStatus(new Error("not found (404)"), 404);
|
|
95
95
|
}
|
|
96
|
-
if (opts.state === "present-denied") {
|
|
96
|
+
if (opts.state === "present-denied" && opts.vendDenied !== false) {
|
|
97
97
|
throw attachStatus(new Error("caller lacks read on this prefix (403)"), 403);
|
|
98
98
|
}
|
|
99
99
|
return FAKE_VEND;
|
|
@@ -122,7 +122,8 @@ export interface RunAccessResult {
|
|
|
122
122
|
outcome: AccessOutcome;
|
|
123
123
|
path: string;
|
|
124
124
|
company: string;
|
|
125
|
-
|
|
125
|
+
/** Omitted when an authorization denial leaves vault existence unknown. */
|
|
126
|
+
exists?: boolean;
|
|
126
127
|
grantor?: AccessGrantor;
|
|
127
128
|
candidates?: string[];
|
|
128
129
|
steps: string[];
|
package/dist/commands/access.js
CHANGED
|
@@ -52,7 +52,7 @@ export function formatAccessOutcome(result) {
|
|
|
52
52
|
lines.push(result.path);
|
|
53
53
|
break;
|
|
54
54
|
case "no-access":
|
|
55
|
-
lines.push(`
|
|
55
|
+
lines.push(`You do not have read access to this path.`);
|
|
56
56
|
lines.push(result.path);
|
|
57
57
|
break;
|
|
58
58
|
case "pending-confirmation":
|
|
@@ -236,72 +236,81 @@ export async function runAccess(input) {
|
|
|
236
236
|
const bucket = entity?.bucketName ?? `hq-vault-cmp-${slug}`;
|
|
237
237
|
const s3 = deps.companyClient({ companyUid: deps.companyUid });
|
|
238
238
|
let listDenied = false;
|
|
239
|
-
const prefixList = await listKeys(s3, bucket, relative);
|
|
240
|
-
const exact = prefixList.keys.find((k) => k === relative);
|
|
241
239
|
let key;
|
|
242
|
-
if (
|
|
243
|
-
// The
|
|
244
|
-
//
|
|
245
|
-
// it for the grantor.
|
|
240
|
+
if (vendDenied) {
|
|
241
|
+
// The caller is denied at the company boundary. Do not use list results to
|
|
242
|
+
// resolve or disclose matching keys, even if a separate listing succeeds.
|
|
246
243
|
listDenied = true;
|
|
247
244
|
key = relative;
|
|
248
|
-
steps.push("list: 403 (no-access)");
|
|
249
|
-
}
|
|
250
|
-
else if (exact) {
|
|
251
|
-
key = exact;
|
|
252
|
-
steps.push("resolve: exact key");
|
|
253
|
-
steps.push("list: 1 hit");
|
|
245
|
+
steps.push("list: skipped after vend 403 (no-access)");
|
|
254
246
|
}
|
|
255
247
|
else {
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const fuzzy = pool.filter((k) => k.toLowerCase().includes(q));
|
|
263
|
-
if (allList.denied && fuzzy.length === 0) {
|
|
248
|
+
const prefixList = await listKeys(s3, bucket, relative);
|
|
249
|
+
const exact = prefixList.keys.find((k) => k === relative);
|
|
250
|
+
if (prefixList.denied) {
|
|
251
|
+
// The server refused to enumerate — the caller has no read on this
|
|
252
|
+
// prefix. Keep the target as the key so the request rung can still name
|
|
253
|
+
// it for the grantor.
|
|
264
254
|
listDenied = true;
|
|
265
255
|
key = relative;
|
|
266
256
|
steps.push("list: 403 (no-access)");
|
|
267
257
|
}
|
|
268
|
-
else if (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
path: anchored(relative) || input.target,
|
|
272
|
-
company: slug,
|
|
273
|
-
exists: false,
|
|
274
|
-
steps,
|
|
275
|
-
}, finishCtx);
|
|
276
|
-
emit(input, result, log);
|
|
277
|
-
return result;
|
|
278
|
-
}
|
|
279
|
-
else if (fuzzy.length === 1) {
|
|
280
|
-
key = fuzzy[0];
|
|
281
|
-
steps.push("resolve: fuzzy (1 hit)");
|
|
258
|
+
else if (exact) {
|
|
259
|
+
key = exact;
|
|
260
|
+
steps.push("resolve: exact key");
|
|
282
261
|
steps.push("list: 1 hit");
|
|
283
262
|
}
|
|
284
263
|
else {
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
264
|
+
const allList = await listKeys(s3, bucket, "");
|
|
265
|
+
const q = relative.toLowerCase();
|
|
266
|
+
// A root-list 403 only says the caller cannot enumerate the WHOLE bucket.
|
|
267
|
+
// The prefix list above succeeded, so fuzzy-match within it first; the
|
|
268
|
+
// root denial is the verdict only when the readable prefix has no match.
|
|
269
|
+
const pool = allList.denied ? prefixList.keys : allList.keys;
|
|
270
|
+
const fuzzy = pool.filter((k) => k.toLowerCase().includes(q));
|
|
271
|
+
if (allList.denied && fuzzy.length === 0) {
|
|
272
|
+
listDenied = true;
|
|
273
|
+
key = relative;
|
|
274
|
+
steps.push("list: 403 (no-access)");
|
|
290
275
|
}
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
276
|
+
else if (fuzzy.length === 0) {
|
|
277
|
+
steps.push("list: 0 hits");
|
|
278
|
+
const result = finish("never-existed", {
|
|
279
|
+
path: anchored(relative) || input.target,
|
|
294
280
|
company: slug,
|
|
295
281
|
exists: false,
|
|
296
|
-
candidates,
|
|
297
282
|
steps,
|
|
298
283
|
}, finishCtx);
|
|
299
284
|
emit(input, result, log);
|
|
300
285
|
return result;
|
|
301
286
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
287
|
+
else if (fuzzy.length === 1) {
|
|
288
|
+
key = fuzzy[0];
|
|
289
|
+
steps.push("resolve: fuzzy (1 hit)");
|
|
290
|
+
steps.push("list: 1 hit");
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
const candidates = fuzzy.map(anchored);
|
|
294
|
+
steps.push(`list: ${fuzzy.length} hits`);
|
|
295
|
+
let picked = null;
|
|
296
|
+
if (deps.pick) {
|
|
297
|
+
picked = await deps.pick(candidates);
|
|
298
|
+
}
|
|
299
|
+
if (!picked) {
|
|
300
|
+
const result = finish("ambiguous", {
|
|
301
|
+
path: input.target,
|
|
302
|
+
company: slug,
|
|
303
|
+
exists: false,
|
|
304
|
+
candidates,
|
|
305
|
+
steps,
|
|
306
|
+
}, finishCtx);
|
|
307
|
+
emit(input, result, log);
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
const stripped = normalizeTarget(picked, slug);
|
|
311
|
+
key = stripped;
|
|
312
|
+
steps.push("resolve: pick");
|
|
313
|
+
}
|
|
305
314
|
}
|
|
306
315
|
}
|
|
307
316
|
const resolvedKey = key;
|
|
@@ -524,7 +533,7 @@ export async function runAccess(input) {
|
|
|
524
533
|
const result = finish(outcome, {
|
|
525
534
|
path: resolvedPath,
|
|
526
535
|
company: slug,
|
|
527
|
-
exists: true,
|
|
536
|
+
...(vendDenied || listDenied ? {} : { exists: true }),
|
|
528
537
|
steps,
|
|
529
538
|
...(fixed ? { fixed, localPath } : {}),
|
|
530
539
|
...(grantor ? { grantor } : {}),
|
|
@@ -541,7 +550,7 @@ function emit(input, result, log) {
|
|
|
541
550
|
outcome: result.outcome,
|
|
542
551
|
path: result.path,
|
|
543
552
|
company: result.company,
|
|
544
|
-
exists: result.exists,
|
|
553
|
+
...(result.exists !== undefined ? { exists: result.exists } : {}),
|
|
545
554
|
steps: result.steps,
|
|
546
555
|
};
|
|
547
556
|
if (result.grantor)
|
|
@@ -32,7 +32,7 @@ import * as fs from "node:fs";
|
|
|
32
32
|
import * as path from "node:path";
|
|
33
33
|
import * as yaml from "js-yaml";
|
|
34
34
|
import { parseUserYaml } from "../utils/user-yaml-error.js";
|
|
35
|
-
import { share } from "@indigoai-us/hq-cloud";
|
|
35
|
+
import { createSyncProgressRecorder, share, } from "@indigoai-us/hq-cloud";
|
|
36
36
|
import { DEFAULT_HQ_ROOT, DEFAULT_VAULT_API_URL, ensureCognitoToken, buildVaultConfig, } from "../utils/cognito-session.js";
|
|
37
37
|
/** Custom error class so the CLI runner can map to exit codes. */
|
|
38
38
|
export class ProvisionError extends Error {
|
|
@@ -421,7 +421,32 @@ async function safeBody(res) {
|
|
|
421
421
|
}
|
|
422
422
|
}
|
|
423
423
|
// ── Default initial-sync runner (wraps share()) ──────────────────────────────
|
|
424
|
+
const INITIAL_SYNC_ERROR_FIELD_LIMIT = 240;
|
|
425
|
+
function truncateInitialSyncErrorField(value) {
|
|
426
|
+
return value.length <= INITIAL_SYNC_ERROR_FIELD_LIMIT
|
|
427
|
+
? value
|
|
428
|
+
: `${value.slice(0, INITIAL_SYNC_ERROR_FIELD_LIMIT - 1)}…`;
|
|
429
|
+
}
|
|
430
|
+
class InitialSyncUploadFailureError extends Error {
|
|
431
|
+
filesUploaded;
|
|
432
|
+
constructor(filesUploaded, failureCount, firstFailure) {
|
|
433
|
+
const detail = `${truncateInitialSyncErrorField(firstFailure.path)}: ` +
|
|
434
|
+
truncateInitialSyncErrorField(firstFailure.message);
|
|
435
|
+
const failureLabel = failureCount === 1
|
|
436
|
+
? "upload failure"
|
|
437
|
+
: "upload failures";
|
|
438
|
+
super(`${failureCount} ${failureLabel}; first failure: ${detail}`);
|
|
439
|
+
this.filesUploaded = filesUploaded;
|
|
440
|
+
this.name = "InitialSyncUploadFailureError";
|
|
441
|
+
}
|
|
442
|
+
}
|
|
424
443
|
async function defaultRunInitialSync(args) {
|
|
444
|
+
let failureCount = 0;
|
|
445
|
+
let firstFailure;
|
|
446
|
+
const recordProgress = createSyncProgressRecorder({
|
|
447
|
+
company: args.slug,
|
|
448
|
+
phase: "push",
|
|
449
|
+
});
|
|
425
450
|
const result = await share({
|
|
426
451
|
paths: [companyDirPath(args.hqRoot, args.slug)],
|
|
427
452
|
company: args.slug,
|
|
@@ -429,7 +454,25 @@ async function defaultRunInitialSync(args) {
|
|
|
429
454
|
onConflict: "keep",
|
|
430
455
|
vaultConfig: buildVaultConfig(args.accessToken),
|
|
431
456
|
hqRoot: args.hqRoot,
|
|
457
|
+
progressRecorder: (event) => {
|
|
458
|
+
recordProgress(event);
|
|
459
|
+
if (event.type === "plan-limit") {
|
|
460
|
+
failureCount += Math.max(1, event.count ?? 0, event.paths?.length ?? 0);
|
|
461
|
+
firstFailure ??= {
|
|
462
|
+
path: event.paths?.[0] ?? "plan limit",
|
|
463
|
+
message: event.message ?? "plan limit refused upload",
|
|
464
|
+
};
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (event.type !== "error" || event.diagnostic === true)
|
|
468
|
+
return;
|
|
469
|
+
failureCount++;
|
|
470
|
+
firstFailure ??= { path: event.path, message: event.message };
|
|
471
|
+
},
|
|
432
472
|
});
|
|
473
|
+
if (failureCount > 0 && firstFailure) {
|
|
474
|
+
throw new InitialSyncUploadFailureError(result.filesUploaded, failureCount, firstFailure);
|
|
475
|
+
}
|
|
433
476
|
return {
|
|
434
477
|
filesUploaded: result.filesUploaded,
|
|
435
478
|
bytesUploaded: result.bytesUploaded,
|
|
@@ -654,7 +697,13 @@ export async function provisionCompany(options) {
|
|
|
654
697
|
created_entity: createdEntity,
|
|
655
698
|
manifest_patched: true,
|
|
656
699
|
config_written: true,
|
|
657
|
-
initial_sync: {
|
|
700
|
+
initial_sync: {
|
|
701
|
+
ok: false,
|
|
702
|
+
error: msg,
|
|
703
|
+
...(err instanceof InitialSyncUploadFailureError
|
|
704
|
+
? { files_uploaded: err.filesUploaded }
|
|
705
|
+
: {}),
|
|
706
|
+
},
|
|
658
707
|
});
|
|
659
708
|
}
|
|
660
709
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { resolveProcessFlagGate } from "../lib/flag-registry.js";
|
|
2
|
+
export const CORE_COMPANY_INDEX_SCOPE_FLAG = "cli.rebuild-index-company-scope";
|
|
3
|
+
export function coreCompanyIndexScopeEnabled() {
|
|
4
|
+
return resolveProcessFlagGate(CORE_COMPANY_INDEX_SCOPE_FLAG, { fallback: false }, () => false);
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=core-company-index-scope-flag.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
/** Preserve raw argv (including the option terminator) through Commander. */
|
|
3
|
+
export declare function rawJobArgumentsForCommand(command: Command, fallback: string[], raw?: string[]): string[];
|
|
4
|
+
/** Shared gated registration path for each leaf in the singular job group. */
|
|
5
|
+
export declare function registerCoreJobLeaf(group: Command, core: Command, name: string, asset: string, summary: string, run: (args: string[], hqRoot: string) => number, canUseLiveRoot: (args: string[]) => boolean, valueOptions: readonly string[], booleanOptions?: readonly string[]): void;
|
|
6
|
+
//# sourceMappingURL=core-job-native-utils.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
2
|
+
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
3
|
+
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
4
|
+
import { coreNativeJobsEnabled } from "./core-native-jobs-flag.js";
|
|
5
|
+
/** Preserve raw argv (including the option terminator) through Commander. */
|
|
6
|
+
export function rawJobArgumentsForCommand(command, fallback, raw = process.argv) {
|
|
7
|
+
const names = [];
|
|
8
|
+
for (let current = command; current?.parent; current = current.parent) {
|
|
9
|
+
names.unshift(current.name());
|
|
10
|
+
}
|
|
11
|
+
let start = -1;
|
|
12
|
+
let cursor = 0;
|
|
13
|
+
for (const name of names) {
|
|
14
|
+
const index = raw.indexOf(name, cursor);
|
|
15
|
+
if (index < 0)
|
|
16
|
+
return command.args.length ? command.args : fallback;
|
|
17
|
+
cursor = index + 1;
|
|
18
|
+
start = cursor;
|
|
19
|
+
}
|
|
20
|
+
return start >= 0 ? raw.slice(start) : (command.args.length ? command.args : fallback);
|
|
21
|
+
}
|
|
22
|
+
/** Shared gated registration path for each leaf in the singular job group. */
|
|
23
|
+
export function registerCoreJobLeaf(group, core, name, asset, summary, run, canUseLiveRoot, valueOptions, booleanOptions = []) {
|
|
24
|
+
const command = group.command(name)
|
|
25
|
+
.description(summary)
|
|
26
|
+
.helpOption(false)
|
|
27
|
+
.allowUnknownOption()
|
|
28
|
+
.allowExcessArguments(true)
|
|
29
|
+
.argument("[args...]", "Arguments passed to the scaffold helper")
|
|
30
|
+
.action(async (args, _options, current) => {
|
|
31
|
+
const argv = rawJobArgumentsForCommand(current, args);
|
|
32
|
+
const groupHqRoot = core.opts().hqRoot;
|
|
33
|
+
const hqRoot = canUseLiveRoot(argv)
|
|
34
|
+
? resolveLiveRoot({ hqRoot: leafRootOverride(argv, valueOptions, booleanOptions) ?? groupHqRoot })
|
|
35
|
+
: groupHqRoot ?? "";
|
|
36
|
+
if (!await coreNativeJobsEnabled()) {
|
|
37
|
+
const result = runBundledScript({ asset, args: argv, cwd: process.cwd(), hqRoot: hqRoot || undefined });
|
|
38
|
+
if (result.code !== 0)
|
|
39
|
+
process.exit(result.code);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const result = run(argv, hqRoot);
|
|
43
|
+
if (result !== 0)
|
|
44
|
+
process.exit(result);
|
|
45
|
+
});
|
|
46
|
+
describeCoreCommand(command, { execution: "native", asset, root: "live" });
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Find a leaf --hq-root without resolving anything until the leaf parser has
|
|
50
|
+
* had the chance to return its own help or usage error. The option lists are
|
|
51
|
+
* the same grammar used by the shell and native leaves.
|
|
52
|
+
*/
|
|
53
|
+
function leafRootOverride(argv, valueOptions, booleanOptions) {
|
|
54
|
+
let root;
|
|
55
|
+
for (let index = 0; index < argv.length;) {
|
|
56
|
+
const flag = argv[index];
|
|
57
|
+
if (flag === "--")
|
|
58
|
+
return root;
|
|
59
|
+
if (flag === "--hq-root") {
|
|
60
|
+
const value = argv[index + 1] ?? "";
|
|
61
|
+
if (!value)
|
|
62
|
+
return undefined;
|
|
63
|
+
root = value;
|
|
64
|
+
index += 2;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (valueOptions.includes(flag)) {
|
|
68
|
+
index += 2;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (booleanOptions.includes(flag)) {
|
|
72
|
+
index += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// The preflight parser has already rejected invalid options and operands.
|
|
76
|
+
return root;
|
|
77
|
+
}
|
|
78
|
+
return root;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=core-job-native-utils.js.map
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
/** Native
|
|
1
|
+
/** Native hq core job command group. */
|
|
2
2
|
import type { Command } from "commander";
|
|
3
|
-
|
|
4
|
-
export declare function registerCoreJobCommand(core: Command): void;
|
|
5
|
-
/** Keep eager `core` registration complete while the jobs fast route loads only its own module. */
|
|
3
|
+
/** Register all job leaves on one singular group plus the existing plural group. */
|
|
6
4
|
export declare function registerCoreJobsNativeCommand(core: Command): void;
|
|
7
5
|
//# sourceMappingURL=core-job-native.d.ts.map
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
+
import { registerCoreJobNotifyNativeCommand } from "./core-job-notify-native.js";
|
|
1
2
|
import { registerCoreJobProbeCommand } from "./core-job-probe-native.js";
|
|
3
|
+
import { registerCoreJobRemediateNativeCommand } from "./core-job-remediate-native.js";
|
|
2
4
|
import { registerCoreJobRunCommand } from "./core-job-run-native.js";
|
|
3
5
|
import { registerCoreJobsNativeCommand as registerPluralJobsCommand } from "./core-jobs-native.js";
|
|
4
|
-
const JOB_GROUP_DESCRIPTION = "Run or
|
|
5
|
-
|
|
6
|
-
const job = core.command("job").description(JOB_GROUP_DESCRIPTION);
|
|
7
|
-
registerCoreJobProbeCommand(job);
|
|
8
|
-
}
|
|
9
|
-
export function registerCoreJobCommand(core) {
|
|
10
|
-
const job = core.command("job").description(JOB_GROUP_DESCRIPTION);
|
|
11
|
-
registerCoreJobRunCommand(job);
|
|
12
|
-
}
|
|
13
|
-
/** Keep eager `core` registration complete while the jobs fast route loads only its own module. */
|
|
6
|
+
const JOB_GROUP_DESCRIPTION = "Run, inspect, notify, or remediate one scheduled job";
|
|
7
|
+
/** Register all job leaves on one singular group plus the existing plural group. */
|
|
14
8
|
export function registerCoreJobsNativeCommand(core) {
|
|
15
9
|
const job = core.command("job").description(JOB_GROUP_DESCRIPTION);
|
|
10
|
+
registerCoreJobNotifyNativeCommand(job, core);
|
|
16
11
|
registerCoreJobProbeCommand(job);
|
|
12
|
+
registerCoreJobRemediateNativeCommand(job, core);
|
|
17
13
|
registerCoreJobRunCommand(job);
|
|
18
14
|
registerPluralJobsCommand(core);
|
|
19
15
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Native TypeScript port of hq-job-notify.sh. */
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
type Environment = Record<string, string | undefined>;
|
|
4
|
+
export type JobNotifyProcessResult = {
|
|
5
|
+
code: number;
|
|
6
|
+
stdout: string;
|
|
7
|
+
};
|
|
8
|
+
export type JobNotifyRunOptions = {
|
|
9
|
+
cwd: string;
|
|
10
|
+
env: Environment;
|
|
11
|
+
captureStdout: boolean;
|
|
12
|
+
logPath?: string;
|
|
13
|
+
};
|
|
14
|
+
export type JobNotifyDependencies = {
|
|
15
|
+
env: Environment;
|
|
16
|
+
cwd: string;
|
|
17
|
+
now: () => Date;
|
|
18
|
+
exists: (file: string) => boolean;
|
|
19
|
+
readFile: (file: string) => string;
|
|
20
|
+
mkdir: (directory: string) => void;
|
|
21
|
+
writeFile: (file: string, content: string) => void;
|
|
22
|
+
appendFile: (file: string, content: string) => void;
|
|
23
|
+
chmod: (file: string, mode: number) => void;
|
|
24
|
+
commandExists: (command: string) => boolean;
|
|
25
|
+
run: (command: string, args: string[], options: JobNotifyRunOptions) => JobNotifyProcessResult;
|
|
26
|
+
stdout: (text: string) => void;
|
|
27
|
+
stderr: (text: string) => void;
|
|
28
|
+
};
|
|
29
|
+
export declare function defaultJobNotifyDependencies(env?: Environment, cwd?: string): JobNotifyDependencies;
|
|
30
|
+
export declare function runJobNotify(argv: string[], options?: {
|
|
31
|
+
hqRoot?: string;
|
|
32
|
+
dependencies?: Partial<JobNotifyDependencies>;
|
|
33
|
+
}): number;
|
|
34
|
+
export declare function registerCoreJobNotifyNativeCommand(group: Command, core: Command): void;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=core-job-notify-native.d.ts.map
|