@warmhub/cli 0.95.0 → 0.97.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 +1086 -118
- 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",
|
|
@@ -42109,47 +42117,41 @@ function normalizeOptionalTypeSpec(spec) {
|
|
|
42109
42117
|
return { spec, optionalByType: false };
|
|
42110
42118
|
}
|
|
42111
42119
|
|
|
42112
|
-
// ../../packages/rules/src/shape-validation-
|
|
42113
|
-
function
|
|
42114
|
-
const
|
|
42120
|
+
// ../../packages/rules/src/shape-validation-content-limits.ts
|
|
42121
|
+
function validatePersistedContentLimits(value, typeDefs, errors3, path) {
|
|
42122
|
+
const declared = normalizedCandidates(typeDefs);
|
|
42115
42123
|
if (typeof value === "string") {
|
|
42116
|
-
if (
|
|
42117
|
-
return;
|
|
42118
|
-
if (normalized !== undefined)
|
|
42124
|
+
if (declared.length > 0)
|
|
42119
42125
|
return;
|
|
42120
42126
|
assertContentFieldWithinLimit(path ?? "<value>", value, errors3);
|
|
42121
42127
|
return;
|
|
42122
42128
|
}
|
|
42123
42129
|
if (Array.isArray(value)) {
|
|
42124
|
-
const
|
|
42125
|
-
if (
|
|
42130
|
+
const arrayCandidates = declared.filter(isDeclaredArrayType);
|
|
42131
|
+
if (declared.length > 0 && arrayCandidates.length === 0)
|
|
42126
42132
|
return;
|
|
42127
|
-
|
|
42133
|
+
const elementTypes = definedOnly(arrayCandidates.map(arrayElementType));
|
|
42128
42134
|
for (let i = 0;i < value.length; i++) {
|
|
42129
|
-
|
|
42135
|
+
validatePersistedContentLimits(value[i], elementTypes, errors3, `${path ?? "<value>"}[${i}]`);
|
|
42130
42136
|
}
|
|
42131
42137
|
return;
|
|
42132
42138
|
}
|
|
42133
42139
|
if (!isPlainObject2(value))
|
|
42134
42140
|
return;
|
|
42135
|
-
const
|
|
42136
|
-
if (
|
|
42141
|
+
const objectCandidates = definedOnly(declared.map(nestedObjectFields));
|
|
42142
|
+
if (declared.length > 0 && objectCandidates.length === 0)
|
|
42137
42143
|
return;
|
|
42138
42144
|
for (const [key, nestedValue] of Object.entries(value)) {
|
|
42139
42145
|
const keyPath = path ? joinFieldPath(path, key) : escapeFieldNameForDisplay(key);
|
|
42140
42146
|
assertContentFieldWithinLimit(keyPath, key, errors3);
|
|
42141
|
-
|
|
42147
|
+
validatePersistedContentLimits(nestedValue, definedOnly(objectCandidates.map((fields) => fields.get(key))), errors3, keyPath);
|
|
42142
42148
|
}
|
|
42143
42149
|
}
|
|
42144
|
-
function
|
|
42145
|
-
|
|
42146
|
-
return true;
|
|
42147
|
-
return isTypeSpecObject(typeDef) && typeDef.type === "string";
|
|
42150
|
+
function normalizedCandidates(typeDefs) {
|
|
42151
|
+
return definedOnly(typeDefs.map((typeDef) => normalizeOptionalTypeSpec(typeDef).spec));
|
|
42148
42152
|
}
|
|
42149
|
-
function
|
|
42150
|
-
|
|
42151
|
-
return true;
|
|
42152
|
-
return isTypeSpecObject(typeDef) && typeDef.type === "wref";
|
|
42153
|
+
function definedOnly(values) {
|
|
42154
|
+
return values.filter((value) => value !== undefined);
|
|
42153
42155
|
}
|
|
42154
42156
|
function arrayElementType(typeDef) {
|
|
42155
42157
|
if (Array.isArray(typeDef))
|
|
@@ -42538,7 +42540,7 @@ function validateShapeDefinition(data, options = {}) {
|
|
|
42538
42540
|
if ("description" in obj && typeof obj.description !== "string") {
|
|
42539
42541
|
errors3.push('"description" must be a string');
|
|
42540
42542
|
}
|
|
42541
|
-
|
|
42543
|
+
validatePersistedContentLimits(obj, [], errors3);
|
|
42542
42544
|
if (errors3.length > 0) {
|
|
42543
42545
|
return { valid: false, errors: errors3 };
|
|
42544
42546
|
}
|
|
@@ -43382,7 +43384,7 @@ class OperationSourceError extends Error {
|
|
|
43382
43384
|
}
|
|
43383
43385
|
}
|
|
43384
43386
|
async function submitOperationsViaStream(client, args) {
|
|
43385
|
-
const chunkSize =
|
|
43387
|
+
const chunkSize = normalizeStreamChunkSize(args.chunkSize);
|
|
43386
43388
|
const submissionId = args.submissionId ?? createOperationEventSubmissionId();
|
|
43387
43389
|
const streamId = args.streamId ?? submissionId;
|
|
43388
43390
|
const policy = resolveRetryPolicy(args.retry);
|
|
@@ -43482,7 +43484,7 @@ async function submitOperationsViaStream(client, args) {
|
|
|
43482
43484
|
async function* normalizedChunks(source, chunkSize, skipExisting) {
|
|
43483
43485
|
try {
|
|
43484
43486
|
if (Array.isArray(source)) {
|
|
43485
|
-
const operations = source.map((operation, index) =>
|
|
43487
|
+
const operations = source.map((operation, index) => normalizeStreamOperation(operation, index, skipExisting));
|
|
43486
43488
|
validateNormalizedOperations(operations, 0);
|
|
43487
43489
|
for (let start = 0;start < operations.length; start += chunkSize) {
|
|
43488
43490
|
yield { operations: operations.slice(start, start + chunkSize), start };
|
|
@@ -43491,8 +43493,8 @@ async function* normalizedChunks(source, chunkSize, skipExisting) {
|
|
|
43491
43493
|
}
|
|
43492
43494
|
let normalizedCount = 0;
|
|
43493
43495
|
let buffer = [];
|
|
43494
|
-
for await (const operation of source) {
|
|
43495
|
-
buffer.push(
|
|
43496
|
+
for await (const operation of normalizedOperationSource(source, skipExisting)) {
|
|
43497
|
+
buffer.push(operation);
|
|
43496
43498
|
normalizedCount += 1;
|
|
43497
43499
|
if (buffer.length < chunkSize)
|
|
43498
43500
|
continue;
|
|
@@ -43510,7 +43512,7 @@ function preflightedChunk(operations, normalizedCount) {
|
|
|
43510
43512
|
validateNormalizedOperations(operations, start);
|
|
43511
43513
|
return { operations, start };
|
|
43512
43514
|
}
|
|
43513
|
-
function
|
|
43515
|
+
function normalizeStreamOperation(operation, index, skipExisting) {
|
|
43514
43516
|
let streamOperation;
|
|
43515
43517
|
try {
|
|
43516
43518
|
streamOperation = toBackendStreamOperation(operation);
|
|
@@ -43523,6 +43525,57 @@ function normalizeOperation(operation, index, skipExisting) {
|
|
|
43523
43525
|
}
|
|
43524
43526
|
return streamOperation;
|
|
43525
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;
|
|
43526
43579
|
function receiptRepoSeq(receipt) {
|
|
43527
43580
|
const repoSeq = receipt.event?.repoSeq;
|
|
43528
43581
|
if (repoSeq === undefined)
|
|
@@ -43539,15 +43592,579 @@ function validateNormalizedOperations(operations, indexOffset) {
|
|
|
43539
43592
|
function isServerAuthoritativeSequenceDiagnostic(diagnostic) {
|
|
43540
43593
|
return diagnostic.code === "ILLEGAL_OP_SEQUENCE" && diagnostic.message.includes("Cannot revise then add ");
|
|
43541
43594
|
}
|
|
43542
|
-
function
|
|
43595
|
+
function normalizeStreamChunkSize(chunkSize) {
|
|
43543
43596
|
if (!Number.isFinite(chunkSize))
|
|
43544
43597
|
return DEFAULT_STREAM_CHUNK_SIZE;
|
|
43545
43598
|
return Math.max(1, Math.min(MAX_STREAM_APPEND_OPERATION_COUNT2, Math.trunc(chunkSize)));
|
|
43546
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
|
+
}
|
|
43547
44164
|
// ../../packages/sdk-ts/package.json
|
|
43548
44165
|
var package_default = {
|
|
43549
44166
|
name: "@warmhub/sdk-ts",
|
|
43550
|
-
version: "0.
|
|
44167
|
+
version: "0.95.0",
|
|
43551
44168
|
private: false,
|
|
43552
44169
|
type: "module",
|
|
43553
44170
|
description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
|
|
@@ -43569,6 +44186,11 @@ var package_default = {
|
|
|
43569
44186
|
url: "https://warmhub.ai"
|
|
43570
44187
|
},
|
|
43571
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
|
+
},
|
|
43572
44194
|
bugs: {
|
|
43573
44195
|
email: "support@warmhub.ai"
|
|
43574
44196
|
},
|
|
@@ -43584,7 +44206,8 @@ var package_default = {
|
|
|
43584
44206
|
],
|
|
43585
44207
|
publishConfig: {
|
|
43586
44208
|
registry: "https://registry.npmjs.org",
|
|
43587
|
-
access: "public"
|
|
44209
|
+
access: "public",
|
|
44210
|
+
tag: "latest"
|
|
43588
44211
|
},
|
|
43589
44212
|
exports: {
|
|
43590
44213
|
".": {
|
|
@@ -43602,7 +44225,7 @@ var package_default = {
|
|
|
43602
44225
|
},
|
|
43603
44226
|
scripts: {
|
|
43604
44227
|
build: 'if [ "${WARMHUB_TEST_RUNTIME_BUILD:-}" = "1" ]; then bun run build:test-runtime; else bun run build:full; fi',
|
|
43605
|
-
"build:full": "
|
|
44228
|
+
"build:full": "bun run build:runtime && bun run build:types && bun run scripts/inject-doc-links.ts",
|
|
43606
44229
|
"build:runtime": "tsup",
|
|
43607
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",
|
|
43608
44231
|
"build:test-runtime": "tsup --config tsup.test-runtime.config.ts",
|
|
@@ -44431,6 +45054,11 @@ class WarmHubClient {
|
|
|
44431
45054
|
throw toWarmHubError(error51);
|
|
44432
45055
|
}
|
|
44433
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
|
+
}),
|
|
44434
45062
|
getReceipt: async (orgName, repoName, eventRequestId) => {
|
|
44435
45063
|
try {
|
|
44436
45064
|
return await this.trpc.commit.getReceipt.query({
|
|
@@ -46186,32 +46814,54 @@ class WarmHubClient {
|
|
|
46186
46814
|
}
|
|
46187
46815
|
async requestResponse(path, init) {
|
|
46188
46816
|
const response = await this.fetchWithAuth(`${this.apiUrl.replace(/\/$/, "")}${path}`, init);
|
|
46189
|
-
if (!response.ok)
|
|
46190
|
-
|
|
46191
|
-
let code = httpStatusToWarmHubCode(response.status);
|
|
46192
|
-
let errorCode;
|
|
46193
|
-
let hint;
|
|
46194
|
-
let retryAfter;
|
|
46195
|
-
try {
|
|
46196
|
-
const body = await response.json();
|
|
46197
|
-
if (typeof body.error?.message === "string") {
|
|
46198
|
-
message = body.error.message;
|
|
46199
|
-
}
|
|
46200
|
-
if (typeof body.error?.code === "string") {
|
|
46201
|
-
code = body.error.code;
|
|
46202
|
-
errorCode = body.error.code;
|
|
46203
|
-
}
|
|
46204
|
-
if (typeof body.error?.hint === "string") {
|
|
46205
|
-
hint = body.error.hint;
|
|
46206
|
-
}
|
|
46207
|
-
if (typeof body.error?.retryAfter === "number") {
|
|
46208
|
-
retryAfter = body.error.retryAfter;
|
|
46209
|
-
}
|
|
46210
|
-
} catch {}
|
|
46211
|
-
throw new WarmHubError(code, message, response.status, hint, retryAfter, errorCode);
|
|
46212
|
-
}
|
|
46817
|
+
if (!response.ok)
|
|
46818
|
+
throw await this.httpResponseError(response);
|
|
46213
46819
|
return response;
|
|
46214
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
|
+
}
|
|
46215
46865
|
async requestJson(path, init) {
|
|
46216
46866
|
const response = await this.requestResponse(path, init);
|
|
46217
46867
|
return await response.json();
|
|
@@ -46756,12 +47406,22 @@ function printCliError(err, errWriter, opts = {}) {
|
|
|
46756
47406
|
function fromWh(exit, kind, err, hint = err.hint) {
|
|
46757
47407
|
return new CliError(exit, kind, err.message, err, hint, undefined, err.errorCode);
|
|
46758
47408
|
}
|
|
46759
|
-
function
|
|
47409
|
+
function classifyAuthError(input) {
|
|
46760
47410
|
if (input.code !== "UNAUTHENTICATED" && input.code !== "FORBIDDEN") {
|
|
46761
47411
|
return;
|
|
46762
47412
|
}
|
|
46763
47413
|
const fallbackHint = input.code === "FORBIDDEN" ? "Check credentials and backend permissions." : unauthenticatedHint(input.message);
|
|
46764
|
-
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);
|
|
46765
47425
|
}
|
|
46766
47426
|
function usageError(usage, ...examples) {
|
|
46767
47427
|
const hint = examples.length === 0 ? undefined : examples.length === 1 ? `Example: ${examples[0]}` : `Examples:
|
|
@@ -46863,61 +47523,62 @@ var OP_USER_INPUT_CODES = new Set([
|
|
|
46863
47523
|
"BUILTIN_SHAPE",
|
|
46864
47524
|
"DEPENDENCY_FAILED"
|
|
46865
47525
|
]);
|
|
46866
|
-
function
|
|
47526
|
+
function classifyOpFailure(failure) {
|
|
46867
47527
|
const diagnostic = failure.errors?.[0] ?? failure.error;
|
|
46868
47528
|
const code = diagnostic?.code ?? "BACKEND";
|
|
46869
47529
|
const message = diagnostic?.message ?? `Operation on "${failure.name}" failed`;
|
|
46870
47530
|
const errorCode = diagnostic?.code;
|
|
46871
|
-
const
|
|
46872
|
-
const authError =
|
|
46873
|
-
code,
|
|
46874
|
-
message,
|
|
46875
|
-
backendCode: errorCode
|
|
46876
|
-
});
|
|
47531
|
+
const classified = (exit, kind, hint) => ({ exit, kind, message, hint, errorCode });
|
|
47532
|
+
const authError = classifyAuthError({ code, message });
|
|
46877
47533
|
if (authError)
|
|
46878
|
-
return authError;
|
|
47534
|
+
return classified(authError.exit, authError.kind, authError.hint);
|
|
46879
47535
|
if (CONFLICT_SHAPED_CODES.has(code)) {
|
|
46880
|
-
return
|
|
47536
|
+
return classified(2 /* UserInput */, "CONFLICT", conflictHint(message, code));
|
|
46881
47537
|
}
|
|
46882
47538
|
if (OP_USER_INPUT_CODES.has(code)) {
|
|
46883
|
-
return
|
|
47539
|
+
return classified(2 /* UserInput */, "USER_INPUT");
|
|
46884
47540
|
}
|
|
46885
47541
|
if (isFieldIndexErrorCode(code)) {
|
|
46886
47542
|
const exit = code === "FIELD_NOT_INDEXABLE" ? 2 /* UserInput */ : 4 /* Backend */;
|
|
46887
|
-
return
|
|
47543
|
+
return classified(exit, code);
|
|
46888
47544
|
}
|
|
46889
47545
|
if (code === "RATE_LIMITED") {
|
|
46890
|
-
return
|
|
47546
|
+
return classified(4 /* Backend */, "RATE_LIMITED", "Rate limited. Wait for the Retry-After window before retrying.");
|
|
46891
47547
|
}
|
|
46892
47548
|
if (code === "QUERY_TOO_EXPENSIVE") {
|
|
46893
|
-
return
|
|
47549
|
+
return classified(2 /* UserInput */, "QUERY_TOO_EXPENSIVE", "Narrow the query with a more selective field predicate or retry with a shallower page.");
|
|
46894
47550
|
}
|
|
46895
|
-
return
|
|
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;
|
|
47570
|
+
}
|
|
47571
|
+
return current;
|
|
46896
47572
|
}
|
|
46897
47573
|
function cliErrorFromAllFailed(failures) {
|
|
46898
|
-
|
|
46899
|
-
if (err.code === 5 /* Auth */)
|
|
46900
|
-
return 4;
|
|
46901
|
-
if (err.code === 2 /* UserInput */)
|
|
46902
|
-
return 3;
|
|
46903
|
-
if (err.kind === "RATE_LIMITED")
|
|
46904
|
-
return 2;
|
|
46905
|
-
return 1;
|
|
46906
|
-
};
|
|
46907
|
-
let bestErr;
|
|
46908
|
-
let bestPriority = -1;
|
|
47574
|
+
let bestFailure;
|
|
46909
47575
|
for (const failure of failures) {
|
|
46910
47576
|
if (!isFailedOpStatus(failure.status))
|
|
46911
47577
|
continue;
|
|
46912
|
-
|
|
46913
|
-
const priority = priorityOf(err);
|
|
46914
|
-
if (priority > bestPriority) {
|
|
46915
|
-
bestErr = err;
|
|
46916
|
-
bestPriority = priority;
|
|
46917
|
-
}
|
|
47578
|
+
bestFailure = preferMoreSevereOpFailure(bestFailure, failure);
|
|
46918
47579
|
}
|
|
46919
|
-
if (
|
|
46920
|
-
return
|
|
47580
|
+
if (bestFailure)
|
|
47581
|
+
return cliErrorFromOpFailure(bestFailure);
|
|
46921
47582
|
return new CliError(4 /* Backend */, "BACKEND", `All ${failures.length} operations failed`);
|
|
46922
47583
|
}
|
|
46923
47584
|
function requireSingleOpSuccess(result) {
|
|
@@ -55108,6 +55769,12 @@ var createFlags3 = {
|
|
|
55108
55769
|
"chunk-size": flag.number({
|
|
55109
55770
|
description: `Streamed ops per append chunk for --stream or .jsonl --file (default: ${DEFAULT_STREAM_APPEND_CHUNK_SIZE}, max: ${MAX_STREAM_APPEND_OPERATION_COUNT})`
|
|
55110
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
|
+
}),
|
|
55111
55778
|
"allow-nul-bytes": flag.boolean({
|
|
55112
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."
|
|
55113
55780
|
}),
|
|
@@ -55172,6 +55839,21 @@ var createFlags3 = {
|
|
|
55172
55839
|
})
|
|
55173
55840
|
};
|
|
55174
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
|
+
|
|
55175
55857
|
// ../../packages/warmhub-cli/src/domains/commit-submit-template.ts
|
|
55176
55858
|
import { writeFile } from "node:fs/promises";
|
|
55177
55859
|
var WRITE_TEMPLATE_KINDS = ["thing", "assertion"];
|
|
@@ -56514,6 +57196,284 @@ var handleSubmit = async (ctx, { flags, args }) => {
|
|
|
56514
57196
|
}
|
|
56515
57197
|
};
|
|
56516
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
|
+
|
|
56517
57477
|
// ../../packages/warmhub-cli/src/domains/commit-submit-domain.ts
|
|
56518
57478
|
var COMMIT_DOMAIN = defineDomain({
|
|
56519
57479
|
name: "commit",
|
|
@@ -56544,6 +57504,7 @@ var COMMIT_DOMAIN = defineDomain({
|
|
|
56544
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"`,
|
|
56545
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"`,
|
|
56546
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"',
|
|
56547
57508
|
"wh shape template Session HypothesisCandidate -o ops.json",
|
|
56548
57509
|
"wh commit submit --type arc --name route --members Location/a,Location/b",
|
|
56549
57510
|
"wh commit submit --type bond --name peers --members Person/alice,Person/bob",
|
|
@@ -56552,9 +57513,10 @@ var COMMIT_DOMAIN = defineDomain({
|
|
|
56552
57513
|
notes: [
|
|
56553
57514
|
"`--dry-run` evaluates the complete bounded input with the real server commit evaluator and makes no durable repository change.",
|
|
56554
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`.",
|
|
56555
|
-
"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."
|
|
56556
57518
|
],
|
|
56557
|
-
handler:
|
|
57519
|
+
handler: handleSubmitWithServerStreaming
|
|
56558
57520
|
}
|
|
56559
57521
|
}
|
|
56560
57522
|
});
|
|
@@ -56646,7 +57608,7 @@ for (const spec of PRE_TERMINATOR_CONTROLS) {
|
|
|
56646
57608
|
if (spec.short)
|
|
56647
57609
|
CONTROL_SHORTS.set(spec.short, { spec, constant: false });
|
|
56648
57610
|
}
|
|
56649
|
-
function
|
|
57611
|
+
function userInput2(message, hint) {
|
|
56650
57612
|
return new CliError(2 /* UserInput */, "USER_INPUT", message, undefined, hint);
|
|
56651
57613
|
}
|
|
56652
57614
|
function hasAttachedValue(record2) {
|
|
@@ -56683,13 +57645,13 @@ function acceptedArgList(method) {
|
|
|
56683
57645
|
function unknownArgumentError(method, record2) {
|
|
56684
57646
|
const accepted = acceptedArgList(method);
|
|
56685
57647
|
const suffix = record2.ownership === "post-terminator" ? ` (use \`--\` to pass globals as method args, e.g. \`-- --${record2.name} <value>\`)` : "";
|
|
56686
|
-
return
|
|
57648
|
+
return userInput2(`Unknown argument: --${record2.name}`, `Method '${method.name}' accepts: ${accepted}${suffix}`);
|
|
56687
57649
|
}
|
|
56688
57650
|
function duplicateError(arg) {
|
|
56689
|
-
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.`);
|
|
56690
57652
|
}
|
|
56691
57653
|
function missingValueError(arg) {
|
|
56692
|
-
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}>`);
|
|
56693
57655
|
}
|
|
56694
57656
|
function coerceArg(arg, value) {
|
|
56695
57657
|
switch (arg.type) {
|
|
@@ -56697,7 +57659,7 @@ function coerceArg(arg, value) {
|
|
|
56697
57659
|
if (typeof value !== "string") {
|
|
56698
57660
|
return {
|
|
56699
57661
|
ok: false,
|
|
56700
|
-
error:
|
|
57662
|
+
error: userInput2(`--${arg.name} requires a string value`)
|
|
56701
57663
|
};
|
|
56702
57664
|
}
|
|
56703
57665
|
if (arg.pattern) {
|
|
@@ -56707,13 +57669,13 @@ function coerceArg(arg, value) {
|
|
|
56707
57669
|
} catch {
|
|
56708
57670
|
return {
|
|
56709
57671
|
ok: false,
|
|
56710
|
-
error:
|
|
57672
|
+
error: userInput2(`invalid regex pattern for --${arg.name}: /${arg.pattern}/`)
|
|
56711
57673
|
};
|
|
56712
57674
|
}
|
|
56713
57675
|
if (!pattern.test(value)) {
|
|
56714
57676
|
return {
|
|
56715
57677
|
ok: false,
|
|
56716
|
-
error:
|
|
57678
|
+
error: userInput2(`--${arg.name} value '${value}' does not match pattern /${arg.pattern}/`)
|
|
56717
57679
|
};
|
|
56718
57680
|
}
|
|
56719
57681
|
}
|
|
@@ -56724,32 +57686,32 @@ function coerceArg(arg, value) {
|
|
|
56724
57686
|
if (typeof value === "boolean") {
|
|
56725
57687
|
return {
|
|
56726
57688
|
ok: false,
|
|
56727
|
-
error:
|
|
57689
|
+
error: userInput2(`--${arg.name} requires a ${arg.type} value`)
|
|
56728
57690
|
};
|
|
56729
57691
|
}
|
|
56730
57692
|
const numeric = typeof value === "number" ? Number.isFinite(value) ? value : undefined : typeof value === "string" ? parseCliNumber(value) : undefined;
|
|
56731
57693
|
if (numeric === undefined) {
|
|
56732
57694
|
return {
|
|
56733
57695
|
ok: false,
|
|
56734
|
-
error:
|
|
57696
|
+
error: userInput2(`--${arg.name} value ${quoteUserValue(value)} is not a valid ${arg.type}`)
|
|
56735
57697
|
};
|
|
56736
57698
|
}
|
|
56737
57699
|
if (arg.type === "integer" && !Number.isInteger(numeric)) {
|
|
56738
57700
|
return {
|
|
56739
57701
|
ok: false,
|
|
56740
|
-
error:
|
|
57702
|
+
error: userInput2(`--${arg.name} value ${quoteUserValue(value)} is not an integer`)
|
|
56741
57703
|
};
|
|
56742
57704
|
}
|
|
56743
57705
|
if (arg.min !== undefined && numeric < arg.min) {
|
|
56744
57706
|
return {
|
|
56745
57707
|
ok: false,
|
|
56746
|
-
error:
|
|
57708
|
+
error: userInput2(`--${arg.name} value ${numeric} is below min ${arg.min}`)
|
|
56747
57709
|
};
|
|
56748
57710
|
}
|
|
56749
57711
|
if (arg.max !== undefined && numeric > arg.max) {
|
|
56750
57712
|
return {
|
|
56751
57713
|
ok: false,
|
|
56752
|
-
error:
|
|
57714
|
+
error: userInput2(`--${arg.name} value ${numeric} is above max ${arg.max}`)
|
|
56753
57715
|
};
|
|
56754
57716
|
}
|
|
56755
57717
|
return { ok: true, value: numeric };
|
|
@@ -56763,7 +57725,7 @@ function coerceArg(arg, value) {
|
|
|
56763
57725
|
return { ok: true, value: false };
|
|
56764
57726
|
return {
|
|
56765
57727
|
ok: false,
|
|
56766
|
-
error:
|
|
57728
|
+
error: userInput2(`--${arg.name} requires true|false`)
|
|
56767
57729
|
};
|
|
56768
57730
|
}
|
|
56769
57731
|
}
|
|
@@ -56790,7 +57752,7 @@ function bindComponentMethodArgs(invocation, method) {
|
|
|
56790
57752
|
continue;
|
|
56791
57753
|
if (record2.kind === "malformed-short") {
|
|
56792
57754
|
claimed.add(record2.index);
|
|
56793
|
-
addError(record2.index,
|
|
57755
|
+
addError(record2.index, userInput2(`Unknown argument: --${record2.source.slice(1)}`, `Method '${method.name}' accepts: ${acceptedArgList(method)}`));
|
|
56794
57756
|
continue;
|
|
56795
57757
|
}
|
|
56796
57758
|
if (record2.kind !== "option")
|
|
@@ -56844,7 +57806,7 @@ function bindComponentMethodArgs(invocation, method) {
|
|
|
56844
57806
|
}
|
|
56845
57807
|
for (const record2 of invocation.records) {
|
|
56846
57808
|
if (record2.kind === "positional" && !claimed.has(record2.index) && record2.source !== "--") {
|
|
56847
|
-
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"));
|
|
56848
57810
|
}
|
|
56849
57811
|
}
|
|
56850
57812
|
for (const arg of method.args) {
|
|
@@ -56860,7 +57822,7 @@ function bindComponentMethodArgs(invocation, method) {
|
|
|
56860
57822
|
continue;
|
|
56861
57823
|
}
|
|
56862
57824
|
if (arg.required && !invocation.help) {
|
|
56863
|
-
addError(Number.POSITIVE_INFINITY,
|
|
57825
|
+
addError(Number.POSITIVE_INFINITY, userInput2(`Missing required argument: --${arg.name}`));
|
|
56864
57826
|
}
|
|
56865
57827
|
}
|
|
56866
57828
|
indexedErrors.sort((left, right) => left.index - right.index || left.order - right.order);
|
|
@@ -60556,7 +61518,7 @@ var PRIME_DOMAIN = defineDomain({
|
|
|
60556
61518
|
});
|
|
60557
61519
|
|
|
60558
61520
|
// ../../packages/warmhub-cli/src/domains/repo/checkpoint.ts
|
|
60559
|
-
import { createReadStream as
|
|
61521
|
+
import { createReadStream as createReadStream6 } from "node:fs";
|
|
60560
61522
|
import { lstat as lstat3 } from "node:fs/promises";
|
|
60561
61523
|
|
|
60562
61524
|
// ../../packages/sdk-ts/src/repository-checkpoint/types.ts
|
|
@@ -60931,7 +61893,7 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
60931
61893
|
|
|
60932
61894
|
// ../../packages/sdk-ts/src/repository-checkpoint/identity-sort.ts
|
|
60933
61895
|
import { once } from "node:events";
|
|
60934
|
-
import { createReadStream as
|
|
61896
|
+
import { createReadStream as createReadStream5, createWriteStream } from "node:fs";
|
|
60935
61897
|
import { open as open3, rm } from "node:fs/promises";
|
|
60936
61898
|
import { join as join10 } from "node:path";
|
|
60937
61899
|
var CHECKPOINT_IDENTITY_SORT_BUDGET_BYTES = 4 * 1024 * 1024;
|
|
@@ -61205,7 +62167,7 @@ class RunCursor {
|
|
|
61205
62167
|
#offset = 0;
|
|
61206
62168
|
current;
|
|
61207
62169
|
constructor(path2) {
|
|
61208
|
-
this.#stream =
|
|
62170
|
+
this.#stream = createReadStream5(path2, {
|
|
61209
62171
|
highWaterMark: RUN_READ_BUFFER_BYTES
|
|
61210
62172
|
});
|
|
61211
62173
|
this.#iterator = this.#stream[Symbol.asyncIterator]();
|
|
@@ -61597,8 +62559,8 @@ async function writeAll(handle, bytes) {
|
|
|
61597
62559
|
offset += bytesWritten;
|
|
61598
62560
|
}
|
|
61599
62561
|
}
|
|
61600
|
-
function assertArtifactIntegrity(byteLength,
|
|
61601
|
-
if (byteLength !== descriptor.byteLength ||
|
|
62562
|
+
function assertArtifactIntegrity(byteLength, sha2562, descriptor) {
|
|
62563
|
+
if (byteLength !== descriptor.byteLength || sha2562 !== descriptor.sha256) {
|
|
61602
62564
|
throw new CliError(4 /* Backend */, "BACKEND", "Downloaded checkpoint artifact did not match its access descriptor.");
|
|
61603
62565
|
}
|
|
61604
62566
|
}
|
|
@@ -61853,7 +62815,7 @@ var handleVerify = async (ctx, { args }) => {
|
|
|
61853
62815
|
}
|
|
61854
62816
|
try {
|
|
61855
62817
|
await lstat3(archive);
|
|
61856
|
-
const result = await verifyRepositoryCheckpointArchive(
|
|
62818
|
+
const result = await verifyRepositoryCheckpointArchive(createReadStream6(archive));
|
|
61857
62819
|
writeOutput(ctx, result, () => {
|
|
61858
62820
|
ctx.out(`Checkpoint: ${result.checkpointId}`);
|
|
61859
62821
|
ctx.out(`Repository sequence: ${result.repoSeq}`);
|
|
@@ -66801,7 +67763,7 @@ function resolveLogLevel(flagLevel, env) {
|
|
|
66801
67763
|
// package.json
|
|
66802
67764
|
var package_default3 = {
|
|
66803
67765
|
name: "@warmhub/cli",
|
|
66804
|
-
version: "0.
|
|
67766
|
+
version: "0.97.0",
|
|
66805
67767
|
private: false,
|
|
66806
67768
|
type: "module",
|
|
66807
67769
|
description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
|
|
@@ -66823,6 +67785,11 @@ var package_default3 = {
|
|
|
66823
67785
|
url: "https://warmhub.ai"
|
|
66824
67786
|
},
|
|
66825
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
|
+
},
|
|
66826
67793
|
bugs: {
|
|
66827
67794
|
email: "support@warmhub.ai"
|
|
66828
67795
|
},
|
|
@@ -66838,7 +67805,8 @@ var package_default3 = {
|
|
|
66838
67805
|
],
|
|
66839
67806
|
publishConfig: {
|
|
66840
67807
|
registry: "https://registry.npmjs.org",
|
|
66841
|
-
access: "public"
|
|
67808
|
+
access: "public",
|
|
67809
|
+
tag: "latest"
|
|
66842
67810
|
},
|
|
66843
67811
|
scripts: {
|
|
66844
67812
|
build: "bun run scripts/build.ts",
|
|
@@ -67420,5 +68388,5 @@ process.exitCode = interceptedExitCode === undefined ? await runPreparedCli(laun
|
|
|
67420
68388
|
version: package_default3.version
|
|
67421
68389
|
}) : interceptedExitCode;
|
|
67422
68390
|
|
|
67423
|
-
//# debugId=
|
|
67424
|
-
//# warmhub-cli-build-info {"cliVersion":"0.
|
|
68391
|
+
//# debugId=52246A7D0DA1719964756E2164756E21
|
|
68392
|
+
//# warmhub-cli-build-info {"cliVersion":"0.97.0","sdkVersion":"0.95.0"}
|