@warmhub/cli 0.96.0 → 0.98.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/dist/wh.js +1070 -96
- package/package.json +8 -2
package/dist/wh.js
CHANGED
|
@@ -19421,7 +19421,15 @@ function formatClientHeader(name, version) {
|
|
|
19421
19421
|
var DEFAULT_MAX_OPS_PER_STREAM = 1e6;
|
|
19422
19422
|
var DEFAULT_STREAM_APPEND_CHUNK_SIZE = 1000;
|
|
19423
19423
|
var MAX_STREAM_APPEND_OPERATION_COUNT = 1e4;
|
|
19424
|
+
var MAX_STREAMING_SUBMISSION_LINE_BYTES = 1024 * 1024;
|
|
19424
19425
|
// ../../packages/rules/src/commit-types.ts
|
|
19426
|
+
var COMMIT_OPERATIONS = [
|
|
19427
|
+
"add",
|
|
19428
|
+
"revise",
|
|
19429
|
+
"retract",
|
|
19430
|
+
"reaffirm",
|
|
19431
|
+
"rename"
|
|
19432
|
+
];
|
|
19425
19433
|
var COMMIT_OPERATION_KINDS = [
|
|
19426
19434
|
"shape",
|
|
19427
19435
|
"thing",
|
|
@@ -43376,7 +43384,7 @@ class OperationSourceError extends Error {
|
|
|
43376
43384
|
}
|
|
43377
43385
|
}
|
|
43378
43386
|
async function submitOperationsViaStream(client, args) {
|
|
43379
|
-
const chunkSize =
|
|
43387
|
+
const chunkSize = normalizeStreamChunkSize(args.chunkSize);
|
|
43380
43388
|
const submissionId = args.submissionId ?? createOperationEventSubmissionId();
|
|
43381
43389
|
const streamId = args.streamId ?? submissionId;
|
|
43382
43390
|
const policy = resolveRetryPolicy(args.retry);
|
|
@@ -43476,7 +43484,7 @@ async function submitOperationsViaStream(client, args) {
|
|
|
43476
43484
|
async function* normalizedChunks(source, chunkSize, skipExisting) {
|
|
43477
43485
|
try {
|
|
43478
43486
|
if (Array.isArray(source)) {
|
|
43479
|
-
const operations = source.map((operation, index) =>
|
|
43487
|
+
const operations = source.map((operation, index) => normalizeStreamOperation(operation, index, skipExisting));
|
|
43480
43488
|
validateNormalizedOperations(operations, 0);
|
|
43481
43489
|
for (let start = 0;start < operations.length; start += chunkSize) {
|
|
43482
43490
|
yield { operations: operations.slice(start, start + chunkSize), start };
|
|
@@ -43485,8 +43493,8 @@ async function* normalizedChunks(source, chunkSize, skipExisting) {
|
|
|
43485
43493
|
}
|
|
43486
43494
|
let normalizedCount = 0;
|
|
43487
43495
|
let buffer = [];
|
|
43488
|
-
for await (const operation of source) {
|
|
43489
|
-
buffer.push(
|
|
43496
|
+
for await (const operation of normalizedOperationSource(source, skipExisting)) {
|
|
43497
|
+
buffer.push(operation);
|
|
43490
43498
|
normalizedCount += 1;
|
|
43491
43499
|
if (buffer.length < chunkSize)
|
|
43492
43500
|
continue;
|
|
@@ -43504,7 +43512,7 @@ function preflightedChunk(operations, normalizedCount) {
|
|
|
43504
43512
|
validateNormalizedOperations(operations, start);
|
|
43505
43513
|
return { operations, start };
|
|
43506
43514
|
}
|
|
43507
|
-
function
|
|
43515
|
+
function normalizeStreamOperation(operation, index, skipExisting) {
|
|
43508
43516
|
let streamOperation;
|
|
43509
43517
|
try {
|
|
43510
43518
|
streamOperation = toBackendStreamOperation(operation);
|
|
@@ -43517,6 +43525,57 @@ function normalizeOperation(operation, index, skipExisting) {
|
|
|
43517
43525
|
}
|
|
43518
43526
|
return streamOperation;
|
|
43519
43527
|
}
|
|
43528
|
+
async function* normalizedOperationSource(source, skipExisting) {
|
|
43529
|
+
const iterator = createNormalizedOperationIterator(source, skipExisting);
|
|
43530
|
+
try {
|
|
43531
|
+
while (true) {
|
|
43532
|
+
const next = await iterator.next();
|
|
43533
|
+
if (next.done)
|
|
43534
|
+
return;
|
|
43535
|
+
yield next.value;
|
|
43536
|
+
}
|
|
43537
|
+
} finally {
|
|
43538
|
+
const returned = iterator.returnSourceOnce();
|
|
43539
|
+
if (returned)
|
|
43540
|
+
await returned;
|
|
43541
|
+
}
|
|
43542
|
+
}
|
|
43543
|
+
function createNormalizedOperationIterator(source, skipExisting) {
|
|
43544
|
+
let sourceIterator;
|
|
43545
|
+
let index = 0;
|
|
43546
|
+
let returned = false;
|
|
43547
|
+
let done = false;
|
|
43548
|
+
const acquire = () => {
|
|
43549
|
+
if (sourceIterator)
|
|
43550
|
+
return sourceIterator;
|
|
43551
|
+
sourceIterator = Symbol.asyncIterator in source ? source[Symbol.asyncIterator]() : source[Symbol.iterator]();
|
|
43552
|
+
return sourceIterator;
|
|
43553
|
+
};
|
|
43554
|
+
return {
|
|
43555
|
+
async next() {
|
|
43556
|
+
const next = await acquire().next();
|
|
43557
|
+
if (next.done) {
|
|
43558
|
+
done = true;
|
|
43559
|
+
return { done: true, value: undefined };
|
|
43560
|
+
}
|
|
43561
|
+
const value = normalizeStreamOperation(next.value, index, skipExisting);
|
|
43562
|
+
index += 1;
|
|
43563
|
+
return { done: false, value };
|
|
43564
|
+
},
|
|
43565
|
+
returnSourceOnce() {
|
|
43566
|
+
if (returned)
|
|
43567
|
+
return;
|
|
43568
|
+
returned = true;
|
|
43569
|
+
if (!done)
|
|
43570
|
+
return sourceIterator?.return?.();
|
|
43571
|
+
}
|
|
43572
|
+
};
|
|
43573
|
+
}
|
|
43574
|
+
function encodeStreamOperation(operation) {
|
|
43575
|
+
return streamOperationEncoder.encode(`${JSON.stringify(operation)}
|
|
43576
|
+
`);
|
|
43577
|
+
}
|
|
43578
|
+
var streamOperationEncoder = new TextEncoder;
|
|
43520
43579
|
function receiptRepoSeq(receipt) {
|
|
43521
43580
|
const repoSeq = receipt.event?.repoSeq;
|
|
43522
43581
|
if (repoSeq === undefined)
|
|
@@ -43533,15 +43592,579 @@ function validateNormalizedOperations(operations, indexOffset) {
|
|
|
43533
43592
|
function isServerAuthoritativeSequenceDiagnostic(diagnostic) {
|
|
43534
43593
|
return diagnostic.code === "ILLEGAL_OP_SEQUENCE" && diagnostic.message.includes("Cannot revise then add ");
|
|
43535
43594
|
}
|
|
43536
|
-
function
|
|
43595
|
+
function normalizeStreamChunkSize(chunkSize) {
|
|
43537
43596
|
if (!Number.isFinite(chunkSize))
|
|
43538
43597
|
return DEFAULT_STREAM_CHUNK_SIZE;
|
|
43539
43598
|
return Math.max(1, Math.min(MAX_STREAM_APPEND_OPERATION_COUNT2, Math.trunc(chunkSize)));
|
|
43540
43599
|
}
|
|
43600
|
+
// ../../packages/sdk-ts/src/streaming-submission-protocol.ts
|
|
43601
|
+
var MANIFEST_DOMAIN = "warmhub/streaming-submission-manifest/v1";
|
|
43602
|
+
var manifestEncoder = new TextEncoder;
|
|
43603
|
+
var digestSchema = exports_external.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
43604
|
+
var positionShape = {
|
|
43605
|
+
type: exports_external.literal("result"),
|
|
43606
|
+
chunkOrdinal: exports_external.number().int().nonnegative(),
|
|
43607
|
+
opIndex: exports_external.number().int().nonnegative(),
|
|
43608
|
+
submissionIndex: exports_external.number().int().nonnegative(),
|
|
43609
|
+
name: exports_external.string()
|
|
43610
|
+
};
|
|
43611
|
+
var currentOperationSchema = exports_external.enum(COMMIT_OPERATIONS);
|
|
43612
|
+
var legacyOperationSchema = exports_external.enum([...COMMIT_OPERATIONS, "noop"]);
|
|
43613
|
+
var issueSchema = exports_external.object({ path: exports_external.string(), message: exports_external.string() });
|
|
43614
|
+
var warningSchema = exports_external.object({
|
|
43615
|
+
undeclaredFields: exports_external.array(exports_external.string()).optional(),
|
|
43616
|
+
undeclaredFieldsTruncated: exports_external.literal(true).optional(),
|
|
43617
|
+
totalUndeclared: exports_external.number().int().nonnegative().optional(),
|
|
43618
|
+
coalescedWrefs: exports_external.array(exports_external.object({
|
|
43619
|
+
fieldPath: exports_external.string(),
|
|
43620
|
+
wref: exports_external.string(),
|
|
43621
|
+
reason: exports_external.string()
|
|
43622
|
+
})).optional(),
|
|
43623
|
+
coalescedWrefsTruncated: exports_external.literal(true).optional(),
|
|
43624
|
+
totalCoalescedWrefs: exports_external.number().int().nonnegative().optional(),
|
|
43625
|
+
deprecations: exports_external.array(exports_external.object({
|
|
43626
|
+
shape: exports_external.string(),
|
|
43627
|
+
message: exports_external.string(),
|
|
43628
|
+
removalMilestone: exports_external.string()
|
|
43629
|
+
})).optional()
|
|
43630
|
+
}).optional();
|
|
43631
|
+
var affirmationSchema = exports_external.object({
|
|
43632
|
+
added: exports_external.array(exports_external.string()),
|
|
43633
|
+
removed: exports_external.array(exports_external.string()),
|
|
43634
|
+
ignored: exports_external.array(exports_external.string())
|
|
43635
|
+
});
|
|
43636
|
+
var legacyErrorDetailsSchema = exports_external.discriminatedUnion("reason", [
|
|
43637
|
+
exports_external.object({
|
|
43638
|
+
reason: exports_external.literal("expected_version_mismatch"),
|
|
43639
|
+
expectedVersion: exports_external.number().int().positive(),
|
|
43640
|
+
currentVersion: exports_external.number().int().positive()
|
|
43641
|
+
}),
|
|
43642
|
+
exports_external.object({
|
|
43643
|
+
reason: exports_external.literal("lease_held"),
|
|
43644
|
+
leaseExpiresAt: exports_external.string().datetime()
|
|
43645
|
+
}),
|
|
43646
|
+
exports_external.object({
|
|
43647
|
+
reason: exports_external.literal("validation_failed"),
|
|
43648
|
+
issues: exports_external.array(issueSchema)
|
|
43649
|
+
}),
|
|
43650
|
+
exports_external.object({
|
|
43651
|
+
reason: exports_external.literal("rate_limit_reset"),
|
|
43652
|
+
retryAfterSeconds: exports_external.number().int().positive(),
|
|
43653
|
+
resetAt: exports_external.string().datetime()
|
|
43654
|
+
}),
|
|
43655
|
+
exports_external.object({
|
|
43656
|
+
reason: exports_external.literal("cursor_fence_unavailable"),
|
|
43657
|
+
cause: exports_external.enum(["not_ready", "below_floor", "above_head"]),
|
|
43658
|
+
retryFromStart: exports_external.literal(true)
|
|
43659
|
+
})
|
|
43660
|
+
]);
|
|
43661
|
+
var currentErrorDetailsSchema = exports_external.discriminatedUnion("reason", [
|
|
43662
|
+
exports_external.object({
|
|
43663
|
+
reason: exports_external.literal("expected_version_mismatch"),
|
|
43664
|
+
expectedVersion: exports_external.number().int().nonnegative(),
|
|
43665
|
+
currentVersion: exports_external.number().int().nonnegative()
|
|
43666
|
+
}).strict(),
|
|
43667
|
+
exports_external.object({
|
|
43668
|
+
reason: exports_external.literal("lease_held"),
|
|
43669
|
+
leaseExpiresAt: exports_external.string().datetime()
|
|
43670
|
+
}).strict(),
|
|
43671
|
+
exports_external.object({
|
|
43672
|
+
reason: exports_external.literal("validation_failed"),
|
|
43673
|
+
issues: exports_external.array(issueSchema.strict())
|
|
43674
|
+
}).strict(),
|
|
43675
|
+
exports_external.object({
|
|
43676
|
+
reason: exports_external.literal("rate_limit_reset"),
|
|
43677
|
+
retryAfterSeconds: exports_external.number().nonnegative(),
|
|
43678
|
+
resetAt: exports_external.string().datetime()
|
|
43679
|
+
}).strict(),
|
|
43680
|
+
exports_external.object({
|
|
43681
|
+
reason: exports_external.literal("cursor_fence_unavailable"),
|
|
43682
|
+
cause: exports_external.enum(["not_ready", "below_floor", "above_head"]),
|
|
43683
|
+
retryFromStart: exports_external.literal(true)
|
|
43684
|
+
}).strict(),
|
|
43685
|
+
exports_external.object({
|
|
43686
|
+
reason: exports_external.literal("dependency_failed"),
|
|
43687
|
+
producerOpIndexes: exports_external.array(exports_external.number().int().nonnegative()).min(1)
|
|
43688
|
+
}).strict()
|
|
43689
|
+
]);
|
|
43690
|
+
var currentBaseShape = {
|
|
43691
|
+
...positionShape,
|
|
43692
|
+
operation: currentOperationSchema,
|
|
43693
|
+
submittedName: exports_external.string().optional(),
|
|
43694
|
+
resolvedName: exports_external.string().optional(),
|
|
43695
|
+
warnings: warningSchema,
|
|
43696
|
+
affirmations: affirmationSchema.optional()
|
|
43697
|
+
};
|
|
43698
|
+
var resultSchema = exports_external.union([
|
|
43699
|
+
exports_external.object({
|
|
43700
|
+
...positionShape,
|
|
43701
|
+
operation: legacyOperationSchema.optional(),
|
|
43702
|
+
version: exports_external.number().optional(),
|
|
43703
|
+
dataHash: exports_external.string().optional(),
|
|
43704
|
+
status: exports_external.enum(["success", "noop", "failed"]).optional(),
|
|
43705
|
+
error: exports_external.object({
|
|
43706
|
+
code: exports_external.string(),
|
|
43707
|
+
message: exports_external.string(),
|
|
43708
|
+
details: legacyErrorDetailsSchema.optional()
|
|
43709
|
+
}).optional(),
|
|
43710
|
+
resolvedName: exports_external.string().optional(),
|
|
43711
|
+
retryable: exports_external.boolean().optional(),
|
|
43712
|
+
warnings: warningSchema,
|
|
43713
|
+
affirmations: affirmationSchema.optional()
|
|
43714
|
+
}).passthrough(),
|
|
43715
|
+
exports_external.object({
|
|
43716
|
+
...currentBaseShape,
|
|
43717
|
+
status: exports_external.enum(["applied", "noop"]),
|
|
43718
|
+
version: exports_external.number().int().positive().optional(),
|
|
43719
|
+
dataHash: exports_external.string().min(1).optional()
|
|
43720
|
+
}).passthrough(),
|
|
43721
|
+
exports_external.object({
|
|
43722
|
+
...currentBaseShape,
|
|
43723
|
+
status: exports_external.literal("error"),
|
|
43724
|
+
errors: exports_external.array(exports_external.object({
|
|
43725
|
+
code: exports_external.string().min(1),
|
|
43726
|
+
message: exports_external.string(),
|
|
43727
|
+
path: exports_external.string().min(1).optional(),
|
|
43728
|
+
details: currentErrorDetailsSchema.optional(),
|
|
43729
|
+
retryable: exports_external.boolean().optional()
|
|
43730
|
+
}).strict()).min(1)
|
|
43731
|
+
}).passthrough()
|
|
43732
|
+
]);
|
|
43733
|
+
var operationCountsSchema = exports_external.object({
|
|
43734
|
+
applied: exports_external.number().int().nonnegative(),
|
|
43735
|
+
noop: exports_external.number().int().nonnegative(),
|
|
43736
|
+
failed: exports_external.number().int().nonnegative()
|
|
43737
|
+
});
|
|
43738
|
+
var groupSchema = exports_external.object({
|
|
43739
|
+
type: exports_external.literal("group"),
|
|
43740
|
+
chunkOrdinal: exports_external.number().int().nonnegative(),
|
|
43741
|
+
eventRequestId: exports_external.string().uuid(),
|
|
43742
|
+
requestDigest: digestSchema,
|
|
43743
|
+
outcome: exports_external.enum(["event", "no_event"]),
|
|
43744
|
+
repoSeq: exports_external.number().int().nonnegative().optional(),
|
|
43745
|
+
operations: operationCountsSchema,
|
|
43746
|
+
manifestDigest: digestSchema
|
|
43747
|
+
}).passthrough();
|
|
43748
|
+
var summarySchema = exports_external.object({
|
|
43749
|
+
type: exports_external.literal("summary"),
|
|
43750
|
+
submissionId: exports_external.string().uuid(),
|
|
43751
|
+
streamId: exports_external.string().min(1),
|
|
43752
|
+
groupSize: exports_external.number().int().positive(),
|
|
43753
|
+
groups: exports_external.number().int().nonnegative(),
|
|
43754
|
+
operations: operationCountsSchema.extend({
|
|
43755
|
+
total: exports_external.number().int().nonnegative()
|
|
43756
|
+
}),
|
|
43757
|
+
repoSeq: exports_external.object({
|
|
43758
|
+
first: exports_external.number().int().nonnegative().nullable(),
|
|
43759
|
+
last: exports_external.number().int().nonnegative().nullable()
|
|
43760
|
+
}),
|
|
43761
|
+
verdict: exports_external.string().min(1),
|
|
43762
|
+
durationMs: exports_external.number().nonnegative(),
|
|
43763
|
+
truncatedAt: exports_external.object({
|
|
43764
|
+
lineNumber: exports_external.number().int().positive(),
|
|
43765
|
+
message: exports_external.string()
|
|
43766
|
+
}).optional(),
|
|
43767
|
+
manifest: exports_external.object({
|
|
43768
|
+
groups: exports_external.number().int().nonnegative(),
|
|
43769
|
+
digest: digestSchema
|
|
43770
|
+
})
|
|
43771
|
+
}).passthrough();
|
|
43772
|
+
var errorSchema = exports_external.object({
|
|
43773
|
+
type: exports_external.literal("error"),
|
|
43774
|
+
code: exports_external.string().min(1),
|
|
43775
|
+
message: exports_external.string().optional()
|
|
43776
|
+
}).passthrough();
|
|
43777
|
+
var rowSchema = exports_external.union([
|
|
43778
|
+
resultSchema,
|
|
43779
|
+
groupSchema,
|
|
43780
|
+
summarySchema,
|
|
43781
|
+
errorSchema
|
|
43782
|
+
]);
|
|
43783
|
+
var emptyCounts = () => ({ applied: 0, noop: 0, failed: 0 });
|
|
43784
|
+
var countsMatch = (left, right) => left.applied === right.applied && left.noop === right.noop && left.failed === right.failed;
|
|
43785
|
+
var exactTotalVerdicts = new Set([
|
|
43786
|
+
"complete",
|
|
43787
|
+
"operation-limit-reached",
|
|
43788
|
+
"group-limit-reached"
|
|
43789
|
+
]);
|
|
43790
|
+
|
|
43791
|
+
class StreamingSubmissionProtocolError extends Error {
|
|
43792
|
+
rowIndex;
|
|
43793
|
+
constructor(message, rowIndex) {
|
|
43794
|
+
super(`Streaming submission protocol error at row ${rowIndex}: ${message}`);
|
|
43795
|
+
this.rowIndex = rowIndex;
|
|
43796
|
+
this.name = "StreamingSubmissionProtocolError";
|
|
43797
|
+
}
|
|
43798
|
+
}
|
|
43799
|
+
async function sha256(value) {
|
|
43800
|
+
const bytes = manifestEncoder.encode(value);
|
|
43801
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
43802
|
+
return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
43803
|
+
}
|
|
43804
|
+
async function advanceManifest(previous, chunkOrdinal, requestDigest) {
|
|
43805
|
+
return sha256(`${MANIFEST_DOMAIN}\x00${previous}\x00${chunkOrdinal}\x00${requestDigest}`);
|
|
43806
|
+
}
|
|
43807
|
+
function resultOutcome(row) {
|
|
43808
|
+
if (row.status === "failed" || row.status === "error")
|
|
43809
|
+
return "failed";
|
|
43810
|
+
if (row.status === "noop" || row.operation === "noop")
|
|
43811
|
+
return "noop";
|
|
43812
|
+
return "applied";
|
|
43813
|
+
}
|
|
43814
|
+
|
|
43815
|
+
class SequenceValidator {
|
|
43816
|
+
index = 0;
|
|
43817
|
+
groupCount = 0;
|
|
43818
|
+
resultCount = 0;
|
|
43819
|
+
pendingCount = 0;
|
|
43820
|
+
pendingCounts = emptyCounts();
|
|
43821
|
+
totalCounts = emptyCounts();
|
|
43822
|
+
firstRepoSeq = null;
|
|
43823
|
+
lastRepoSeq = null;
|
|
43824
|
+
manifestDigest = sha256(MANIFEST_DOMAIN);
|
|
43825
|
+
terminalSeen = false;
|
|
43826
|
+
accept(row) {
|
|
43827
|
+
const fail = (message) => {
|
|
43828
|
+
throw new StreamingSubmissionProtocolError(message, this.index);
|
|
43829
|
+
};
|
|
43830
|
+
if (this.terminalSeen)
|
|
43831
|
+
fail("No row may follow the terminal row");
|
|
43832
|
+
if (row.type === "result") {
|
|
43833
|
+
if (row.chunkOrdinal !== this.groupCount)
|
|
43834
|
+
fail(`Expected result chunkOrdinal ${this.groupCount}`);
|
|
43835
|
+
if (row.opIndex !== this.pendingCount)
|
|
43836
|
+
fail(`Expected result opIndex ${this.pendingCount}`);
|
|
43837
|
+
if (row.submissionIndex !== this.resultCount)
|
|
43838
|
+
fail(`Expected submissionIndex ${this.resultCount}`);
|
|
43839
|
+
const outcome = resultOutcome(row);
|
|
43840
|
+
this.pendingCounts[outcome] += 1;
|
|
43841
|
+
this.totalCounts[outcome] += 1;
|
|
43842
|
+
this.pendingCount += 1;
|
|
43843
|
+
this.resultCount += 1;
|
|
43844
|
+
this.index += 1;
|
|
43845
|
+
return;
|
|
43846
|
+
}
|
|
43847
|
+
if (row.type === "group") {
|
|
43848
|
+
if (row.chunkOrdinal !== this.groupCount)
|
|
43849
|
+
fail(`Expected group chunkOrdinal ${this.groupCount}`);
|
|
43850
|
+
if (this.pendingCount === 0)
|
|
43851
|
+
fail("A group must close result rows");
|
|
43852
|
+
if (!countsMatch(this.pendingCounts, row.operations))
|
|
43853
|
+
fail("Group operation counters do not match its result rows");
|
|
43854
|
+
return this.acceptGroup(row, fail);
|
|
43855
|
+
}
|
|
43856
|
+
this.terminalSeen = true;
|
|
43857
|
+
if (this.pendingCount > 0)
|
|
43858
|
+
fail("Terminal row cannot close a partial group");
|
|
43859
|
+
if (row.type === "summary") {
|
|
43860
|
+
return this.acceptSummary(row, fail);
|
|
43861
|
+
}
|
|
43862
|
+
this.index += 1;
|
|
43863
|
+
}
|
|
43864
|
+
async acceptGroup(row, fail) {
|
|
43865
|
+
this.manifestDigest = this.manifestDigest.then((previous) => advanceManifest(previous, row.chunkOrdinal, row.requestDigest));
|
|
43866
|
+
if (row.manifestDigest !== await this.manifestDigest)
|
|
43867
|
+
fail("Group manifest digest does not match the receipt chain");
|
|
43868
|
+
if (row.repoSeq !== undefined) {
|
|
43869
|
+
this.firstRepoSeq ??= row.repoSeq;
|
|
43870
|
+
this.lastRepoSeq = row.repoSeq;
|
|
43871
|
+
}
|
|
43872
|
+
this.groupCount += 1;
|
|
43873
|
+
this.pendingCount = 0;
|
|
43874
|
+
this.pendingCounts = emptyCounts();
|
|
43875
|
+
this.index += 1;
|
|
43876
|
+
}
|
|
43877
|
+
async acceptSummary(row, fail) {
|
|
43878
|
+
if (!countsMatch(this.totalCounts, row.operations))
|
|
43879
|
+
fail("Summary operation counters do not match result rows");
|
|
43880
|
+
if (row.groups !== this.groupCount || row.manifest.groups !== this.groupCount)
|
|
43881
|
+
fail("Summary group counters do not match group rows");
|
|
43882
|
+
if (row.manifest.digest !== await this.manifestDigest)
|
|
43883
|
+
fail("Summary manifest digest does not match the receipt chain");
|
|
43884
|
+
if (row.repoSeq.first !== this.firstRepoSeq || row.repoSeq.last !== this.lastRepoSeq)
|
|
43885
|
+
fail("Summary repo sequence does not match group rows");
|
|
43886
|
+
const totalIsInvalid = exactTotalVerdicts.has(row.verdict) ? row.operations.total !== this.resultCount : row.operations.total < this.resultCount;
|
|
43887
|
+
if (totalIsInvalid)
|
|
43888
|
+
fail("Summary total is incompatible with its verdict");
|
|
43889
|
+
if (row.verdict === "truncated-by-parse-error" !== (row.truncatedAt !== undefined))
|
|
43890
|
+
fail("truncatedAt must appear only for a parse-truncated verdict");
|
|
43891
|
+
this.index += 1;
|
|
43892
|
+
}
|
|
43893
|
+
finish() {
|
|
43894
|
+
if (!this.terminalSeen)
|
|
43895
|
+
throw new StreamingSubmissionProtocolError("Response must end with a summary or error row", this.index);
|
|
43896
|
+
}
|
|
43897
|
+
}
|
|
43898
|
+
function parseRow(line, index) {
|
|
43899
|
+
let value;
|
|
43900
|
+
try {
|
|
43901
|
+
value = JSON.parse(line);
|
|
43902
|
+
} catch (cause) {
|
|
43903
|
+
throw new StreamingSubmissionProtocolError(cause instanceof Error ? cause.message : "Invalid JSON", index);
|
|
43904
|
+
}
|
|
43905
|
+
const parsed = rowSchema.safeParse(value);
|
|
43906
|
+
if (!parsed.success) {
|
|
43907
|
+
throw new StreamingSubmissionProtocolError(parsed.error.issues[0]?.message ?? "Malformed row", index);
|
|
43908
|
+
}
|
|
43909
|
+
return parsed.data;
|
|
43910
|
+
}
|
|
43911
|
+
async function* readStreamingSubmissionRows(response, expectedIdentity) {
|
|
43912
|
+
const reader = response.body?.getReader();
|
|
43913
|
+
if (!reader)
|
|
43914
|
+
throw new StreamingSubmissionProtocolError("Missing response body", 0);
|
|
43915
|
+
const validator = new SequenceValidator;
|
|
43916
|
+
const decoder = new TextDecoder;
|
|
43917
|
+
let buffer = "";
|
|
43918
|
+
let rowIndex = 0;
|
|
43919
|
+
let finished = false;
|
|
43920
|
+
let terminalRow;
|
|
43921
|
+
const consume = (line) => {
|
|
43922
|
+
if (line.endsWith("\r"))
|
|
43923
|
+
line = line.slice(0, -1);
|
|
43924
|
+
if (line.length === 0)
|
|
43925
|
+
throw new StreamingSubmissionProtocolError("Blank response row", rowIndex);
|
|
43926
|
+
const row = parseRow(line, rowIndex);
|
|
43927
|
+
const pending = validator.accept(row);
|
|
43928
|
+
return { pending, row };
|
|
43929
|
+
};
|
|
43930
|
+
const validateIdentity = (row) => {
|
|
43931
|
+
if (row.type === "summary" && expectedIdentity !== undefined && (row.submissionId !== expectedIdentity.submissionId || row.streamId !== expectedIdentity.streamId || row.groupSize !== expectedIdentity.groupSize)) {
|
|
43932
|
+
throw new StreamingSubmissionProtocolError("Summary identity does not match the submission handle", rowIndex);
|
|
43933
|
+
}
|
|
43934
|
+
rowIndex += 1;
|
|
43935
|
+
};
|
|
43936
|
+
try {
|
|
43937
|
+
while (true) {
|
|
43938
|
+
const { done, value } = await reader.read();
|
|
43939
|
+
if (done)
|
|
43940
|
+
break;
|
|
43941
|
+
buffer += decoder.decode(value, { stream: true });
|
|
43942
|
+
while (true) {
|
|
43943
|
+
const newline = buffer.indexOf(`
|
|
43944
|
+
`);
|
|
43945
|
+
if (newline === -1)
|
|
43946
|
+
break;
|
|
43947
|
+
const line = buffer.slice(0, newline);
|
|
43948
|
+
buffer = buffer.slice(newline + 1);
|
|
43949
|
+
const { pending, row } = consume(line);
|
|
43950
|
+
if (pending)
|
|
43951
|
+
await pending;
|
|
43952
|
+
validateIdentity(row);
|
|
43953
|
+
if (row.type === "summary" || row.type === "error")
|
|
43954
|
+
terminalRow = row;
|
|
43955
|
+
else
|
|
43956
|
+
yield row;
|
|
43957
|
+
}
|
|
43958
|
+
}
|
|
43959
|
+
buffer += decoder.decode();
|
|
43960
|
+
if (buffer.length > 0) {
|
|
43961
|
+
const { pending, row } = consume(buffer);
|
|
43962
|
+
if (pending)
|
|
43963
|
+
await pending;
|
|
43964
|
+
validateIdentity(row);
|
|
43965
|
+
if (row.type === "summary" || row.type === "error")
|
|
43966
|
+
terminalRow = row;
|
|
43967
|
+
else
|
|
43968
|
+
yield row;
|
|
43969
|
+
}
|
|
43970
|
+
validator.finish();
|
|
43971
|
+
finished = true;
|
|
43972
|
+
if (terminalRow)
|
|
43973
|
+
yield terminalRow;
|
|
43974
|
+
} finally {
|
|
43975
|
+
if (!finished)
|
|
43976
|
+
await reader.cancel().catch(() => {
|
|
43977
|
+
return;
|
|
43978
|
+
});
|
|
43979
|
+
reader.releaseLock();
|
|
43980
|
+
}
|
|
43981
|
+
}
|
|
43982
|
+
|
|
43983
|
+
// ../../packages/sdk-ts/src/streaming-submission-types.ts
|
|
43984
|
+
class StreamingSubmissionOutcomeUnknownError extends Error {
|
|
43985
|
+
code = "STREAMING_SUBMISSION_OUTCOME_UNKNOWN";
|
|
43986
|
+
outcome = "unknown";
|
|
43987
|
+
retryable = false;
|
|
43988
|
+
retryIdentity;
|
|
43989
|
+
cause;
|
|
43990
|
+
constructor(retryIdentity, cause) {
|
|
43991
|
+
super(`Streaming submission outcome is unknown for ${retryIdentity.submissionId}. Reconstruct the original source and copy every retryIdentity field into the next applyStreaming call.`);
|
|
43992
|
+
this.name = "StreamingSubmissionOutcomeUnknownError";
|
|
43993
|
+
this.retryIdentity = retryIdentity;
|
|
43994
|
+
this.cause = cause;
|
|
43995
|
+
}
|
|
43996
|
+
}
|
|
43997
|
+
|
|
43998
|
+
// ../../packages/sdk-ts/src/streaming-submission-handle.ts
|
|
43999
|
+
function normalizedOptional(value) {
|
|
44000
|
+
return value === undefined || value === "" ? undefined : value;
|
|
44001
|
+
}
|
|
44002
|
+
function supportsStreamingRequests() {
|
|
44003
|
+
const runtime = globalThis;
|
|
44004
|
+
if (runtime.Bun !== undefined)
|
|
44005
|
+
return true;
|
|
44006
|
+
const node = runtime.process?.versions?.node;
|
|
44007
|
+
if (!node)
|
|
44008
|
+
return false;
|
|
44009
|
+
const [major = 0, minor = 0] = node.split(".").map(Number);
|
|
44010
|
+
return major > 22 || major === 22 && minor >= 2;
|
|
44011
|
+
}
|
|
44012
|
+
function submissionPath(identity2) {
|
|
44013
|
+
const query = new URLSearchParams({
|
|
44014
|
+
submissionId: identity2.submissionId,
|
|
44015
|
+
groupSize: String(identity2.groupSize)
|
|
44016
|
+
});
|
|
44017
|
+
if (identity2.message !== undefined)
|
|
44018
|
+
query.set("message", identity2.message);
|
|
44019
|
+
if (identity2.committer !== undefined)
|
|
44020
|
+
query.set("committer", identity2.committer);
|
|
44021
|
+
return `/api/repos/${encodeURIComponent(identity2.orgName)}/${encodeURIComponent(identity2.repoName)}/streams/${encodeURIComponent(identity2.streamId)}/submissions?${query}`;
|
|
44022
|
+
}
|
|
44023
|
+
function createRequestBody(source) {
|
|
44024
|
+
const iterator = createNormalizedOperationIterator(source, false);
|
|
44025
|
+
let closed = false;
|
|
44026
|
+
const returnSourceOnce = () => {
|
|
44027
|
+
if (closed)
|
|
44028
|
+
return;
|
|
44029
|
+
closed = true;
|
|
44030
|
+
const returned = iterator.returnSourceOnce();
|
|
44031
|
+
if (returned)
|
|
44032
|
+
Promise.resolve(returned).catch(() => {
|
|
44033
|
+
return;
|
|
44034
|
+
});
|
|
44035
|
+
};
|
|
44036
|
+
const stream = new ReadableStream({
|
|
44037
|
+
async pull(controller) {
|
|
44038
|
+
try {
|
|
44039
|
+
const next = await iterator.next();
|
|
44040
|
+
if (closed)
|
|
44041
|
+
return;
|
|
44042
|
+
if (next.done) {
|
|
44043
|
+
controller.close();
|
|
44044
|
+
return;
|
|
44045
|
+
}
|
|
44046
|
+
controller.enqueue(encodeStreamOperation(next.value));
|
|
44047
|
+
} catch (cause) {
|
|
44048
|
+
controller.error(cause);
|
|
44049
|
+
returnSourceOnce();
|
|
44050
|
+
}
|
|
44051
|
+
},
|
|
44052
|
+
cancel() {
|
|
44053
|
+
returnSourceOnce();
|
|
44054
|
+
}
|
|
44055
|
+
});
|
|
44056
|
+
return { stream, returnSourceOnce };
|
|
44057
|
+
}
|
|
44058
|
+
function linkSignal(input, controller) {
|
|
44059
|
+
if (!input)
|
|
44060
|
+
return () => {
|
|
44061
|
+
return;
|
|
44062
|
+
};
|
|
44063
|
+
const abort = () => controller.abort(input.reason);
|
|
44064
|
+
if (input.aborted)
|
|
44065
|
+
abort();
|
|
44066
|
+
else
|
|
44067
|
+
input.addEventListener("abort", abort, { once: true });
|
|
44068
|
+
return () => input.removeEventListener("abort", abort);
|
|
44069
|
+
}
|
|
44070
|
+
function createIdentity(input) {
|
|
44071
|
+
const submissionId = input.options?.submissionId ?? createOperationEventSubmissionId();
|
|
44072
|
+
return {
|
|
44073
|
+
orgName: input.orgName,
|
|
44074
|
+
repoName: input.repoName,
|
|
44075
|
+
streamId: input.options.streamId,
|
|
44076
|
+
submissionId,
|
|
44077
|
+
groupSize: normalizeStreamChunkSize(input.options.groupSize),
|
|
44078
|
+
...normalizedOptional(input.message) !== undefined ? { message: normalizedOptional(input.message) } : {},
|
|
44079
|
+
...normalizedOptional(input.options?.committer) !== undefined ? { committer: normalizedOptional(input.options?.committer) } : {}
|
|
44080
|
+
};
|
|
44081
|
+
}
|
|
44082
|
+
function createStreamingSubmissionHandle(input, deps) {
|
|
44083
|
+
if (!input.options.streamId) {
|
|
44084
|
+
throw deps.localError("VALIDATION_ERROR", "Streaming submissions require a non-empty streamId.");
|
|
44085
|
+
}
|
|
44086
|
+
const retryIdentity = createIdentity(input);
|
|
44087
|
+
const controller = new AbortController;
|
|
44088
|
+
let unlink = () => {
|
|
44089
|
+
return;
|
|
44090
|
+
};
|
|
44091
|
+
let iterator;
|
|
44092
|
+
let stopped = false;
|
|
44093
|
+
const consume = async function* () {
|
|
44094
|
+
let dispatched = false;
|
|
44095
|
+
let definiteError = false;
|
|
44096
|
+
let body;
|
|
44097
|
+
try {
|
|
44098
|
+
if (!(deps.runtimeSupported ?? supportsStreamingRequests)()) {
|
|
44099
|
+
throw deps.localError("VALIDATION_ERROR", "Streaming submissions require Bun or Node.js 22.2 or newer.");
|
|
44100
|
+
}
|
|
44101
|
+
unlink = linkSignal(input.options.signal, controller);
|
|
44102
|
+
if (controller.signal.aborted) {
|
|
44103
|
+
throw deps.localError("CANCELLED", "Streaming submission was aborted.");
|
|
44104
|
+
}
|
|
44105
|
+
const prepared = await deps.prepare(submissionPath(retryIdentity));
|
|
44106
|
+
if (controller.signal.aborted) {
|
|
44107
|
+
throw deps.localError("CANCELLED", "Streaming submission was aborted.");
|
|
44108
|
+
}
|
|
44109
|
+
body = createRequestBody(input.operations);
|
|
44110
|
+
dispatched = true;
|
|
44111
|
+
const response = await prepared.dispatch(body.stream, controller.signal);
|
|
44112
|
+
if (!response.ok) {
|
|
44113
|
+
definiteError = true;
|
|
44114
|
+
throw await prepared.httpError(response);
|
|
44115
|
+
}
|
|
44116
|
+
for await (const row of readStreamingSubmissionRows(response, retryIdentity)) {
|
|
44117
|
+
yield row;
|
|
44118
|
+
}
|
|
44119
|
+
} catch (cause) {
|
|
44120
|
+
if (cause instanceof StreamingSubmissionOutcomeUnknownError)
|
|
44121
|
+
throw cause;
|
|
44122
|
+
if (dispatched && !definiteError) {
|
|
44123
|
+
throw new StreamingSubmissionOutcomeUnknownError(retryIdentity, cause);
|
|
44124
|
+
}
|
|
44125
|
+
throw deps.normalizeLocalError(cause);
|
|
44126
|
+
} finally {
|
|
44127
|
+
controller.abort();
|
|
44128
|
+
body?.returnSourceOnce();
|
|
44129
|
+
unlink();
|
|
44130
|
+
stopped = true;
|
|
44131
|
+
}
|
|
44132
|
+
};
|
|
44133
|
+
return {
|
|
44134
|
+
retryIdentity,
|
|
44135
|
+
[Symbol.asyncIterator]() {
|
|
44136
|
+
return this;
|
|
44137
|
+
},
|
|
44138
|
+
next() {
|
|
44139
|
+
if (stopped)
|
|
44140
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
44141
|
+
iterator ??= consume();
|
|
44142
|
+
return iterator.next();
|
|
44143
|
+
},
|
|
44144
|
+
async return(value) {
|
|
44145
|
+
controller.abort();
|
|
44146
|
+
if (!iterator) {
|
|
44147
|
+
stopped = true;
|
|
44148
|
+
unlink();
|
|
44149
|
+
return { done: true, value };
|
|
44150
|
+
}
|
|
44151
|
+
return iterator.return(value);
|
|
44152
|
+
},
|
|
44153
|
+
async throw(error51) {
|
|
44154
|
+
controller.abort(error51);
|
|
44155
|
+
if (!iterator) {
|
|
44156
|
+
stopped = true;
|
|
44157
|
+
unlink();
|
|
44158
|
+
throw error51;
|
|
44159
|
+
}
|
|
44160
|
+
return iterator.throw(error51);
|
|
44161
|
+
}
|
|
44162
|
+
};
|
|
44163
|
+
}
|
|
43541
44164
|
// ../../packages/sdk-ts/package.json
|
|
43542
44165
|
var package_default = {
|
|
43543
44166
|
name: "@warmhub/sdk-ts",
|
|
43544
|
-
version: "0.
|
|
44167
|
+
version: "0.96.0",
|
|
43545
44168
|
private: false,
|
|
43546
44169
|
type: "module",
|
|
43547
44170
|
description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
|
|
@@ -43563,6 +44186,11 @@ var package_default = {
|
|
|
43563
44186
|
url: "https://warmhub.ai"
|
|
43564
44187
|
},
|
|
43565
44188
|
homepage: "https://docs.warmhub.ai/get-started/quickstart/#connect-via-sdk",
|
|
44189
|
+
repository: {
|
|
44190
|
+
type: "git",
|
|
44191
|
+
url: "https://github.com/warmhub/warmhub-app.git",
|
|
44192
|
+
directory: "packages/sdk-ts"
|
|
44193
|
+
},
|
|
43566
44194
|
bugs: {
|
|
43567
44195
|
email: "support@warmhub.ai"
|
|
43568
44196
|
},
|
|
@@ -43578,7 +44206,8 @@ var package_default = {
|
|
|
43578
44206
|
],
|
|
43579
44207
|
publishConfig: {
|
|
43580
44208
|
registry: "https://registry.npmjs.org",
|
|
43581
|
-
access: "public"
|
|
44209
|
+
access: "public",
|
|
44210
|
+
tag: "latest"
|
|
43582
44211
|
},
|
|
43583
44212
|
exports: {
|
|
43584
44213
|
".": {
|
|
@@ -43596,7 +44225,7 @@ var package_default = {
|
|
|
43596
44225
|
},
|
|
43597
44226
|
scripts: {
|
|
43598
44227
|
build: 'if [ "${WARMHUB_TEST_RUNTIME_BUILD:-}" = "1" ]; then bun run build:test-runtime; else bun run build:full; fi',
|
|
43599
|
-
"build:full": "
|
|
44228
|
+
"build:full": "bun run build:runtime && bun run build:types && bun run scripts/inject-doc-links.ts",
|
|
43600
44229
|
"build:runtime": "tsup",
|
|
43601
44230
|
"build:types": "NODE_OPTIONS=--max-old-space-size=4096 tsup --config tsup.dts-core.config.ts && NODE_OPTIONS=--max-old-space-size=4096 tsup --config tsup.dts-checkpoint.config.ts",
|
|
43602
44231
|
"build:test-runtime": "tsup --config tsup.test-runtime.config.ts",
|
|
@@ -44425,6 +45054,11 @@ class WarmHubClient {
|
|
|
44425
45054
|
throw toWarmHubError(error51);
|
|
44426
45055
|
}
|
|
44427
45056
|
},
|
|
45057
|
+
applyStreaming: (orgName, repoName, message, operations, opts) => createStreamingSubmissionHandle({ orgName, repoName, message, operations, options: opts }, {
|
|
45058
|
+
prepare: async (path) => await this.prepareStreamingSubmissionRequest(path),
|
|
45059
|
+
localError: (code, errorMessage) => new WarmHubError(code, errorMessage),
|
|
45060
|
+
normalizeLocalError: toWarmHubError
|
|
45061
|
+
}),
|
|
44428
45062
|
getReceipt: async (orgName, repoName, eventRequestId) => {
|
|
44429
45063
|
try {
|
|
44430
45064
|
return await this.trpc.commit.getReceipt.query({
|
|
@@ -46180,32 +46814,54 @@ class WarmHubClient {
|
|
|
46180
46814
|
}
|
|
46181
46815
|
async requestResponse(path, init) {
|
|
46182
46816
|
const response = await this.fetchWithAuth(`${this.apiUrl.replace(/\/$/, "")}${path}`, init);
|
|
46183
|
-
if (!response.ok)
|
|
46184
|
-
|
|
46185
|
-
let code = httpStatusToWarmHubCode(response.status);
|
|
46186
|
-
let errorCode;
|
|
46187
|
-
let hint;
|
|
46188
|
-
let retryAfter;
|
|
46189
|
-
try {
|
|
46190
|
-
const body = await response.json();
|
|
46191
|
-
if (typeof body.error?.message === "string") {
|
|
46192
|
-
message = body.error.message;
|
|
46193
|
-
}
|
|
46194
|
-
if (typeof body.error?.code === "string") {
|
|
46195
|
-
code = body.error.code;
|
|
46196
|
-
errorCode = body.error.code;
|
|
46197
|
-
}
|
|
46198
|
-
if (typeof body.error?.hint === "string") {
|
|
46199
|
-
hint = body.error.hint;
|
|
46200
|
-
}
|
|
46201
|
-
if (typeof body.error?.retryAfter === "number") {
|
|
46202
|
-
retryAfter = body.error.retryAfter;
|
|
46203
|
-
}
|
|
46204
|
-
} catch {}
|
|
46205
|
-
throw new WarmHubError(code, message, response.status, hint, retryAfter, errorCode);
|
|
46206
|
-
}
|
|
46817
|
+
if (!response.ok)
|
|
46818
|
+
throw await this.httpResponseError(response);
|
|
46207
46819
|
return response;
|
|
46208
46820
|
}
|
|
46821
|
+
async prepareStreamingSubmissionRequest(path) {
|
|
46822
|
+
const fetchImpl = this.fetchImpl ?? globalThis.fetch;
|
|
46823
|
+
const headers = new Headers({
|
|
46824
|
+
accept: "application/x-ndjson",
|
|
46825
|
+
"content-type": "application/x-ndjson"
|
|
46826
|
+
});
|
|
46827
|
+
const token = await resolveAccessToken(this.accessToken);
|
|
46828
|
+
if (token)
|
|
46829
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
46830
|
+
this.applyClientHeader(headers);
|
|
46831
|
+
const url2 = `${this.apiUrl.replace(/\/$/, "")}${path}`;
|
|
46832
|
+
return {
|
|
46833
|
+
dispatch: async (body, signal) => await fetchImpl(url2, {
|
|
46834
|
+
method: "POST",
|
|
46835
|
+
headers,
|
|
46836
|
+
body,
|
|
46837
|
+
signal,
|
|
46838
|
+
duplex: "half"
|
|
46839
|
+
}),
|
|
46840
|
+
httpError: async (response) => await this.httpResponseError(response)
|
|
46841
|
+
};
|
|
46842
|
+
}
|
|
46843
|
+
async httpResponseError(response) {
|
|
46844
|
+
let message = `Request failed with status ${response.status}`;
|
|
46845
|
+
let code = httpStatusToWarmHubCode(response.status);
|
|
46846
|
+
let errorCode;
|
|
46847
|
+
let hint;
|
|
46848
|
+
let retryAfter;
|
|
46849
|
+
try {
|
|
46850
|
+
const body = await response.json();
|
|
46851
|
+
if (typeof body.error?.message === "string")
|
|
46852
|
+
message = body.error.message;
|
|
46853
|
+
if (typeof body.error?.code === "string") {
|
|
46854
|
+
code = body.error.code;
|
|
46855
|
+
errorCode = body.error.code;
|
|
46856
|
+
}
|
|
46857
|
+
if (typeof body.error?.hint === "string")
|
|
46858
|
+
hint = body.error.hint;
|
|
46859
|
+
if (typeof body.error?.retryAfter === "number") {
|
|
46860
|
+
retryAfter = body.error.retryAfter;
|
|
46861
|
+
}
|
|
46862
|
+
} catch {}
|
|
46863
|
+
return new WarmHubError(code, message, response.status, hint, retryAfter, errorCode);
|
|
46864
|
+
}
|
|
46209
46865
|
async requestJson(path, init) {
|
|
46210
46866
|
const response = await this.requestResponse(path, init);
|
|
46211
46867
|
return await response.json();
|
|
@@ -46750,12 +47406,22 @@ function printCliError(err, errWriter, opts = {}) {
|
|
|
46750
47406
|
function fromWh(exit, kind, err, hint = err.hint) {
|
|
46751
47407
|
return new CliError(exit, kind, err.message, err, hint, undefined, err.errorCode);
|
|
46752
47408
|
}
|
|
46753
|
-
function
|
|
47409
|
+
function classifyAuthError(input) {
|
|
46754
47410
|
if (input.code !== "UNAUTHENTICATED" && input.code !== "FORBIDDEN") {
|
|
46755
47411
|
return;
|
|
46756
47412
|
}
|
|
46757
47413
|
const fallbackHint = input.code === "FORBIDDEN" ? "Check credentials and backend permissions." : unauthenticatedHint(input.message);
|
|
46758
|
-
return
|
|
47414
|
+
return {
|
|
47415
|
+
exit: 5 /* Auth */,
|
|
47416
|
+
kind: "AUTH",
|
|
47417
|
+
hint: input.hint ?? fallbackHint
|
|
47418
|
+
};
|
|
47419
|
+
}
|
|
47420
|
+
function authCliError(input) {
|
|
47421
|
+
const classified = classifyAuthError(input);
|
|
47422
|
+
if (!classified)
|
|
47423
|
+
return;
|
|
47424
|
+
return new CliError(classified.exit, classified.kind, input.message, input.cause, classified.hint, undefined, input.backendCode);
|
|
46759
47425
|
}
|
|
46760
47426
|
function usageError(usage, ...examples) {
|
|
46761
47427
|
const hint = examples.length === 0 ? undefined : examples.length === 1 ? `Example: ${examples[0]}` : `Examples:
|
|
@@ -46857,61 +47523,62 @@ var OP_USER_INPUT_CODES = new Set([
|
|
|
46857
47523
|
"BUILTIN_SHAPE",
|
|
46858
47524
|
"DEPENDENCY_FAILED"
|
|
46859
47525
|
]);
|
|
46860
|
-
function
|
|
47526
|
+
function classifyOpFailure(failure) {
|
|
46861
47527
|
const diagnostic = failure.errors?.[0] ?? failure.error;
|
|
46862
47528
|
const code = diagnostic?.code ?? "BACKEND";
|
|
46863
47529
|
const message = diagnostic?.message ?? `Operation on "${failure.name}" failed`;
|
|
46864
47530
|
const errorCode = diagnostic?.code;
|
|
46865
|
-
const
|
|
46866
|
-
const authError =
|
|
46867
|
-
code,
|
|
46868
|
-
message,
|
|
46869
|
-
backendCode: errorCode
|
|
46870
|
-
});
|
|
47531
|
+
const classified = (exit, kind, hint) => ({ exit, kind, message, hint, errorCode });
|
|
47532
|
+
const authError = classifyAuthError({ code, message });
|
|
46871
47533
|
if (authError)
|
|
46872
|
-
return authError;
|
|
47534
|
+
return classified(authError.exit, authError.kind, authError.hint);
|
|
46873
47535
|
if (CONFLICT_SHAPED_CODES.has(code)) {
|
|
46874
|
-
return
|
|
47536
|
+
return classified(2 /* UserInput */, "CONFLICT", conflictHint(message, code));
|
|
46875
47537
|
}
|
|
46876
47538
|
if (OP_USER_INPUT_CODES.has(code)) {
|
|
46877
|
-
return
|
|
47539
|
+
return classified(2 /* UserInput */, "USER_INPUT");
|
|
46878
47540
|
}
|
|
46879
47541
|
if (isFieldIndexErrorCode(code)) {
|
|
46880
47542
|
const exit = code === "FIELD_NOT_INDEXABLE" ? 2 /* UserInput */ : 4 /* Backend */;
|
|
46881
|
-
return
|
|
47543
|
+
return classified(exit, code);
|
|
46882
47544
|
}
|
|
46883
47545
|
if (code === "RATE_LIMITED") {
|
|
46884
|
-
return
|
|
47546
|
+
return classified(4 /* Backend */, "RATE_LIMITED", "Rate limited. Wait for the Retry-After window before retrying.");
|
|
46885
47547
|
}
|
|
46886
47548
|
if (code === "QUERY_TOO_EXPENSIVE") {
|
|
46887
|
-
return
|
|
47549
|
+
return classified(2 /* UserInput */, "QUERY_TOO_EXPENSIVE", "Narrow the query with a more selective field predicate or retry with a shallower page.");
|
|
47550
|
+
}
|
|
47551
|
+
return classified(4 /* Backend */, "BACKEND");
|
|
47552
|
+
}
|
|
47553
|
+
function cliErrorFromOpFailure(failure) {
|
|
47554
|
+
const classified = classifyOpFailure(failure);
|
|
47555
|
+
return new CliError(classified.exit, classified.kind, classified.message, undefined, classified.hint, undefined, classified.errorCode);
|
|
47556
|
+
}
|
|
47557
|
+
function opFailurePriority(failure) {
|
|
47558
|
+
const classified = classifyOpFailure(failure);
|
|
47559
|
+
if (classified.exit === 5 /* Auth */)
|
|
47560
|
+
return 4;
|
|
47561
|
+
if (classified.exit === 2 /* UserInput */)
|
|
47562
|
+
return 3;
|
|
47563
|
+
if (classified.kind === "RATE_LIMITED")
|
|
47564
|
+
return 2;
|
|
47565
|
+
return 1;
|
|
47566
|
+
}
|
|
47567
|
+
function preferMoreSevereOpFailure(current, candidate) {
|
|
47568
|
+
if (!current || opFailurePriority(candidate) > opFailurePriority(current)) {
|
|
47569
|
+
return candidate;
|
|
46888
47570
|
}
|
|
46889
|
-
return
|
|
47571
|
+
return current;
|
|
46890
47572
|
}
|
|
46891
47573
|
function cliErrorFromAllFailed(failures) {
|
|
46892
|
-
|
|
46893
|
-
if (err.code === 5 /* Auth */)
|
|
46894
|
-
return 4;
|
|
46895
|
-
if (err.code === 2 /* UserInput */)
|
|
46896
|
-
return 3;
|
|
46897
|
-
if (err.kind === "RATE_LIMITED")
|
|
46898
|
-
return 2;
|
|
46899
|
-
return 1;
|
|
46900
|
-
};
|
|
46901
|
-
let bestErr;
|
|
46902
|
-
let bestPriority = -1;
|
|
47574
|
+
let bestFailure;
|
|
46903
47575
|
for (const failure of failures) {
|
|
46904
47576
|
if (!isFailedOpStatus(failure.status))
|
|
46905
47577
|
continue;
|
|
46906
|
-
|
|
46907
|
-
const priority = priorityOf(err);
|
|
46908
|
-
if (priority > bestPriority) {
|
|
46909
|
-
bestErr = err;
|
|
46910
|
-
bestPriority = priority;
|
|
46911
|
-
}
|
|
47578
|
+
bestFailure = preferMoreSevereOpFailure(bestFailure, failure);
|
|
46912
47579
|
}
|
|
46913
|
-
if (
|
|
46914
|
-
return
|
|
47580
|
+
if (bestFailure)
|
|
47581
|
+
return cliErrorFromOpFailure(bestFailure);
|
|
46915
47582
|
return new CliError(4 /* Backend */, "BACKEND", `All ${failures.length} operations failed`);
|
|
46916
47583
|
}
|
|
46917
47584
|
function requireSingleOpSuccess(result) {
|
|
@@ -55102,6 +55769,12 @@ var createFlags3 = {
|
|
|
55102
55769
|
"chunk-size": flag.number({
|
|
55103
55770
|
description: `Streamed ops per append chunk for --stream or .jsonl --file (default: ${DEFAULT_STREAM_APPEND_CHUNK_SIZE}, max: ${MAX_STREAM_APPEND_OPERATION_COUNT})`
|
|
55104
55771
|
}),
|
|
55772
|
+
"server-stream": flag.boolean({
|
|
55773
|
+
description: "use the full-duplex server submission transport and emit results as groups commit"
|
|
55774
|
+
}),
|
|
55775
|
+
"group-size": flag.number({
|
|
55776
|
+
description: `operations per server-committed group with --server-stream (default: ${DEFAULT_STREAM_APPEND_CHUNK_SIZE}, max: ${MAX_STREAM_APPEND_OPERATION_COUNT})`
|
|
55777
|
+
}),
|
|
55105
55778
|
"allow-nul-bytes": flag.boolean({
|
|
55106
55779
|
description: "Skip the client-side pre-check that rejects literal U+0000 bytes in op data. PostgreSQL `text` columns still cannot store these — use only when the backend storage type is known to tolerate them."
|
|
55107
55780
|
}),
|
|
@@ -55166,6 +55839,21 @@ var createFlags3 = {
|
|
|
55166
55839
|
})
|
|
55167
55840
|
};
|
|
55168
55841
|
|
|
55842
|
+
// ../../packages/warmhub-cli/src/domains/commit-stream-output-contract-id.ts
|
|
55843
|
+
var COMMIT_SUBMIT_STREAM_ROW_SCHEMA_ID = "wh.commit.submit.stream.row/v0.1";
|
|
55844
|
+
function identify(row) {
|
|
55845
|
+
if ("schema" in row) {
|
|
55846
|
+
throw new Error("Streaming submission row already defines a schema field");
|
|
55847
|
+
}
|
|
55848
|
+
return { schema: COMMIT_SUBMIT_STREAM_ROW_SCHEMA_ID, ...row };
|
|
55849
|
+
}
|
|
55850
|
+
function identifyCommitSubmitStreamStart(retryIdentity) {
|
|
55851
|
+
return identify({ type: "start", retryIdentity });
|
|
55852
|
+
}
|
|
55853
|
+
function identifyCommitSubmitStreamRow(row) {
|
|
55854
|
+
return identify(row);
|
|
55855
|
+
}
|
|
55856
|
+
|
|
55169
55857
|
// ../../packages/warmhub-cli/src/domains/commit-submit-template.ts
|
|
55170
55858
|
import { writeFile } from "node:fs/promises";
|
|
55171
55859
|
var WRITE_TEMPLATE_KINDS = ["thing", "assertion"];
|
|
@@ -56508,6 +57196,284 @@ var handleSubmit = async (ctx, { flags, args }) => {
|
|
|
56508
57196
|
}
|
|
56509
57197
|
};
|
|
56510
57198
|
|
|
57199
|
+
// ../../packages/warmhub-cli/src/domains/commit-submit-server-source.ts
|
|
57200
|
+
import { createReadStream as createReadStream4 } from "node:fs";
|
|
57201
|
+
function parseOperationLine(rawLine, lineNumber, label, allowNulBytes) {
|
|
57202
|
+
const trimmed = rawLine.trim();
|
|
57203
|
+
if (!trimmed)
|
|
57204
|
+
return;
|
|
57205
|
+
const operation = safeParseJson(trimmed, `${label} JSONL line ${lineNumber}`);
|
|
57206
|
+
rejectLegacyLifecycleOperations([operation]);
|
|
57207
|
+
if (!allowNulBytes) {
|
|
57208
|
+
assertNoNulBytes("data" in operation ? operation.data : undefined, `${label} JSONL line ${lineNumber}`);
|
|
57209
|
+
}
|
|
57210
|
+
return operation;
|
|
57211
|
+
}
|
|
57212
|
+
async function* serverJsonlOperationSource(input, label, allowNulBytes) {
|
|
57213
|
+
const decoder = new TextDecoder;
|
|
57214
|
+
const encoder = new TextEncoder;
|
|
57215
|
+
let pending = new Uint8Array(0);
|
|
57216
|
+
let pendingLength = 0;
|
|
57217
|
+
let lineNumber = 0;
|
|
57218
|
+
const lineLimitError = (atLine = lineNumber + 1) => new CliError(2 /* UserInput */, "USER_INPUT", `${label} JSONL line ${atLine} exceeds the 1 MiB line limit.`, undefined, "wh commit submit --file operations.jsonl --server-stream --stream-id bulk-import --format jsonl --repo acme/world");
|
|
57219
|
+
const append = (bytes) => {
|
|
57220
|
+
const required2 = pendingLength + bytes.length;
|
|
57221
|
+
if (required2 > MAX_STREAMING_SUBMISSION_LINE_BYTES)
|
|
57222
|
+
throw lineLimitError();
|
|
57223
|
+
if (required2 > pending.length) {
|
|
57224
|
+
let capacity = Math.max(1024, pending.length);
|
|
57225
|
+
while (capacity < required2) {
|
|
57226
|
+
capacity = Math.min(MAX_STREAMING_SUBMISSION_LINE_BYTES, capacity * 2);
|
|
57227
|
+
}
|
|
57228
|
+
const grown = new Uint8Array(capacity);
|
|
57229
|
+
grown.set(pending.subarray(0, pendingLength));
|
|
57230
|
+
pending = grown;
|
|
57231
|
+
}
|
|
57232
|
+
pending.set(bytes, pendingLength);
|
|
57233
|
+
pendingLength = required2;
|
|
57234
|
+
};
|
|
57235
|
+
const decodeLine = (bytes) => {
|
|
57236
|
+
const required2 = pendingLength + bytes.length;
|
|
57237
|
+
if (required2 > MAX_STREAMING_SUBMISSION_LINE_BYTES) {
|
|
57238
|
+
throw lineLimitError(lineNumber);
|
|
57239
|
+
}
|
|
57240
|
+
if (pendingLength === 0)
|
|
57241
|
+
return decoder.decode(bytes);
|
|
57242
|
+
append(bytes);
|
|
57243
|
+
const line = decoder.decode(pending.subarray(0, pendingLength));
|
|
57244
|
+
pendingLength = 0;
|
|
57245
|
+
return line;
|
|
57246
|
+
};
|
|
57247
|
+
for await (const chunk of input) {
|
|
57248
|
+
const bytes = typeof chunk === "string" ? encoder.encode(chunk) : chunk;
|
|
57249
|
+
let lineStart = 0;
|
|
57250
|
+
let newline = bytes.indexOf(10, lineStart);
|
|
57251
|
+
while (newline >= 0) {
|
|
57252
|
+
lineNumber += 1;
|
|
57253
|
+
const operation = parseOperationLine(decodeLine(bytes.subarray(lineStart, newline)), lineNumber, label, allowNulBytes);
|
|
57254
|
+
if (operation)
|
|
57255
|
+
yield operation;
|
|
57256
|
+
lineStart = newline + 1;
|
|
57257
|
+
newline = bytes.indexOf(10, lineStart);
|
|
57258
|
+
}
|
|
57259
|
+
append(bytes.subarray(lineStart));
|
|
57260
|
+
}
|
|
57261
|
+
if (pendingLength > 0) {
|
|
57262
|
+
lineNumber += 1;
|
|
57263
|
+
const operation = parseOperationLine(decoder.decode(pending.subarray(0, pendingLength)), lineNumber, label, allowNulBytes);
|
|
57264
|
+
if (operation)
|
|
57265
|
+
yield operation;
|
|
57266
|
+
}
|
|
57267
|
+
}
|
|
57268
|
+
async function* jsonArrayFileSource(path2, allowNulBytes) {
|
|
57269
|
+
const raw = await readValidationJsonArrayFile(path2);
|
|
57270
|
+
const operations = parseJsonArray(raw, "--file contents");
|
|
57271
|
+
rejectLegacyLifecycleOperations(operations);
|
|
57272
|
+
for (const [index, operation] of operations.entries()) {
|
|
57273
|
+
if (!allowNulBytes) {
|
|
57274
|
+
assertNoNulBytes("data" in operation ? operation.data : undefined, `--file operation ${index + 1}`);
|
|
57275
|
+
}
|
|
57276
|
+
yield operation;
|
|
57277
|
+
}
|
|
57278
|
+
}
|
|
57279
|
+
function createServerStreamingOperationSource(args) {
|
|
57280
|
+
if (args.source === "--stream") {
|
|
57281
|
+
const input = args.ctx.stdin ?? process.stdin;
|
|
57282
|
+
if (isTTY(input)) {
|
|
57283
|
+
throw new CliError(2 /* UserInput */, "USER_INPUT", "No JSONL operations provided on stdin.", undefined, `printf '%s\\n' '{"operation":"add","kind":"thing","name":"Example/item","data":{}}' | wh commit submit --stream --server-stream --stream-id example --repo acme/world`);
|
|
57284
|
+
}
|
|
57285
|
+
return serverJsonlOperationSource(input, "--stream", args.allowNulBytes);
|
|
57286
|
+
}
|
|
57287
|
+
const path2 = args.file;
|
|
57288
|
+
if (!path2.endsWith(".jsonl")) {
|
|
57289
|
+
return jsonArrayFileSource(path2, args.allowNulBytes);
|
|
57290
|
+
}
|
|
57291
|
+
return async function* () {
|
|
57292
|
+
yield* serverJsonlOperationSource(createReadStream4(path2), "--file", args.allowNulBytes);
|
|
57293
|
+
}();
|
|
57294
|
+
}
|
|
57295
|
+
|
|
57296
|
+
// ../../packages/warmhub-cli/src/domains/commit-submit-server-stream.ts
|
|
57297
|
+
function userInput(message) {
|
|
57298
|
+
throw new CliError(2 /* UserInput */, "USER_INPUT", message, undefined, "wh commit submit --file operations.jsonl --server-stream --stream-id bulk-import --format jsonl --repo acme/world");
|
|
57299
|
+
}
|
|
57300
|
+
function parseGroupSize(value) {
|
|
57301
|
+
if (value === undefined)
|
|
57302
|
+
return;
|
|
57303
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
57304
|
+
userInput("--group-size must be a positive integer.");
|
|
57305
|
+
}
|
|
57306
|
+
if (value > MAX_STREAM_APPEND_OPERATION_COUNT) {
|
|
57307
|
+
userInput(`--group-size must be at most ${MAX_STREAM_APPEND_OPERATION_COUNT}.`);
|
|
57308
|
+
}
|
|
57309
|
+
return value;
|
|
57310
|
+
}
|
|
57311
|
+
function resolveSource(flags) {
|
|
57312
|
+
return resolveCommitOperationSource({
|
|
57313
|
+
stream: flags.stream === true,
|
|
57314
|
+
ops: flags.ops !== undefined,
|
|
57315
|
+
file: flags.file !== undefined,
|
|
57316
|
+
add: (flags.add?.length ?? 0) > 0,
|
|
57317
|
+
revise: flags.revise !== undefined,
|
|
57318
|
+
retract: (flags.retract?.length ?? 0) > 0,
|
|
57319
|
+
collection: flags.type !== undefined
|
|
57320
|
+
});
|
|
57321
|
+
}
|
|
57322
|
+
function assertServerStreamOptions(ctx, flags, source) {
|
|
57323
|
+
if (ctx.dryRun)
|
|
57324
|
+
userInput("--server-stream cannot be combined with --dry-run.");
|
|
57325
|
+
if (ctx.format === "json") {
|
|
57326
|
+
userInput("--server-stream requires pretty output or --format jsonl; --json would buffer the response.");
|
|
57327
|
+
}
|
|
57328
|
+
if (source !== "--stream" && source !== "--file") {
|
|
57329
|
+
userInput("--server-stream requires --stream or --file.");
|
|
57330
|
+
}
|
|
57331
|
+
if (flags["submission-id"] !== undefined && !/^(?:00000000-0000-0000-0000-000000000000|[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/i.test(flags["submission-id"])) {
|
|
57332
|
+
userInput("--submission-id must be a UUID.");
|
|
57333
|
+
}
|
|
57334
|
+
const incompatible = [
|
|
57335
|
+
["--chunk-size", flags["chunk-size"] !== undefined],
|
|
57336
|
+
["--skip-existing", flags["skip-existing"] === true],
|
|
57337
|
+
["--progress", flags.progress === true],
|
|
57338
|
+
["--return-repo-seq", flags["return-repo-seq"] === true],
|
|
57339
|
+
["--timing-out", flags["timing-out"] !== undefined],
|
|
57340
|
+
["--include-would-be-body", flags["include-would-be-body"] === true],
|
|
57341
|
+
["--expected-version", flags["expected-version"] !== undefined],
|
|
57342
|
+
["--lease-id", flags["lease-id"] !== undefined]
|
|
57343
|
+
].find(([, present]) => present);
|
|
57344
|
+
if (incompatible) {
|
|
57345
|
+
userInput(`${incompatible[0]} cannot be combined with --server-stream.`);
|
|
57346
|
+
}
|
|
57347
|
+
assertFlagsApplyToCommitSource(source, {
|
|
57348
|
+
data: (flags.data?.length ?? 0) > 0,
|
|
57349
|
+
shape: (flags.shape?.length ?? 0) > 0,
|
|
57350
|
+
about: (flags.about?.length ?? 0) > 0,
|
|
57351
|
+
affirm: (flags.affirm?.length ?? 0) > 0,
|
|
57352
|
+
reason: (flags.reason?.length ?? 0) > 0,
|
|
57353
|
+
kind: (flags.kind?.length ?? 0) > 0,
|
|
57354
|
+
name: flags.name !== undefined,
|
|
57355
|
+
members: flags.members !== undefined
|
|
57356
|
+
});
|
|
57357
|
+
const streamId = flags["stream-id"];
|
|
57358
|
+
if (!streamId)
|
|
57359
|
+
userInput("--server-stream requires --stream-id.");
|
|
57360
|
+
if (source === "--stream")
|
|
57361
|
+
return { source, streamId };
|
|
57362
|
+
const file2 = flags.file;
|
|
57363
|
+
if (!file2)
|
|
57364
|
+
userInput("--server-stream with --file requires a file path.");
|
|
57365
|
+
return { source, file: file2, streamId };
|
|
57366
|
+
}
|
|
57367
|
+
function recoveryGuidance(identity2) {
|
|
57368
|
+
return `Reconstruct the identical operation source and retry only by copying every field from this retry identity: ${JSON.stringify(identity2)}`;
|
|
57369
|
+
}
|
|
57370
|
+
function failClosedStreamingOutput(ctx, identity2) {
|
|
57371
|
+
ctx.requestedExitCode = 4 /* Backend */;
|
|
57372
|
+
ctx.err(`Streaming submission output closed before completion. ${recoveryGuidance(identity2)}`);
|
|
57373
|
+
}
|
|
57374
|
+
async function writeStreamingRow(ctx, row) {
|
|
57375
|
+
writeOutput(ctx, identifyCommitSubmitStreamRow(row), () => renderPrettyStreamingRow(ctx, row));
|
|
57376
|
+
return await ctx.flushOut?.() ?? true;
|
|
57377
|
+
}
|
|
57378
|
+
function renderPrettyStreamingRow(ctx, row) {
|
|
57379
|
+
const c = ctx.colors;
|
|
57380
|
+
if (row.type === "result") {
|
|
57381
|
+
const failed = isFailedOpStatus(row.status);
|
|
57382
|
+
const marker = failed ? "!" : row.status === "noop" ? "=" : "+";
|
|
57383
|
+
const diagnostic = "errors" in row ? row.errors[0] : ("error" in row) ? row.error : undefined;
|
|
57384
|
+
ctx.out(` ${marker} ${displayName(c, row.name)}${failed ? ` ${diagnostic?.message ?? diagnostic?.code ?? "failed"}` : ""}`);
|
|
57385
|
+
renderWarningLine(ctx.out, c, ctx.chars, row);
|
|
57386
|
+
return;
|
|
57387
|
+
}
|
|
57388
|
+
if (row.type === "group") {
|
|
57389
|
+
ctx.out(`${c.dim}group ${row.chunkOrdinal}${c.reset} event request ${row.eventRequestId}`);
|
|
57390
|
+
return;
|
|
57391
|
+
}
|
|
57392
|
+
if (row.type === "summary") {
|
|
57393
|
+
ctx.out(`${c.dim}summary${c.reset} ${row.verdict} (${row.operations.applied} applied, ${row.operations.noop} noop, ${row.operations.failed} failed)`);
|
|
57394
|
+
return;
|
|
57395
|
+
}
|
|
57396
|
+
ctx.out(`${c.red}error${c.reset} ${row.code}${row.message ? `: ${row.message}` : ""}`);
|
|
57397
|
+
}
|
|
57398
|
+
async function runServerStreamingSubmit(ctx, invocation) {
|
|
57399
|
+
const flags = invocation.flags;
|
|
57400
|
+
const sourceKind = resolveSource(flags);
|
|
57401
|
+
const options = assertServerStreamOptions(ctx, flags, sourceKind);
|
|
57402
|
+
const groupSize = parseGroupSize(flags["group-size"]);
|
|
57403
|
+
const { org, repo } = parseOrgRepo(getRepoRef(ctx) ?? invocation.args[0], ctx.config);
|
|
57404
|
+
const operations = options.source === "--stream" ? createServerStreamingOperationSource({
|
|
57405
|
+
source: options.source,
|
|
57406
|
+
ctx,
|
|
57407
|
+
allowNulBytes: flags["allow-nul-bytes"] === true
|
|
57408
|
+
}) : createServerStreamingOperationSource({
|
|
57409
|
+
source: options.source,
|
|
57410
|
+
file: options.file,
|
|
57411
|
+
allowNulBytes: flags["allow-nul-bytes"] === true
|
|
57412
|
+
});
|
|
57413
|
+
const handle = ctx.client.commit.applyStreaming(org, repo, flags.message, operations, {
|
|
57414
|
+
streamId: options.streamId,
|
|
57415
|
+
submissionId: flags["submission-id"],
|
|
57416
|
+
groupSize,
|
|
57417
|
+
committer: flags.committer,
|
|
57418
|
+
signal: ctx.signal
|
|
57419
|
+
});
|
|
57420
|
+
const identity2 = handle.retryIdentity;
|
|
57421
|
+
ctx.err(`streaming submission retry identity ${JSON.stringify(identity2)}`);
|
|
57422
|
+
writeOutput(ctx, identifyCommitSubmitStreamStart(identity2), () => {
|
|
57423
|
+
return;
|
|
57424
|
+
});
|
|
57425
|
+
if (await ctx.flushOut?.() === false) {
|
|
57426
|
+
failClosedStreamingOutput(ctx, identity2);
|
|
57427
|
+
await handle.return?.();
|
|
57428
|
+
return;
|
|
57429
|
+
}
|
|
57430
|
+
let terminal;
|
|
57431
|
+
let bestFailure;
|
|
57432
|
+
try {
|
|
57433
|
+
for await (const row of handle) {
|
|
57434
|
+
if (row.type === "result") {
|
|
57435
|
+
if (isFailedOpStatus(row.status)) {
|
|
57436
|
+
bestFailure = preferMoreSevereOpFailure(bestFailure, row);
|
|
57437
|
+
}
|
|
57438
|
+
} else if (row.type === "summary" || row.type === "error") {
|
|
57439
|
+
terminal = row;
|
|
57440
|
+
}
|
|
57441
|
+
if (!await writeStreamingRow(ctx, row)) {
|
|
57442
|
+
failClosedStreamingOutput(ctx, identity2);
|
|
57443
|
+
return;
|
|
57444
|
+
}
|
|
57445
|
+
}
|
|
57446
|
+
} catch (error51) {
|
|
57447
|
+
if (error51 instanceof StreamingSubmissionOutcomeUnknownError) {
|
|
57448
|
+
throw new CliError(4 /* Backend */, "BACKEND", error51.message, error51, recoveryGuidance(error51.retryIdentity));
|
|
57449
|
+
}
|
|
57450
|
+
throw error51;
|
|
57451
|
+
}
|
|
57452
|
+
if (!terminal) {
|
|
57453
|
+
throw new CliError(4 /* Backend */, "BACKEND", "Streaming submission ended without a terminal row; its outcome is unknown.", undefined, recoveryGuidance(identity2));
|
|
57454
|
+
}
|
|
57455
|
+
if (terminal.type === "error" || terminal.verdict !== "complete") {
|
|
57456
|
+
ctx.requestedExitCode = 4 /* Backend */;
|
|
57457
|
+
ctx.err(`${terminal.type === "error" ? terminal.message ?? terminal.code : `submission stopped with verdict ${terminal.verdict}`}. ${recoveryGuidance(identity2)}`);
|
|
57458
|
+
return;
|
|
57459
|
+
}
|
|
57460
|
+
if (terminal.operations.total > 0 && terminal.operations.applied === 0 && terminal.operations.noop === 0) {
|
|
57461
|
+
const error51 = (bestFailure && cliErrorFromOpFailure(bestFailure)) ?? new CliError(4 /* Backend */, "BACKEND", "All streaming submission operations failed.");
|
|
57462
|
+
ctx.requestedExitCode = error51.code;
|
|
57463
|
+
ctx.err(`${error51.message}. ${recoveryGuidance(identity2)}`);
|
|
57464
|
+
}
|
|
57465
|
+
}
|
|
57466
|
+
var handleSubmitWithServerStreaming = async (ctx, invocation) => {
|
|
57467
|
+
if (invocation.flags["server-stream"] === true) {
|
|
57468
|
+
await runServerStreamingSubmit(ctx, invocation);
|
|
57469
|
+
return;
|
|
57470
|
+
}
|
|
57471
|
+
if (invocation.flags["group-size"] !== undefined) {
|
|
57472
|
+
userInput("--group-size requires --server-stream.");
|
|
57473
|
+
}
|
|
57474
|
+
await handleSubmit(ctx, invocation);
|
|
57475
|
+
};
|
|
57476
|
+
|
|
56511
57477
|
// ../../packages/warmhub-cli/src/domains/commit-submit-domain.ts
|
|
56512
57478
|
var COMMIT_DOMAIN = defineDomain({
|
|
56513
57479
|
name: "commit",
|
|
@@ -56538,6 +57504,7 @@ var COMMIT_DOMAIN = defineDomain({
|
|
|
56538
57504
|
`wh commit submit --ops '[{"operation":"add","kind":"thing","name":"Session/run-001","data":{}},{"operation":"add","kind":"assertion","name":"HypothesisCandidate/run-001-claim","about":"Session/run-001","data":{}}]' -m "Create session + assertion"`,
|
|
56539
57505
|
`printf '%s\\n' '{"operation":"add","kind":"thing","name":"Player/alice","data":{"score":1}}' | wh commit submit --stream --stream-id bulk-2026-06-04 --skip-existing --repo acme/world -m "stdin stream"`,
|
|
56540
57506
|
'wh commit submit --file dataset.jsonl --stream-id bulk-2026-06-04 --skip-existing --progress -m "bulk stream"',
|
|
57507
|
+
'wh commit submit --file dataset.jsonl --server-stream --stream-id bulk-2026-08-14 --group-size 1000 --format jsonl -m "server stream"',
|
|
56541
57508
|
"wh shape template Session HypothesisCandidate -o ops.json",
|
|
56542
57509
|
"wh commit submit --type arc --name route --members Location/a,Location/b",
|
|
56543
57510
|
"wh commit submit --type bond --name peers --members Person/alice,Person/bob",
|
|
@@ -56546,9 +57513,10 @@ var COMMIT_DOMAIN = defineDomain({
|
|
|
56546
57513
|
notes: [
|
|
56547
57514
|
"`--dry-run` evaluates the complete bounded input with the real server commit evaluator and makes no durable repository change.",
|
|
56548
57515
|
"Need to build an ops file? Run `wh shape template <Shape>` to scaffold the JSON from a shape definition, edit the FILL_IN placeholders, then pass it to `--file`.",
|
|
56549
|
-
"Inspect a shape's fields first with `wh thing view <Shape>` before authoring or editing an ops file."
|
|
57516
|
+
"Inspect a shape's fields first with `wh thing view <Shape>` before authoring or editing an ops file.",
|
|
57517
|
+
"`--server-stream` is an opt-in full-duplex transport. It preserves the default aggregate path and requires `--stream-id`; use `--format jsonl` for the versioned row stream."
|
|
56550
57518
|
],
|
|
56551
|
-
handler:
|
|
57519
|
+
handler: handleSubmitWithServerStreaming
|
|
56552
57520
|
}
|
|
56553
57521
|
}
|
|
56554
57522
|
});
|
|
@@ -56640,7 +57608,7 @@ for (const spec of PRE_TERMINATOR_CONTROLS) {
|
|
|
56640
57608
|
if (spec.short)
|
|
56641
57609
|
CONTROL_SHORTS.set(spec.short, { spec, constant: false });
|
|
56642
57610
|
}
|
|
56643
|
-
function
|
|
57611
|
+
function userInput2(message, hint) {
|
|
56644
57612
|
return new CliError(2 /* UserInput */, "USER_INPUT", message, undefined, hint);
|
|
56645
57613
|
}
|
|
56646
57614
|
function hasAttachedValue(record2) {
|
|
@@ -56677,13 +57645,13 @@ function acceptedArgList(method) {
|
|
|
56677
57645
|
function unknownArgumentError(method, record2) {
|
|
56678
57646
|
const accepted = acceptedArgList(method);
|
|
56679
57647
|
const suffix = record2.ownership === "post-terminator" ? ` (use \`--\` to pass globals as method args, e.g. \`-- --${record2.name} <value>\`)` : "";
|
|
56680
|
-
return
|
|
57648
|
+
return userInput2(`Unknown argument: --${record2.name}`, `Method '${method.name}' accepts: ${accepted}${suffix}`);
|
|
56681
57649
|
}
|
|
56682
57650
|
function duplicateError(arg) {
|
|
56683
|
-
return
|
|
57651
|
+
return userInput2(`Flag --${arg.name} may only be specified once`, `Remove the duplicate occurrence; every spelling of --${arg.name} counts as the same flag.`);
|
|
56684
57652
|
}
|
|
56685
57653
|
function missingValueError(arg) {
|
|
56686
|
-
return
|
|
57654
|
+
return userInput2(`Flag --${arg.name} requires a value`, arg.type === "string" ? `To pass a value starting with '-', use the attached form: --${arg.name}=<value>` : `Usage: --${arg.name} <${arg.type}>`);
|
|
56687
57655
|
}
|
|
56688
57656
|
function coerceArg(arg, value) {
|
|
56689
57657
|
switch (arg.type) {
|
|
@@ -56691,7 +57659,7 @@ function coerceArg(arg, value) {
|
|
|
56691
57659
|
if (typeof value !== "string") {
|
|
56692
57660
|
return {
|
|
56693
57661
|
ok: false,
|
|
56694
|
-
error:
|
|
57662
|
+
error: userInput2(`--${arg.name} requires a string value`)
|
|
56695
57663
|
};
|
|
56696
57664
|
}
|
|
56697
57665
|
if (arg.pattern) {
|
|
@@ -56701,13 +57669,13 @@ function coerceArg(arg, value) {
|
|
|
56701
57669
|
} catch {
|
|
56702
57670
|
return {
|
|
56703
57671
|
ok: false,
|
|
56704
|
-
error:
|
|
57672
|
+
error: userInput2(`invalid regex pattern for --${arg.name}: /${arg.pattern}/`)
|
|
56705
57673
|
};
|
|
56706
57674
|
}
|
|
56707
57675
|
if (!pattern.test(value)) {
|
|
56708
57676
|
return {
|
|
56709
57677
|
ok: false,
|
|
56710
|
-
error:
|
|
57678
|
+
error: userInput2(`--${arg.name} value '${value}' does not match pattern /${arg.pattern}/`)
|
|
56711
57679
|
};
|
|
56712
57680
|
}
|
|
56713
57681
|
}
|
|
@@ -56718,32 +57686,32 @@ function coerceArg(arg, value) {
|
|
|
56718
57686
|
if (typeof value === "boolean") {
|
|
56719
57687
|
return {
|
|
56720
57688
|
ok: false,
|
|
56721
|
-
error:
|
|
57689
|
+
error: userInput2(`--${arg.name} requires a ${arg.type} value`)
|
|
56722
57690
|
};
|
|
56723
57691
|
}
|
|
56724
57692
|
const numeric = typeof value === "number" ? Number.isFinite(value) ? value : undefined : typeof value === "string" ? parseCliNumber(value) : undefined;
|
|
56725
57693
|
if (numeric === undefined) {
|
|
56726
57694
|
return {
|
|
56727
57695
|
ok: false,
|
|
56728
|
-
error:
|
|
57696
|
+
error: userInput2(`--${arg.name} value ${quoteUserValue(value)} is not a valid ${arg.type}`)
|
|
56729
57697
|
};
|
|
56730
57698
|
}
|
|
56731
57699
|
if (arg.type === "integer" && !Number.isInteger(numeric)) {
|
|
56732
57700
|
return {
|
|
56733
57701
|
ok: false,
|
|
56734
|
-
error:
|
|
57702
|
+
error: userInput2(`--${arg.name} value ${quoteUserValue(value)} is not an integer`)
|
|
56735
57703
|
};
|
|
56736
57704
|
}
|
|
56737
57705
|
if (arg.min !== undefined && numeric < arg.min) {
|
|
56738
57706
|
return {
|
|
56739
57707
|
ok: false,
|
|
56740
|
-
error:
|
|
57708
|
+
error: userInput2(`--${arg.name} value ${numeric} is below min ${arg.min}`)
|
|
56741
57709
|
};
|
|
56742
57710
|
}
|
|
56743
57711
|
if (arg.max !== undefined && numeric > arg.max) {
|
|
56744
57712
|
return {
|
|
56745
57713
|
ok: false,
|
|
56746
|
-
error:
|
|
57714
|
+
error: userInput2(`--${arg.name} value ${numeric} is above max ${arg.max}`)
|
|
56747
57715
|
};
|
|
56748
57716
|
}
|
|
56749
57717
|
return { ok: true, value: numeric };
|
|
@@ -56757,7 +57725,7 @@ function coerceArg(arg, value) {
|
|
|
56757
57725
|
return { ok: true, value: false };
|
|
56758
57726
|
return {
|
|
56759
57727
|
ok: false,
|
|
56760
|
-
error:
|
|
57728
|
+
error: userInput2(`--${arg.name} requires true|false`)
|
|
56761
57729
|
};
|
|
56762
57730
|
}
|
|
56763
57731
|
}
|
|
@@ -56784,7 +57752,7 @@ function bindComponentMethodArgs(invocation, method) {
|
|
|
56784
57752
|
continue;
|
|
56785
57753
|
if (record2.kind === "malformed-short") {
|
|
56786
57754
|
claimed.add(record2.index);
|
|
56787
|
-
addError(record2.index,
|
|
57755
|
+
addError(record2.index, userInput2(`Unknown argument: --${record2.source.slice(1)}`, `Method '${method.name}' accepts: ${acceptedArgList(method)}`));
|
|
56788
57756
|
continue;
|
|
56789
57757
|
}
|
|
56790
57758
|
if (record2.kind !== "option")
|
|
@@ -56838,7 +57806,7 @@ function bindComponentMethodArgs(invocation, method) {
|
|
|
56838
57806
|
}
|
|
56839
57807
|
for (const record2 of invocation.records) {
|
|
56840
57808
|
if (record2.kind === "positional" && !claimed.has(record2.index) && record2.source !== "--") {
|
|
56841
|
-
addError(record2.index,
|
|
57809
|
+
addError(record2.index, userInput2(`Unexpected argument: '${record2.source}'`, "'wh component exec' accepts at most 2 positional arguments (<component> <method>); pass method inputs as flags"));
|
|
56842
57810
|
}
|
|
56843
57811
|
}
|
|
56844
57812
|
for (const arg of method.args) {
|
|
@@ -56854,7 +57822,7 @@ function bindComponentMethodArgs(invocation, method) {
|
|
|
56854
57822
|
continue;
|
|
56855
57823
|
}
|
|
56856
57824
|
if (arg.required && !invocation.help) {
|
|
56857
|
-
addError(Number.POSITIVE_INFINITY,
|
|
57825
|
+
addError(Number.POSITIVE_INFINITY, userInput2(`Missing required argument: --${arg.name}`));
|
|
56858
57826
|
}
|
|
56859
57827
|
}
|
|
56860
57828
|
indexedErrors.sort((left, right) => left.index - right.index || left.order - right.order);
|
|
@@ -60550,7 +61518,7 @@ var PRIME_DOMAIN = defineDomain({
|
|
|
60550
61518
|
});
|
|
60551
61519
|
|
|
60552
61520
|
// ../../packages/warmhub-cli/src/domains/repo/checkpoint.ts
|
|
60553
|
-
import { createReadStream as
|
|
61521
|
+
import { createReadStream as createReadStream6 } from "node:fs";
|
|
60554
61522
|
import { lstat as lstat3 } from "node:fs/promises";
|
|
60555
61523
|
|
|
60556
61524
|
// ../../packages/sdk-ts/src/repository-checkpoint/types.ts
|
|
@@ -60925,7 +61893,7 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
60925
61893
|
|
|
60926
61894
|
// ../../packages/sdk-ts/src/repository-checkpoint/identity-sort.ts
|
|
60927
61895
|
import { once } from "node:events";
|
|
60928
|
-
import { createReadStream as
|
|
61896
|
+
import { createReadStream as createReadStream5, createWriteStream } from "node:fs";
|
|
60929
61897
|
import { open as open3, rm } from "node:fs/promises";
|
|
60930
61898
|
import { join as join10 } from "node:path";
|
|
60931
61899
|
var CHECKPOINT_IDENTITY_SORT_BUDGET_BYTES = 4 * 1024 * 1024;
|
|
@@ -61199,7 +62167,7 @@ class RunCursor {
|
|
|
61199
62167
|
#offset = 0;
|
|
61200
62168
|
current;
|
|
61201
62169
|
constructor(path2) {
|
|
61202
|
-
this.#stream =
|
|
62170
|
+
this.#stream = createReadStream5(path2, {
|
|
61203
62171
|
highWaterMark: RUN_READ_BUFFER_BYTES
|
|
61204
62172
|
});
|
|
61205
62173
|
this.#iterator = this.#stream[Symbol.asyncIterator]();
|
|
@@ -61591,8 +62559,8 @@ async function writeAll(handle, bytes) {
|
|
|
61591
62559
|
offset += bytesWritten;
|
|
61592
62560
|
}
|
|
61593
62561
|
}
|
|
61594
|
-
function assertArtifactIntegrity(byteLength,
|
|
61595
|
-
if (byteLength !== descriptor.byteLength ||
|
|
62562
|
+
function assertArtifactIntegrity(byteLength, sha2562, descriptor) {
|
|
62563
|
+
if (byteLength !== descriptor.byteLength || sha2562 !== descriptor.sha256) {
|
|
61596
62564
|
throw new CliError(4 /* Backend */, "BACKEND", "Downloaded checkpoint artifact did not match its access descriptor.");
|
|
61597
62565
|
}
|
|
61598
62566
|
}
|
|
@@ -61847,7 +62815,7 @@ var handleVerify = async (ctx, { args }) => {
|
|
|
61847
62815
|
}
|
|
61848
62816
|
try {
|
|
61849
62817
|
await lstat3(archive);
|
|
61850
|
-
const result = await verifyRepositoryCheckpointArchive(
|
|
62818
|
+
const result = await verifyRepositoryCheckpointArchive(createReadStream6(archive));
|
|
61851
62819
|
writeOutput(ctx, result, () => {
|
|
61852
62820
|
ctx.out(`Checkpoint: ${result.checkpointId}`);
|
|
61853
62821
|
ctx.out(`Repository sequence: ${result.repoSeq}`);
|
|
@@ -66795,7 +67763,7 @@ function resolveLogLevel(flagLevel, env) {
|
|
|
66795
67763
|
// package.json
|
|
66796
67764
|
var package_default3 = {
|
|
66797
67765
|
name: "@warmhub/cli",
|
|
66798
|
-
version: "0.
|
|
67766
|
+
version: "0.98.0",
|
|
66799
67767
|
private: false,
|
|
66800
67768
|
type: "module",
|
|
66801
67769
|
description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
|
|
@@ -66817,6 +67785,11 @@ var package_default3 = {
|
|
|
66817
67785
|
url: "https://warmhub.ai"
|
|
66818
67786
|
},
|
|
66819
67787
|
homepage: "https://docs.warmhub.ai/get-started/quickstart/#connect-via-cli",
|
|
67788
|
+
repository: {
|
|
67789
|
+
type: "git",
|
|
67790
|
+
url: "https://github.com/warmhub/warmhub-app.git",
|
|
67791
|
+
directory: "apps/cli"
|
|
67792
|
+
},
|
|
66820
67793
|
bugs: {
|
|
66821
67794
|
email: "support@warmhub.ai"
|
|
66822
67795
|
},
|
|
@@ -66832,7 +67805,8 @@ var package_default3 = {
|
|
|
66832
67805
|
],
|
|
66833
67806
|
publishConfig: {
|
|
66834
67807
|
registry: "https://registry.npmjs.org",
|
|
66835
|
-
access: "public"
|
|
67808
|
+
access: "public",
|
|
67809
|
+
tag: "latest"
|
|
66836
67810
|
},
|
|
66837
67811
|
scripts: {
|
|
66838
67812
|
build: "bun run scripts/build.ts",
|
|
@@ -67414,5 +68388,5 @@ process.exitCode = interceptedExitCode === undefined ? await runPreparedCli(laun
|
|
|
67414
68388
|
version: package_default3.version
|
|
67415
68389
|
}) : interceptedExitCode;
|
|
67416
68390
|
|
|
67417
|
-
//# debugId=
|
|
67418
|
-
//# warmhub-cli-build-info {"cliVersion":"0.
|
|
68391
|
+
//# debugId=8AF6B7466C2C7E3364756E2164756E21
|
|
68392
|
+
//# warmhub-cli-build-info {"cliVersion":"0.98.0","sdkVersion":"0.96.0"}
|