@indigoai-us/hq-cli 5.108.19 → 5.108.21
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 +36 -0
- package/dist/commands/files-browse.d.ts +22 -0
- package/dist/commands/files-browse.js +62 -8
- package/dist/lib/mesh/live/session-event.schema.json +144 -0
- package/dist/lib/work-context/contract.schema.json +107 -0
- package/dist/schemas/hq-package.schema.json +155 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.108.21] — 2026-09-07
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- A large `hq files cat`/`hq files get`/`hq files browse`/`hq files search`
|
|
10
|
+
download no longer dies part-way through with an unexplained crash (Sentry
|
|
11
|
+
HQ-CLI-5 — 7596554539). Every company-mode vault read goes through a presigned
|
|
12
|
+
GET, and the CLI handed the caller the download's response body *before anyone
|
|
13
|
+
started reading it*. Node's bundled `undici` builds that body stream with a
|
|
14
|
+
zero high-water mark, so the first chunk pauses its HTTP parser until a
|
|
15
|
+
consumer pulls — and the download loop ran a synchronous `mkdirSync` before it
|
|
16
|
+
began pulling. When S3 closed the connection inside that gap, undici's
|
|
17
|
+
socket-end handler tripped an internal `assert(!this.paused)` and threw an
|
|
18
|
+
`AssertionError` from a background tick that no `try/catch` around the download
|
|
19
|
+
could see, so `@sentry/node` filed it as a fatal and the process exited
|
|
20
|
+
mid-download. The presigned GET is now drained into memory before its body is
|
|
21
|
+
handed on whenever the response declares a length at or below 32 MiB (which
|
|
22
|
+
covers every object the CLI reads today), removing the paused-parser window
|
|
23
|
+
entirely; larger or unmeasured responses keep streaming, and the destination
|
|
24
|
+
directory is now created *before* the download is issued so no synchronous
|
|
25
|
+
filesystem call sits inside the read window on that path either. The observable
|
|
26
|
+
output of every `hq files` subcommand is unchanged.
|
|
27
|
+
|
|
28
|
+
## [5.108.20] — 2026-09-07
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- `hq mesh session <kind> --enqueue` and the Work Mesh Live daemon's transcript
|
|
33
|
+
watcher no longer fail with `ENOENT … session-event.schema.json` on installed
|
|
34
|
+
copies of the CLI. The session-event validator reads its JSON schema from disk
|
|
35
|
+
next to the compiled module, but the build only ran `tsc`, which does not emit
|
|
36
|
+
JSON files that are not imported, so no published package since Work Mesh
|
|
37
|
+
Live shipped contained the schema (unit tests run against `src/` and never
|
|
38
|
+
noticed). The build now copies every `src/**/*.schema.json` into `dist/`, and a
|
|
39
|
+
CI guard test keeps it that way.
|
|
40
|
+
|
|
5
41
|
## [5.108.19] — 2026-09-07
|
|
6
42
|
|
|
7
43
|
## [5.108.18] — 2026-09-07
|
|
@@ -403,6 +403,28 @@ export interface RunGetResult {
|
|
|
403
403
|
* HQ root itself, which is too broad to do implicitly.
|
|
404
404
|
*/
|
|
405
405
|
export declare function runGet(input: RunGetInput): Promise<RunGetResult>;
|
|
406
|
+
/**
|
|
407
|
+
* Upper bound (bytes) on a presigned download we drain into memory before
|
|
408
|
+
* handing the caller a Body (HQ-CLI-5).
|
|
409
|
+
*
|
|
410
|
+
* fetch()'s response body is an undici ReadableStream with a zero high-water
|
|
411
|
+
* mark: the first chunk drives desiredSize to 0 and undici PAUSES its llhttp
|
|
412
|
+
* parser until a consumer pulls. `getObject` used to hand that still-paused
|
|
413
|
+
* stream straight to the orchestrators, which each run a synchronous fs call
|
|
414
|
+
* before they start pulling. If S3 sends its connection FIN inside that gap,
|
|
415
|
+
* undici's socket-end handler trips `assert(!this.paused)` in Parser.finish and
|
|
416
|
+
* throws an AssertionError from a process tick — outside any try/catch — that
|
|
417
|
+
* @sentry/node files as an uncaught fatal and that kills the CLI mid-download.
|
|
418
|
+
*
|
|
419
|
+
* Draining the body up front removes the pause entirely (the same
|
|
420
|
+
* buffer-then-rewrap posture peekPlanLimitStatus uses in vault-api.ts). We cap
|
|
421
|
+
* it so a very large object still streams and never buffers into a small agent
|
|
422
|
+
* box's memory; a response above the ceiling (or with no declared length) keeps
|
|
423
|
+
* the streaming path, whose residual window is narrowed by the mkdirSync hoist
|
|
424
|
+
* in runGet/runCat. 32 MiB comfortably covers every object hq-cli reads today
|
|
425
|
+
* (vault JSON + images) while staying well under a 4 GB host's headroom.
|
|
426
|
+
*/
|
|
427
|
+
export declare const PRESIGN_BUFFER_MAX_BYTES: number;
|
|
406
428
|
/**
|
|
407
429
|
* Build a COMPANY-mode browse client backed by the list + presign API. The
|
|
408
430
|
* access token + companyUid are captured here; the orchestrator just calls
|
|
@@ -318,6 +318,15 @@ export async function runCat(input) {
|
|
|
318
318
|
// COMPANY mode (HQ-59): GetObject → presign GET. No STS vend, no direct S3.
|
|
319
319
|
s3 = requireCompanyClient(input.companyClient)({ companyUid: entity.uid });
|
|
320
320
|
}
|
|
321
|
+
// HQ-CLI-5: when writing to --out, create the parent directory BEFORE issuing
|
|
322
|
+
// the presigned GET, so no synchronous filesystem call sits between receiving
|
|
323
|
+
// the body and the nextTick resume that starts pulling it — that gap is what
|
|
324
|
+
// left undici's HTTP parser paused into the socket FIN. The guard above
|
|
325
|
+
// already validated absOut is outside the protected companies/ tree, so its
|
|
326
|
+
// parent is outside too.
|
|
327
|
+
if (absOut !== undefined) {
|
|
328
|
+
fs.mkdirSync(path.dirname(absOut), { recursive: true });
|
|
329
|
+
}
|
|
321
330
|
const resp = (await s3.send(new GetObjectCommand({ Bucket: bucket, Key: s3Key })));
|
|
322
331
|
if (!resp.Body) {
|
|
323
332
|
throw new Error(`GetObject for '${key}' returned no body.`);
|
|
@@ -331,10 +340,6 @@ export async function runCat(input) {
|
|
|
331
340
|
bytesWritten += Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
|
|
332
341
|
});
|
|
333
342
|
if (absOut !== undefined) {
|
|
334
|
-
// Ensure the parent directory exists — but ONLY if it's also outside
|
|
335
|
-
// the protected tree (the guard already validated absOut itself; the
|
|
336
|
-
// parent of an outside-tree path is by definition outside too).
|
|
337
|
-
fs.mkdirSync(path.dirname(absOut), { recursive: true });
|
|
338
343
|
await pipeline(body, fs.createWriteStream(absOut));
|
|
339
344
|
return {
|
|
340
345
|
bytesWritten,
|
|
@@ -536,12 +541,17 @@ export async function runGet(input) {
|
|
|
536
541
|
else {
|
|
537
542
|
destAbs = path.join(hqRoot, "companies", slug, key);
|
|
538
543
|
}
|
|
544
|
+
// HQ-CLI-5: create the destination directory BEFORE issuing the presigned
|
|
545
|
+
// GET, so no synchronous filesystem call sits between receiving the body
|
|
546
|
+
// and starting to pull it — that gap is what left undici's HTTP parser
|
|
547
|
+
// paused into the socket FIN. Buffered downloads no longer keep a live
|
|
548
|
+
// socket at all; this also closes the window on the streaming branch.
|
|
549
|
+
fs.mkdirSync(path.dirname(destAbs), { recursive: true });
|
|
539
550
|
const resp = (await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key })));
|
|
540
551
|
if (!resp.Body) {
|
|
541
552
|
throw new Error(`GetObject for '${key}' returned no body.`);
|
|
542
553
|
}
|
|
543
554
|
const body = resp.Body;
|
|
544
|
-
fs.mkdirSync(path.dirname(destAbs), { recursive: true });
|
|
545
555
|
await pipeline(body, fs.createWriteStream(destAbs));
|
|
546
556
|
bytesWritten += fs.statSync(destAbs).size;
|
|
547
557
|
destinations.push(destAbs);
|
|
@@ -557,6 +567,28 @@ export async function runGet(input) {
|
|
|
557
567
|
}
|
|
558
568
|
// ── CLI registration ────────────────────────────────────────────────────────
|
|
559
569
|
const defaultS3Factory = ({ region, credentials }) => new S3Client({ region, credentials });
|
|
570
|
+
/**
|
|
571
|
+
* Upper bound (bytes) on a presigned download we drain into memory before
|
|
572
|
+
* handing the caller a Body (HQ-CLI-5).
|
|
573
|
+
*
|
|
574
|
+
* fetch()'s response body is an undici ReadableStream with a zero high-water
|
|
575
|
+
* mark: the first chunk drives desiredSize to 0 and undici PAUSES its llhttp
|
|
576
|
+
* parser until a consumer pulls. `getObject` used to hand that still-paused
|
|
577
|
+
* stream straight to the orchestrators, which each run a synchronous fs call
|
|
578
|
+
* before they start pulling. If S3 sends its connection FIN inside that gap,
|
|
579
|
+
* undici's socket-end handler trips `assert(!this.paused)` in Parser.finish and
|
|
580
|
+
* throws an AssertionError from a process tick — outside any try/catch — that
|
|
581
|
+
* @sentry/node files as an uncaught fatal and that kills the CLI mid-download.
|
|
582
|
+
*
|
|
583
|
+
* Draining the body up front removes the pause entirely (the same
|
|
584
|
+
* buffer-then-rewrap posture peekPlanLimitStatus uses in vault-api.ts). We cap
|
|
585
|
+
* it so a very large object still streams and never buffers into a small agent
|
|
586
|
+
* box's memory; a response above the ceiling (or with no declared length) keeps
|
|
587
|
+
* the streaming path, whose residual window is narrowed by the mkdirSync hoist
|
|
588
|
+
* in runGet/runCat. 32 MiB comfortably covers every object hq-cli reads today
|
|
589
|
+
* (vault JSON + images) while staying well under a 4 GB host's headroom.
|
|
590
|
+
*/
|
|
591
|
+
export const PRESIGN_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
|
|
560
592
|
/**
|
|
561
593
|
* Build a COMPANY-mode browse client backed by the list + presign API. The
|
|
562
594
|
* access token + companyUid are captured here; the orchestrator just calls
|
|
@@ -616,9 +648,31 @@ export function createCompanyPresignClient(input) {
|
|
|
616
648
|
if (!dl.ok) {
|
|
617
649
|
throw new Error(`Failed to download '${key}' (HTTP ${dl.status})`);
|
|
618
650
|
}
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
651
|
+
// HQ-CLI-5: when the response declares a length at or below the ceiling,
|
|
652
|
+
// DRAIN it here — read the whole body before send() resolves and replay it
|
|
653
|
+
// from memory. That removes undici's paused-parser window entirely: by the
|
|
654
|
+
// time the orchestrators pull, there is no live socket left to hit
|
|
655
|
+
// `assert(!this.paused)` on its FIN. `Number(null)` is 0, so guard the
|
|
656
|
+
// absent header explicitly (NaN) — an unmeasured body must keep streaming,
|
|
657
|
+
// never look like a zero-length buffer.
|
|
658
|
+
const declaredLengthHeader = dl.headers.get("content-length");
|
|
659
|
+
const declaredLength = declaredLengthHeader === null ? NaN : Number(declaredLengthHeader);
|
|
660
|
+
const canBuffer = Number.isFinite(declaredLength) &&
|
|
661
|
+
declaredLength >= 0 &&
|
|
662
|
+
declaredLength <= PRESIGN_BUFFER_MAX_BYTES;
|
|
663
|
+
if (canBuffer) {
|
|
664
|
+
const buf = Buffer.from(await dl.arrayBuffer());
|
|
665
|
+
return {
|
|
666
|
+
Body: Readable.from(buf),
|
|
667
|
+
$metadata: {},
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
// Above the ceiling, or no declared length: keep streaming so a very large
|
|
671
|
+
// object is never buffered whole. fetch() yields a web ReadableStream; the
|
|
672
|
+
// orchestrators consume Body as a Node Readable (body.on('data') + stream
|
|
673
|
+
// pipeline), so adapt it. An empty body (no stream) becomes an empty
|
|
674
|
+
// Readable. The mkdirSync hoist in runGet/runCat narrows the residual
|
|
675
|
+
// paused-parser window on this branch.
|
|
622
676
|
const nodeBody = dl.body
|
|
623
677
|
? Readable.fromWeb(dl.body)
|
|
624
678
|
: Readable.from([]);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://hq.getindigo.ai/schemas/work-mesh-live/session-event.schema.json",
|
|
4
|
+
"title": "Work Mesh Live Session Event",
|
|
5
|
+
"description": "Metadata-only spool / batch-ingest session event (contractVersion 1). Local-only fields are permitted on the spool line and MUST be stripped by the daemon before any network call. There is no free-text intent field and no prompt-derived content.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"v",
|
|
10
|
+
"eventId",
|
|
11
|
+
"kind",
|
|
12
|
+
"sessionId",
|
|
13
|
+
"harness",
|
|
14
|
+
"adapterVersion",
|
|
15
|
+
"at",
|
|
16
|
+
"seq"
|
|
17
|
+
],
|
|
18
|
+
"properties": {
|
|
19
|
+
"v": {
|
|
20
|
+
"type": "integer",
|
|
21
|
+
"const": 1,
|
|
22
|
+
"description": "Schema version. Always 1 for this contract."
|
|
23
|
+
},
|
|
24
|
+
"eventId": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
|
|
27
|
+
"description": "ULID (Crockford base32, 26 chars). Idempotency key for batch ingest."
|
|
28
|
+
},
|
|
29
|
+
"kind": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": [
|
|
32
|
+
"session_start",
|
|
33
|
+
"turn_start",
|
|
34
|
+
"turn_end",
|
|
35
|
+
"session_end",
|
|
36
|
+
"task_status",
|
|
37
|
+
"blocked",
|
|
38
|
+
"note"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"sessionId": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"minLength": 1,
|
|
44
|
+
"maxLength": 128,
|
|
45
|
+
"description": "Stable runtime session identifier."
|
|
46
|
+
},
|
|
47
|
+
"harness": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"enum": [
|
|
50
|
+
"claude-code",
|
|
51
|
+
"claude-desktop",
|
|
52
|
+
"codex",
|
|
53
|
+
"grok",
|
|
54
|
+
"hq-sessions",
|
|
55
|
+
"agent-box"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"adapterVersion": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"minLength": 1,
|
|
61
|
+
"maxLength": 64,
|
|
62
|
+
"description": "Hook / adapter package version that emitted the event."
|
|
63
|
+
},
|
|
64
|
+
"runtimeVersion": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"minLength": 1,
|
|
67
|
+
"maxLength": 64,
|
|
68
|
+
"description": "Optional host runtime version (claude, codex, etc.)."
|
|
69
|
+
},
|
|
70
|
+
"source": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"enum": ["hooks", "transcript"],
|
|
73
|
+
"description": "Origin of the event stream: hooks (default) or the transcript-watch fallback (US-018)."
|
|
74
|
+
},
|
|
75
|
+
"at": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$",
|
|
78
|
+
"description": "ISO-8601 timestamp when the event occurred."
|
|
79
|
+
},
|
|
80
|
+
"seq": {
|
|
81
|
+
"type": "integer",
|
|
82
|
+
"minimum": 1,
|
|
83
|
+
"description": "Monotonic per-session sequence number, starting at 1."
|
|
84
|
+
},
|
|
85
|
+
"taskId": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"minLength": 1,
|
|
88
|
+
"maxLength": 128,
|
|
89
|
+
"description": "Canonical task identifier. storyId is an ingress alias normalized to taskId by the resolver; it is never a field on this schema."
|
|
90
|
+
},
|
|
91
|
+
"status": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"enum": [
|
|
94
|
+
"queued",
|
|
95
|
+
"in_progress",
|
|
96
|
+
"review",
|
|
97
|
+
"done"
|
|
98
|
+
],
|
|
99
|
+
"description": "Task status; used with kind=task_status."
|
|
100
|
+
},
|
|
101
|
+
"reason": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"minLength": 1,
|
|
104
|
+
"maxLength": 500,
|
|
105
|
+
"description": "Blocked reason or similar short machine-safe note. Never prompt text."
|
|
106
|
+
},
|
|
107
|
+
"summary": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"minLength": 1,
|
|
110
|
+
"maxLength": 280,
|
|
111
|
+
"description": "Optional short summary allowed only on note, blocked, and task_status. Never prompt-derived content."
|
|
112
|
+
},
|
|
113
|
+
"cwd": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"maxLength": 1024,
|
|
116
|
+
"description": "LOCAL-ONLY. Working directory at emit time. Stripped by the daemon before any network call."
|
|
117
|
+
},
|
|
118
|
+
"hqRoot": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"maxLength": 1024,
|
|
121
|
+
"description": "LOCAL-ONLY. Absolute path to the HQ tree root. Stripped before network."
|
|
122
|
+
},
|
|
123
|
+
"companySlug": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"maxLength": 128,
|
|
126
|
+
"description": "LOCAL-ONLY. Hint slug from local context. Never used as tenant authority. Stripped before network."
|
|
127
|
+
},
|
|
128
|
+
"project": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"maxLength": 256,
|
|
131
|
+
"description": "LOCAL-ONLY. Local project name/path hint. Stripped before network."
|
|
132
|
+
},
|
|
133
|
+
"task": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"maxLength": 256,
|
|
136
|
+
"description": "LOCAL-ONLY. Local task label hint. Stripped before network. Distinct from canonical taskId."
|
|
137
|
+
},
|
|
138
|
+
"toolWrites": {
|
|
139
|
+
"type": "integer",
|
|
140
|
+
"minimum": 0,
|
|
141
|
+
"description": "LOCAL-ONLY. Count of tool file writes observed this session. Stripped before network."
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://hq.getindigo.ai/schemas/work-context/contract.schema.json",
|
|
4
|
+
"title": "Work Context Reconciliation Contract",
|
|
5
|
+
"description": "Recreated 2026-09-03 from work-context-resolver architectural-decisions.md (US-001A) because feature/work-context-resolver-cli was not on origin.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{ "$ref": "#/$defs/SessionObservation" },
|
|
8
|
+
{ "$ref": "#/$defs/ContextResult" }
|
|
9
|
+
],
|
|
10
|
+
"$defs": {
|
|
11
|
+
"ClassificationState": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"enum": [
|
|
14
|
+
"unresolved",
|
|
15
|
+
"needs_company",
|
|
16
|
+
"company_conflict",
|
|
17
|
+
"needs_project",
|
|
18
|
+
"needs_task",
|
|
19
|
+
"bound",
|
|
20
|
+
"untracked",
|
|
21
|
+
"migration_pending"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"DeliveryState": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["clean", "queued", "acked", "quarantined"]
|
|
27
|
+
},
|
|
28
|
+
"LifecycleState": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"enum": ["open", "terminal"]
|
|
31
|
+
},
|
|
32
|
+
"ContextResultKind": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"enum": [
|
|
35
|
+
"bound",
|
|
36
|
+
"needs_company",
|
|
37
|
+
"company_conflict",
|
|
38
|
+
"needs_project",
|
|
39
|
+
"needs_task",
|
|
40
|
+
"queued",
|
|
41
|
+
"untracked",
|
|
42
|
+
"migration_pending",
|
|
43
|
+
"incompatible",
|
|
44
|
+
"error"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"SessionObservation": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["contractVersion", "identity", "clientOperationId"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"contractVersion": { "type": "integer", "const": 1 },
|
|
53
|
+
"identity": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"required": ["sessionId"],
|
|
57
|
+
"properties": {
|
|
58
|
+
"sessionId": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
59
|
+
"providerSessionId": { "type": "string", "maxLength": 128 },
|
|
60
|
+
"harness": { "type": "string", "maxLength": 64 },
|
|
61
|
+
"adapterVersion": { "type": "string", "maxLength": 64 },
|
|
62
|
+
"runtimeVersion": { "type": "string", "maxLength": 64 }
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"explicit": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"additionalProperties": false,
|
|
68
|
+
"properties": {
|
|
69
|
+
"companyUid": { "type": "string", "maxLength": 64 },
|
|
70
|
+
"projectId": { "type": "string", "maxLength": 128 },
|
|
71
|
+
"taskId": { "type": "string", "maxLength": 128 },
|
|
72
|
+
"storyId": { "type": "string", "maxLength": 128 }
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"clientOperationId": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
76
|
+
"untrackedOrigin": { "type": "string", "const": "user" }
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"ContextResult": {
|
|
80
|
+
"type": "object",
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"required": [
|
|
83
|
+
"contractVersion",
|
|
84
|
+
"kind",
|
|
85
|
+
"classification",
|
|
86
|
+
"delivery",
|
|
87
|
+
"lifecycle",
|
|
88
|
+
"sessionId",
|
|
89
|
+
"clientOperationId"
|
|
90
|
+
],
|
|
91
|
+
"properties": {
|
|
92
|
+
"contractVersion": { "type": "integer", "const": 1 },
|
|
93
|
+
"kind": { "$ref": "#/$defs/ContextResultKind" },
|
|
94
|
+
"classification": { "$ref": "#/$defs/ClassificationState" },
|
|
95
|
+
"delivery": { "$ref": "#/$defs/DeliveryState" },
|
|
96
|
+
"lifecycle": { "$ref": "#/$defs/LifecycleState" },
|
|
97
|
+
"sessionId": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
98
|
+
"companyUid": { "type": "string", "maxLength": 64 },
|
|
99
|
+
"projectId": { "type": "string", "maxLength": 128 },
|
|
100
|
+
"taskId": { "type": "string", "maxLength": 128 },
|
|
101
|
+
"bindingEpisodeId": { "type": "string", "maxLength": 128 },
|
|
102
|
+
"clientOperationId": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
103
|
+
"errorCode": { "type": "string", "maxLength": 64 }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "hq-package.yaml",
|
|
4
|
+
"description": "Manifest schema for HQ packages — versioned bundles of workers, commands, skills, and knowledge installable via hq install.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["name", "type", "version", "description", "author", "exposes"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Package slug — lowercase alphanumeric with hyphens. Used as the registry identifier and install target directory name.",
|
|
12
|
+
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$",
|
|
13
|
+
"minLength": 1
|
|
14
|
+
},
|
|
15
|
+
"type": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Package type. Controls install target routing for all exposes entries.",
|
|
18
|
+
"enum": ["worker-pack", "command-set", "skill-bundle", "knowledge-base", "company-template"]
|
|
19
|
+
},
|
|
20
|
+
"version": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Semver version string for this release.",
|
|
23
|
+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
|
|
24
|
+
},
|
|
25
|
+
"minHQVersion": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Minimum HQ version required to install this package. Installer aborts if installed HQ version is below this value.",
|
|
28
|
+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
|
|
29
|
+
},
|
|
30
|
+
"description": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Short human-readable description of what this package provides.",
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"maxLength": 500
|
|
35
|
+
},
|
|
36
|
+
"author": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Author name or organization that created and maintains this package.",
|
|
39
|
+
"minLength": 1
|
|
40
|
+
},
|
|
41
|
+
"repo": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "Git repository URL for the package source. Used as offline fallback when the registry is unavailable.",
|
|
44
|
+
"pattern": "^(https?://|git@|ssh://).+"
|
|
45
|
+
},
|
|
46
|
+
"requires": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"description": "External dependencies this package needs at runtime.",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"properties": {
|
|
51
|
+
"packages": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"description": "HQ package slugs this package depends on. Installed before this package.",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$"
|
|
57
|
+
},
|
|
58
|
+
"uniqueItems": true
|
|
59
|
+
},
|
|
60
|
+
"services": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"description": "Service slugs required at runtime. Installer warns if not configured in the target HQ installation.",
|
|
63
|
+
"items": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"minLength": 1
|
|
66
|
+
},
|
|
67
|
+
"uniqueItems": true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"exposes": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"description": "Declares what this package installs into the HQ workspace. At least one sub-array must be non-empty.",
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"workers": {
|
|
77
|
+
"type": "array",
|
|
78
|
+
"description": "Paths within the tarball to worker.yaml files to install.",
|
|
79
|
+
"items": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"minLength": 1
|
|
82
|
+
},
|
|
83
|
+
"uniqueItems": true
|
|
84
|
+
},
|
|
85
|
+
"commands": {
|
|
86
|
+
"type": "array",
|
|
87
|
+
"description": "Paths within the tarball to command .md files to install.",
|
|
88
|
+
"items": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"minLength": 1
|
|
91
|
+
},
|
|
92
|
+
"uniqueItems": true
|
|
93
|
+
},
|
|
94
|
+
"skills": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"description": "Paths within the tarball to skill .md files to install.",
|
|
97
|
+
"items": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"minLength": 1
|
|
100
|
+
},
|
|
101
|
+
"uniqueItems": true
|
|
102
|
+
},
|
|
103
|
+
"knowledge": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"description": "Paths within the tarball to knowledge directories or files to install.",
|
|
106
|
+
"items": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"minLength": 1
|
|
109
|
+
},
|
|
110
|
+
"uniqueItems": true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"hooks": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"description": "Shell scripts run at install lifecycle events. Each value is a path within the tarball to an executable shell script.",
|
|
117
|
+
"additionalProperties": false,
|
|
118
|
+
"properties": {
|
|
119
|
+
"on-install": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"description": "Path to shell script run after all files are installed.",
|
|
122
|
+
"minLength": 1
|
|
123
|
+
},
|
|
124
|
+
"on-update": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"description": "Path to shell script run after an existing installation is updated.",
|
|
127
|
+
"minLength": 1
|
|
128
|
+
},
|
|
129
|
+
"on-remove": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "Path to shell script run before files are removed from the HQ workspace.",
|
|
132
|
+
"minLength": 1
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"initialization": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"description": "Optional post-install onboarding. `entrypoint` is the pack's primary get-started action (a skill or command that must resolve to one of this package's exposes.skills / exposes.commands entries); HQ surfaces a safe auto-generated 'get started' line from it after install. `prompt` is optional free-text the user can copy/paste into their agent to begin setup — treated as UNTRUSTED instruction text, surfaced only after marketplace injection-scan/moderation (suppressed for non-marketplace installs).",
|
|
139
|
+
"additionalProperties": false,
|
|
140
|
+
"properties": {
|
|
141
|
+
"entrypoint": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"minLength": 1,
|
|
144
|
+
"description": "Primary get-started action — a skill or command name (with or without a leading slash) that must resolve to a declared exposes.skills / exposes.commands entry."
|
|
145
|
+
},
|
|
146
|
+
"prompt": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"maxLength": 2000,
|
|
149
|
+
"description": "Optional copy/paste setup prompt. Untrusted; surfaced only after moderation for marketplace packs, suppressed for local/git installs."
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"required": ["entrypoint"]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indigoai-us/hq-cli",
|
|
3
|
-
"version": "5.108.
|
|
3
|
+
"version": "5.108.21",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"CHANGELOG.md"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "node scripts/generate-dsn.mjs && tsc && node scripts/chmod-bins.mjs",
|
|
18
|
+
"build": "node scripts/generate-dsn.mjs && tsc && node scripts/copy-schemas.mjs && node scripts/chmod-bins.mjs",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
21
21
|
"gen:scan-golden": "node scripts/generate-scan-packages-table.mjs > src/utils/__fixtures__/scan-packages.generated-block.sh",
|