@linzumi/cli 0.0.101-beta → 0.0.103-beta
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/README.md +1 -1
- package/dist/index.js +292 -146
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -3634,7 +3634,7 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3634
3634
|
}
|
|
3635
3635
|
dispatchPushedFrame(topic, name, ref, payload);
|
|
3636
3636
|
};
|
|
3637
|
-
const pushOnOpenSocket = (topic, event, payload, timeoutMs = pushTimeoutMs) => {
|
|
3637
|
+
const pushOnOpenSocket = (topic, event, payload, timeoutMs = pushTimeoutMs, onSent) => {
|
|
3638
3638
|
const websocket = state.websocket;
|
|
3639
3639
|
if (websocket === void 0 || websocket.readyState !== WebSocket.OPEN) {
|
|
3640
3640
|
return Promise.reject(phoenixPushErrors.socketNotOpen());
|
|
@@ -3655,6 +3655,7 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3655
3655
|
}, replyBudgetMs);
|
|
3656
3656
|
pending.set(ref, { event, timer, resolve: resolve12, reject });
|
|
3657
3657
|
websocket.send(JSON.stringify(frame));
|
|
3658
|
+
onSent?.();
|
|
3658
3659
|
});
|
|
3659
3660
|
};
|
|
3660
3661
|
const mergedJoinPayload = (topic, payloads) => {
|
|
@@ -3867,10 +3868,16 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3867
3868
|
}
|
|
3868
3869
|
);
|
|
3869
3870
|
});
|
|
3870
|
-
const push = async (topic, event, payload) => {
|
|
3871
|
+
const push = async (topic, event, payload, onSent) => {
|
|
3871
3872
|
const deadlineMs = Date.now() + pushTimeoutMs;
|
|
3872
3873
|
await awaitReady(event, deadlineMs);
|
|
3873
|
-
return pushOnOpenSocket(
|
|
3874
|
+
return pushOnOpenSocket(
|
|
3875
|
+
topic,
|
|
3876
|
+
event,
|
|
3877
|
+
payload,
|
|
3878
|
+
deadlineMs - Date.now(),
|
|
3879
|
+
onSent
|
|
3880
|
+
);
|
|
3874
3881
|
};
|
|
3875
3882
|
return {
|
|
3876
3883
|
join: async (topic, payload, options2) => {
|
|
@@ -4548,10 +4555,15 @@ function createThreadOutbox(options) {
|
|
|
4548
4555
|
let paused = false;
|
|
4549
4556
|
let acceptingEnqueues = true;
|
|
4550
4557
|
let stopped = false;
|
|
4551
|
-
let drainPromise;
|
|
4552
|
-
let wakeSleep;
|
|
4553
4558
|
let closePromise;
|
|
4554
4559
|
let journal;
|
|
4560
|
+
const maxInFlightPushes = Math.max(
|
|
4561
|
+
1,
|
|
4562
|
+
Math.floor(options.maxInFlightPushes ?? 1)
|
|
4563
|
+
);
|
|
4564
|
+
let inFlightCount = 0;
|
|
4565
|
+
let drainIdleWaiters = [];
|
|
4566
|
+
let sendChainTail = Promise.resolve();
|
|
4555
4567
|
const headEntry = () => queue[headIndex];
|
|
4556
4568
|
const depth = () => queue.length - headIndex;
|
|
4557
4569
|
const currentCounters = () => ({
|
|
@@ -4661,44 +4673,90 @@ function createThreadOutbox(options) {
|
|
|
4661
4673
|
coalescible.delete(key);
|
|
4662
4674
|
}
|
|
4663
4675
|
};
|
|
4664
|
-
const
|
|
4676
|
+
const applyHeadOutcome = (pending, outcome) => {
|
|
4677
|
+
const entry = pending.entry;
|
|
4678
|
+
switch (outcome.kind) {
|
|
4679
|
+
case "ack": {
|
|
4680
|
+
const builder = builders[entry.kind];
|
|
4681
|
+
try {
|
|
4682
|
+
builder?.onReply?.(entry.data, outcome.reply, context);
|
|
4683
|
+
} catch (error) {
|
|
4684
|
+
log2("outbox_on_reply_failed", {
|
|
4685
|
+
threadKey,
|
|
4686
|
+
entrySeq: entry.entrySeq,
|
|
4687
|
+
kind: entry.kind,
|
|
4688
|
+
error: errorMessage2(error)
|
|
4689
|
+
});
|
|
4690
|
+
}
|
|
4691
|
+
journal?.appendAck(entry.entrySeq);
|
|
4692
|
+
break;
|
|
4693
|
+
}
|
|
4694
|
+
case "skip": {
|
|
4695
|
+
log2("outbox_entry_skipped", {
|
|
4696
|
+
threadKey,
|
|
4697
|
+
entrySeq: entry.entrySeq,
|
|
4698
|
+
kind: entry.kind,
|
|
4699
|
+
reason: outcome.reason
|
|
4700
|
+
});
|
|
4701
|
+
journal?.appendSkip(entry.entrySeq, outcome.reason);
|
|
4702
|
+
break;
|
|
4703
|
+
}
|
|
4704
|
+
case "fail": {
|
|
4705
|
+
if (entry.resolution === "must_ack") {
|
|
4706
|
+
journal?.appendFail(entry.entrySeq);
|
|
4707
|
+
try {
|
|
4708
|
+
onPermanentFailure(entry, outcome.failure);
|
|
4709
|
+
} catch (error) {
|
|
4710
|
+
log2("outbox_permanent_failure_callback_failed", {
|
|
4711
|
+
threadKey,
|
|
4712
|
+
entrySeq: entry.entrySeq,
|
|
4713
|
+
error: errorMessage2(error)
|
|
4714
|
+
});
|
|
4715
|
+
}
|
|
4716
|
+
} else {
|
|
4717
|
+
journal?.appendDrop(entry.entrySeq);
|
|
4718
|
+
}
|
|
4719
|
+
break;
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4665
4722
|
removeFromCoalescible(pending);
|
|
4666
4723
|
advanceHead();
|
|
4667
4724
|
notifyFlushWaiters();
|
|
4668
4725
|
};
|
|
4669
|
-
const
|
|
4726
|
+
const settleContiguousPrefix = () => {
|
|
4727
|
+
while (true) {
|
|
4728
|
+
const head = headEntry();
|
|
4729
|
+
if (head === void 0 || head.outcome === void 0) {
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4732
|
+
applyHeadOutcome(head, head.outcome);
|
|
4733
|
+
}
|
|
4734
|
+
};
|
|
4735
|
+
const recordOutcome = (pending, outcome) => {
|
|
4670
4736
|
const entry = pending.entry;
|
|
4671
|
-
if (
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
entrySeq: entry.entrySeq,
|
|
4676
|
-
kind: entry.kind,
|
|
4677
|
-
attempts: entry.attempts,
|
|
4678
|
-
error: failure.message
|
|
4679
|
-
});
|
|
4680
|
-
journal?.appendFail(entry.entrySeq);
|
|
4681
|
-
try {
|
|
4682
|
-
onPermanentFailure(entry, failure);
|
|
4683
|
-
} catch (error) {
|
|
4684
|
-
log2("outbox_permanent_failure_callback_failed", {
|
|
4737
|
+
if (outcome.kind === "fail") {
|
|
4738
|
+
if (entry.resolution === "must_ack") {
|
|
4739
|
+
totalPermanentFailures += 1;
|
|
4740
|
+
log2("outbox_permanent_failure", {
|
|
4685
4741
|
threadKey,
|
|
4686
4742
|
entrySeq: entry.entrySeq,
|
|
4687
|
-
|
|
4743
|
+
kind: entry.kind,
|
|
4744
|
+
attempts: entry.attempts,
|
|
4745
|
+
error: outcome.failure.message
|
|
4746
|
+
});
|
|
4747
|
+
} else {
|
|
4748
|
+
totalDroppedBestEffort += 1;
|
|
4749
|
+
log2("outbox_dropped_best_effort", {
|
|
4750
|
+
threadKey,
|
|
4751
|
+
entrySeq: entry.entrySeq,
|
|
4752
|
+
kind: entry.kind,
|
|
4753
|
+
attempts: entry.attempts,
|
|
4754
|
+
error: outcome.failure.message
|
|
4688
4755
|
});
|
|
4689
4756
|
}
|
|
4690
|
-
} else {
|
|
4691
|
-
totalDroppedBestEffort += 1;
|
|
4692
|
-
log2("outbox_dropped_best_effort", {
|
|
4693
|
-
threadKey,
|
|
4694
|
-
entrySeq: entry.entrySeq,
|
|
4695
|
-
kind: entry.kind,
|
|
4696
|
-
attempts: entry.attempts,
|
|
4697
|
-
error: failure.message
|
|
4698
|
-
});
|
|
4699
|
-
journal?.appendDrop(entry.entrySeq);
|
|
4700
4757
|
}
|
|
4701
|
-
|
|
4758
|
+
pending.outcome = outcome;
|
|
4759
|
+
pending.state = "queued";
|
|
4702
4760
|
};
|
|
4703
4761
|
const recordPushLatency = (latencyMs) => {
|
|
4704
4762
|
if (pushLatencyWindow.length < outboxPushLatencyWindowSize) {
|
|
@@ -4708,136 +4766,180 @@ function createThreadOutbox(options) {
|
|
|
4708
4766
|
}
|
|
4709
4767
|
pushLatencyNextIndex = (pushLatencyNextIndex + 1) % outboxPushLatencyWindowSize;
|
|
4710
4768
|
};
|
|
4711
|
-
const
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
}
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4769
|
+
const clearRetryTimer = (pending) => {
|
|
4770
|
+
if (pending.retryTimer !== void 0) {
|
|
4771
|
+
clearTimeout(pending.retryTimer);
|
|
4772
|
+
pending.retryTimer = void 0;
|
|
4773
|
+
}
|
|
4774
|
+
};
|
|
4775
|
+
const notifyDrainIdle = () => {
|
|
4776
|
+
if (inFlightCount > 0) {
|
|
4777
|
+
return;
|
|
4778
|
+
}
|
|
4779
|
+
const waiters = drainIdleWaiters;
|
|
4780
|
+
drainIdleWaiters = [];
|
|
4781
|
+
for (const resolve12 of waiters) {
|
|
4719
4782
|
resolve12();
|
|
4720
|
-
}
|
|
4721
|
-
}
|
|
4722
|
-
const
|
|
4783
|
+
}
|
|
4784
|
+
};
|
|
4785
|
+
const headInBackoff = () => {
|
|
4786
|
+
const head = headEntry();
|
|
4787
|
+
return head !== void 0 && head.outcome === void 0 && head.state === "queued" && head.retryTimer !== void 0;
|
|
4788
|
+
};
|
|
4789
|
+
const scheduleRetry = (pending, delayMs) => {
|
|
4790
|
+
clearRetryTimer(pending);
|
|
4791
|
+
pending.retryTimer = setTimeout(() => {
|
|
4792
|
+
pending.retryTimer = void 0;
|
|
4793
|
+
if (stopped || paused || pending.outcome !== void 0) {
|
|
4794
|
+
return;
|
|
4795
|
+
}
|
|
4796
|
+
pumpWindow();
|
|
4797
|
+
}, delayMs);
|
|
4798
|
+
pending.retryTimer?.unref?.();
|
|
4799
|
+
};
|
|
4800
|
+
const pushEntry = async (pending) => {
|
|
4723
4801
|
const entry = pending.entry;
|
|
4724
|
-
|
|
4802
|
+
const sendTurn = sendChainTail;
|
|
4803
|
+
let releaseSendChain = () => void 0;
|
|
4804
|
+
sendChainTail = new Promise((resolve12) => {
|
|
4805
|
+
releaseSendChain = resolve12;
|
|
4806
|
+
});
|
|
4807
|
+
let released = false;
|
|
4808
|
+
const releaseSend = () => {
|
|
4809
|
+
if (!released) {
|
|
4810
|
+
released = true;
|
|
4811
|
+
releaseSendChain();
|
|
4812
|
+
}
|
|
4813
|
+
};
|
|
4814
|
+
pending.state = "pushing";
|
|
4815
|
+
inFlightCount += 1;
|
|
4816
|
+
try {
|
|
4817
|
+
await sendTurn;
|
|
4818
|
+
if (stopped || paused || pending.outcome !== void 0) {
|
|
4819
|
+
pending.state = "queued";
|
|
4820
|
+
releaseSend();
|
|
4821
|
+
return;
|
|
4822
|
+
}
|
|
4725
4823
|
const builder = builders[entry.kind];
|
|
4726
4824
|
if (builder === void 0) {
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4825
|
+
releaseSend();
|
|
4826
|
+
recordOutcome(pending, {
|
|
4827
|
+
kind: "fail",
|
|
4828
|
+
failure: new Error(
|
|
4829
|
+
`no outbox builder registered for kind ${entry.kind}`
|
|
4830
|
+
)
|
|
4831
|
+
});
|
|
4731
4832
|
return;
|
|
4732
4833
|
}
|
|
4733
4834
|
let built;
|
|
4734
4835
|
try {
|
|
4735
4836
|
built = builder.build(entry.data, context);
|
|
4736
4837
|
} catch (error) {
|
|
4737
|
-
|
|
4838
|
+
releaseSend();
|
|
4839
|
+
recordOutcome(pending, { kind: "fail", failure: toError(error) });
|
|
4738
4840
|
return;
|
|
4739
4841
|
}
|
|
4740
4842
|
if ("skip" in built) {
|
|
4741
|
-
|
|
4843
|
+
releaseSend();
|
|
4844
|
+
recordOutcome(pending, { kind: "skip", reason: built.reason });
|
|
4845
|
+
return;
|
|
4846
|
+
}
|
|
4847
|
+
entry.attempts += 1;
|
|
4848
|
+
await pushBuilt(pending, built, releaseSend);
|
|
4849
|
+
} finally {
|
|
4850
|
+
inFlightCount -= 1;
|
|
4851
|
+
}
|
|
4852
|
+
};
|
|
4853
|
+
const pushBuilt = async (pending, built, releaseSend) => {
|
|
4854
|
+
const entry = pending.entry;
|
|
4855
|
+
const pushStartedAtMs = now();
|
|
4856
|
+
try {
|
|
4857
|
+
const reply = await options.push(
|
|
4858
|
+
built.event,
|
|
4859
|
+
built.payload,
|
|
4860
|
+
built.timeoutMs,
|
|
4861
|
+
releaseSend
|
|
4862
|
+
);
|
|
4863
|
+
releaseSend();
|
|
4864
|
+
recordPushLatency(Math.max(0, now() - pushStartedAtMs));
|
|
4865
|
+
totalAcked += 1;
|
|
4866
|
+
recordOutcome(pending, { kind: "ack", reply });
|
|
4867
|
+
} catch (error) {
|
|
4868
|
+
releaseSend();
|
|
4869
|
+
pending.state = "queued";
|
|
4870
|
+
const failure = toError(error);
|
|
4871
|
+
if (options.isRetryBudgetExempt?.(failure) === true) {
|
|
4872
|
+
entry.attempts -= 1;
|
|
4873
|
+
totalRetried += 1;
|
|
4874
|
+
log2("outbox_push_connection_wait", {
|
|
4742
4875
|
threadKey,
|
|
4743
4876
|
entrySeq: entry.entrySeq,
|
|
4744
4877
|
kind: entry.kind,
|
|
4745
|
-
|
|
4878
|
+
error: failure.message
|
|
4746
4879
|
});
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
try {
|
|
4756
|
-
reply = await options.push(built.event, built.payload, built.timeoutMs);
|
|
4757
|
-
} catch (error) {
|
|
4758
|
-
pending.state = "queued";
|
|
4759
|
-
const failure = toError(error);
|
|
4760
|
-
if (options.isRetryBudgetExempt?.(failure) === true) {
|
|
4761
|
-
entry.attempts -= 1;
|
|
4880
|
+
if (!stopped && !paused) {
|
|
4881
|
+
scheduleRetry(pending, budgetExemptRetryFallbackDelayMs);
|
|
4882
|
+
}
|
|
4883
|
+
} else {
|
|
4884
|
+
const delayMs = retryDelaysMs[entry.attempts - 1];
|
|
4885
|
+
if (delayMs === void 0) {
|
|
4886
|
+
recordOutcome(pending, { kind: "fail", failure });
|
|
4887
|
+
} else {
|
|
4762
4888
|
totalRetried += 1;
|
|
4763
|
-
log2("
|
|
4889
|
+
log2("outbox_push_retry", {
|
|
4764
4890
|
threadKey,
|
|
4765
4891
|
entrySeq: entry.entrySeq,
|
|
4766
4892
|
kind: entry.kind,
|
|
4893
|
+
attempt: entry.attempts,
|
|
4894
|
+
delayMs,
|
|
4767
4895
|
error: failure.message
|
|
4768
4896
|
});
|
|
4769
|
-
if (stopped
|
|
4770
|
-
|
|
4897
|
+
if (!stopped && !paused) {
|
|
4898
|
+
scheduleRetry(pending, delayMs);
|
|
4771
4899
|
}
|
|
4772
|
-
await sleep2(budgetExemptRetryFallbackDelayMs);
|
|
4773
|
-
if (stopped || paused) {
|
|
4774
|
-
return;
|
|
4775
|
-
}
|
|
4776
|
-
continue;
|
|
4777
4900
|
}
|
|
4778
|
-
const delayMs = retryDelaysMs[entry.attempts - 1];
|
|
4779
|
-
if (delayMs === void 0) {
|
|
4780
|
-
settleFailure(pending, failure);
|
|
4781
|
-
return;
|
|
4782
|
-
}
|
|
4783
|
-
totalRetried += 1;
|
|
4784
|
-
log2("outbox_push_retry", {
|
|
4785
|
-
threadKey,
|
|
4786
|
-
entrySeq: entry.entrySeq,
|
|
4787
|
-
kind: entry.kind,
|
|
4788
|
-
attempt: entry.attempts,
|
|
4789
|
-
delayMs,
|
|
4790
|
-
error: failure.message
|
|
4791
|
-
});
|
|
4792
|
-
await sleep2(delayMs);
|
|
4793
|
-
if (stopped || paused) {
|
|
4794
|
-
return;
|
|
4795
|
-
}
|
|
4796
|
-
continue;
|
|
4797
4901
|
}
|
|
4798
|
-
pending.state = "queued";
|
|
4799
|
-
try {
|
|
4800
|
-
builder.onReply?.(entry.data, reply, context);
|
|
4801
|
-
} catch (error) {
|
|
4802
|
-
log2("outbox_on_reply_failed", {
|
|
4803
|
-
threadKey,
|
|
4804
|
-
entrySeq: entry.entrySeq,
|
|
4805
|
-
kind: entry.kind,
|
|
4806
|
-
error: errorMessage2(error)
|
|
4807
|
-
});
|
|
4808
|
-
}
|
|
4809
|
-
totalAcked += 1;
|
|
4810
|
-
recordPushLatency(Math.max(0, now() - pushStartedAtMs));
|
|
4811
|
-
journal?.appendAck(entry.entrySeq);
|
|
4812
|
-
settleHead(pending);
|
|
4813
|
-
return;
|
|
4814
4902
|
}
|
|
4815
4903
|
};
|
|
4816
|
-
const
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4904
|
+
const pumpWindow = () => {
|
|
4905
|
+
settleContiguousPrefix();
|
|
4906
|
+
if (stopped || paused) {
|
|
4907
|
+
notifyDrainIdle();
|
|
4908
|
+
return;
|
|
4909
|
+
}
|
|
4910
|
+
if (headInBackoff()) {
|
|
4911
|
+
notifyDrainIdle();
|
|
4912
|
+
return;
|
|
4913
|
+
}
|
|
4914
|
+
for (let index = headIndex; index < queue.length && inFlightCount < maxInFlightPushes; index += 1) {
|
|
4915
|
+
const pending = queue[index];
|
|
4916
|
+
if (pending === void 0 || pending.outcome !== void 0 || pending.state === "pushing" || pending.retryTimer !== void 0) {
|
|
4917
|
+
continue;
|
|
4822
4918
|
}
|
|
4823
|
-
|
|
4919
|
+
void pushEntry(pending).then(
|
|
4920
|
+
() => pumpWindow(),
|
|
4921
|
+
(error) => {
|
|
4922
|
+
log2("outbox_drain_loop_error", {
|
|
4923
|
+
threadKey,
|
|
4924
|
+
error: errorMessage2(error)
|
|
4925
|
+
});
|
|
4926
|
+
pumpWindow();
|
|
4927
|
+
}
|
|
4928
|
+
);
|
|
4824
4929
|
}
|
|
4930
|
+
notifyDrainIdle();
|
|
4825
4931
|
};
|
|
4826
4932
|
const ensureDraining = () => {
|
|
4827
|
-
if (
|
|
4933
|
+
if (paused || stopped) {
|
|
4828
4934
|
return;
|
|
4829
4935
|
}
|
|
4830
4936
|
if (headEntry() === void 0) {
|
|
4831
4937
|
return;
|
|
4832
4938
|
}
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
});
|
|
4838
|
-
}).finally(() => {
|
|
4839
|
-
drainPromise = void 0;
|
|
4840
|
-
ensureDraining();
|
|
4939
|
+
void Promise.resolve().then(() => {
|
|
4940
|
+
if (!stopped && !paused) {
|
|
4941
|
+
pumpWindow();
|
|
4942
|
+
}
|
|
4841
4943
|
});
|
|
4842
4944
|
};
|
|
4843
4945
|
const flush = (timeoutMs) => {
|
|
@@ -4881,10 +4983,16 @@ function createThreadOutbox(options) {
|
|
|
4881
4983
|
});
|
|
4882
4984
|
}
|
|
4883
4985
|
stopped = true;
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4986
|
+
for (let index = headIndex; index < queue.length; index += 1) {
|
|
4987
|
+
clearRetryTimer(queue[index]);
|
|
4988
|
+
}
|
|
4989
|
+
if (inFlightCount > 0) {
|
|
4990
|
+
await Promise.race([
|
|
4991
|
+
new Promise((resolve12) => {
|
|
4992
|
+
drainIdleWaiters.push(resolve12);
|
|
4993
|
+
}),
|
|
4994
|
+
sleepUncancellable(closeDrainGraceMs)
|
|
4995
|
+
]);
|
|
4888
4996
|
}
|
|
4889
4997
|
for (const waiter of flushWaiters.splice(0)) {
|
|
4890
4998
|
if (waiter.timer !== void 0) {
|
|
@@ -4903,7 +5011,7 @@ function createThreadOutbox(options) {
|
|
|
4903
5011
|
if (input.coalesceKey !== void 0) {
|
|
4904
5012
|
const key = coalesceMapKey(input.kind, input.coalesceKey);
|
|
4905
5013
|
const existing = coalescible.get(key);
|
|
4906
|
-
if (existing !== void 0 && existing.state === "queued") {
|
|
5014
|
+
if (existing !== void 0 && existing.state === "queued" && existing.outcome === void 0) {
|
|
4907
5015
|
existing.entry.data = coalesceEntryData(
|
|
4908
5016
|
builders[input.kind],
|
|
4909
5017
|
existing.entry,
|
|
@@ -4948,9 +5056,9 @@ function createThreadOutbox(options) {
|
|
|
4948
5056
|
const pending = queue[index];
|
|
4949
5057
|
if (pending !== void 0) {
|
|
4950
5058
|
pending.entry.attempts = 0;
|
|
5059
|
+
clearRetryTimer(pending);
|
|
4951
5060
|
}
|
|
4952
5061
|
}
|
|
4953
|
-
wakeSleep?.();
|
|
4954
5062
|
ensureDraining();
|
|
4955
5063
|
},
|
|
4956
5064
|
metrics: () => {
|
|
@@ -6619,6 +6727,7 @@ function createSessionPipeline(host) {
|
|
|
6619
6727
|
builders,
|
|
6620
6728
|
...host.persistDir === void 0 ? {} : { persistDir: host.persistDir },
|
|
6621
6729
|
...host.outboxRetryDelaysMs === void 0 ? {} : { retryDelaysMs: [...host.outboxRetryDelaysMs] },
|
|
6730
|
+
maxInFlightPushes: host.outboxMaxInFlightPushes ?? defaultOutboxMaxInFlightPushes,
|
|
6622
6731
|
...host.journalRecoveryMaxAgeMs === void 0 ? {} : { journalRecoveryMaxAgeMs: host.journalRecoveryMaxAgeMs },
|
|
6623
6732
|
// Budget semantics (pre-merge review finding 6): wait_for_connection
|
|
6624
6733
|
// failures never consume retry budget - the push never reached the
|
|
@@ -7067,7 +7176,7 @@ function sleep(ms) {
|
|
|
7067
7176
|
setTimeout(resolve12, ms);
|
|
7068
7177
|
});
|
|
7069
7178
|
}
|
|
7070
|
-
var defaultSnapshotRetryDelaysMs, toolCallOutputTypes, toolCallItemTypes;
|
|
7179
|
+
var defaultSnapshotRetryDelaysMs, defaultOutboxMaxInFlightPushes, toolCallOutputTypes, toolCallItemTypes;
|
|
7071
7180
|
var init_integration = __esm({
|
|
7072
7181
|
"src/pipeline/integration.ts"() {
|
|
7073
7182
|
"use strict";
|
|
@@ -7081,6 +7190,7 @@ var init_integration = __esm({
|
|
|
7081
7190
|
init_threadEventLoop();
|
|
7082
7191
|
init_transition();
|
|
7083
7192
|
defaultSnapshotRetryDelaysMs = [1e3, 5e3];
|
|
7193
|
+
defaultOutboxMaxInFlightPushes = 16;
|
|
7084
7194
|
toolCallOutputTypes = /* @__PURE__ */ new Map([
|
|
7085
7195
|
["function_call", "function_call_output"],
|
|
7086
7196
|
["custom_tool_call", "custom_tool_call_output"]
|
|
@@ -8739,6 +8849,14 @@ function codexStartRequestTimeoutMs() {
|
|
|
8739
8849
|
const parsed = Number.parseInt(raw, 10);
|
|
8740
8850
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : CODEX_START_REQUEST_TIMEOUT_MS;
|
|
8741
8851
|
}
|
|
8852
|
+
function outboxMaxInFlightPushesOverride() {
|
|
8853
|
+
const raw = process.env.LINZUMI_OUTBOX_MAX_INFLIGHT_PUSHES?.trim();
|
|
8854
|
+
if (raw === void 0 || raw === "") {
|
|
8855
|
+
return void 0;
|
|
8856
|
+
}
|
|
8857
|
+
const parsed = Number.parseInt(raw, 10);
|
|
8858
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
8859
|
+
}
|
|
8742
8860
|
function isCodexStartRequestTimeoutError(error) {
|
|
8743
8861
|
return error instanceof CodexStartRequestTimeoutError;
|
|
8744
8862
|
}
|
|
@@ -8983,7 +9101,7 @@ function createChannelSessionPipeline(args, state, payloadContext) {
|
|
|
8983
9101
|
instanceId: args.instanceId,
|
|
8984
9102
|
threadKey,
|
|
8985
9103
|
persistDir: args.options.pipelinePersistDir,
|
|
8986
|
-
push: (event, payload) => pipelinePush(args, state, event, payload),
|
|
9104
|
+
push: (event, payload, _timeoutMs, onSent) => pipelinePush(args, state, event, payload, onSent),
|
|
8987
9105
|
wire: () => ({
|
|
8988
9106
|
workspaceSlug: session.workspaceSlug,
|
|
8989
9107
|
channelSlug: session.channelSlug,
|
|
@@ -9014,15 +9132,16 @@ function createChannelSessionPipeline(args, state, payloadContext) {
|
|
|
9014
9132
|
args.log(event, fields);
|
|
9015
9133
|
},
|
|
9016
9134
|
outboxRetryDelaysMs: args.options.pipelineOutboxRetryDelaysMs,
|
|
9135
|
+
outboxMaxInFlightPushes: outboxMaxInFlightPushesOverride(),
|
|
9017
9136
|
snapshotRetryDelaysMs: args.options.pipelineSnapshotRetryDelaysMs,
|
|
9018
9137
|
statsIntervalMs: args.options.pipelineStatsIntervalMs,
|
|
9019
9138
|
turnStalledThresholdMs: args.options.pipelineTurnStalledThresholdMs
|
|
9020
9139
|
});
|
|
9021
9140
|
}
|
|
9022
|
-
async function pipelinePush(args, state, event, payload) {
|
|
9141
|
+
async function pipelinePush(args, state, event, payload, onSent) {
|
|
9023
9142
|
const prepared = await preparePipelinePushPayload(args, event, payload);
|
|
9024
9143
|
try {
|
|
9025
|
-
return await pushOk2(args.kandan, args.topic, event, prepared);
|
|
9144
|
+
return await pushOk2(args.kandan, args.topic, event, prepared, onSent);
|
|
9026
9145
|
} catch (error) {
|
|
9027
9146
|
if (isConnectionLostPushError(error)) {
|
|
9028
9147
|
state.pipeline?.pause();
|
|
@@ -12060,8 +12179,8 @@ function developerInstructionsForThreadStart(options) {
|
|
|
12060
12179
|
})
|
|
12061
12180
|
};
|
|
12062
12181
|
}
|
|
12063
|
-
async function pushOk2(kandan, topic, event, payload) {
|
|
12064
|
-
const reply = await kandan.push(topic, event, payload);
|
|
12182
|
+
async function pushOk2(kandan, topic, event, payload, onSent) {
|
|
12183
|
+
const reply = await kandan.push(topic, event, payload, onSent);
|
|
12065
12184
|
if (isJsonObject(reply) && reply.status === "ok" && isJsonObject(reply.response)) {
|
|
12066
12185
|
return reply.response;
|
|
12067
12186
|
}
|
|
@@ -12393,10 +12512,10 @@ function createClaudeCodeSessionPipeline(host) {
|
|
|
12393
12512
|
let lastSourceSeq;
|
|
12394
12513
|
let lastUsage;
|
|
12395
12514
|
let pipeline2;
|
|
12396
|
-
const push = async (event, payload, timeoutMs) => {
|
|
12515
|
+
const push = async (event, payload, timeoutMs, onSent) => {
|
|
12397
12516
|
const prepared = host.preparePush === void 0 ? payload : await host.preparePush(event, payload);
|
|
12398
12517
|
try {
|
|
12399
|
-
return await host.push(event, prepared, timeoutMs);
|
|
12518
|
+
return await host.push(event, prepared, timeoutMs, onSent);
|
|
12400
12519
|
} catch (error) {
|
|
12401
12520
|
if (isConnectionLostPushError(error)) {
|
|
12402
12521
|
pipeline2.pause();
|
|
@@ -19829,7 +19948,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
19829
19948
|
var init_version = __esm({
|
|
19830
19949
|
"src/version.ts"() {
|
|
19831
19950
|
"use strict";
|
|
19832
|
-
linzumiCliVersion = "0.0.
|
|
19951
|
+
linzumiCliVersion = "0.0.103-beta";
|
|
19833
19952
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
19834
19953
|
}
|
|
19835
19954
|
});
|
|
@@ -21102,6 +21221,8 @@ function rateLimitSummaryKey(summary) {
|
|
|
21102
21221
|
}
|
|
21103
21222
|
function formatRunnerConsoleEvent(event, payload) {
|
|
21104
21223
|
switch (event) {
|
|
21224
|
+
case "runner.starting":
|
|
21225
|
+
return runnerWelcomeLine;
|
|
21105
21226
|
case "runner.instance_started":
|
|
21106
21227
|
return connectedRunnerMessage(payload);
|
|
21107
21228
|
case "runner.replaced":
|
|
@@ -22043,11 +22164,12 @@ function stringValue5(value) {
|
|
|
22043
22164
|
function numberValue(value) {
|
|
22044
22165
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
22045
22166
|
}
|
|
22046
|
-
var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey, dashboardTableColumns, runnerWelcomeHeaderLines, runnerWelcomeCompactHeaderLines, minimumVisibleTuiTableRows, redrawScreen, keyboardState, tickerState, tuiState;
|
|
22167
|
+
var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey, dashboardTableColumns, runnerWelcomeLine, runnerWelcomeHeaderLines, runnerWelcomeCompactHeaderLines, minimumVisibleTuiTableRows, redrawScreen, keyboardState, tickerState, tuiState;
|
|
22047
22168
|
var init_runnerConsoleReporter = __esm({
|
|
22048
22169
|
"src/runnerConsoleReporter.ts"() {
|
|
22049
22170
|
"use strict";
|
|
22050
22171
|
init_blessedTputSetulcShim();
|
|
22172
|
+
init_version();
|
|
22051
22173
|
dashboardState = {
|
|
22052
22174
|
jobs: /* @__PURE__ */ new Map(),
|
|
22053
22175
|
discovery: /* @__PURE__ */ new Map(),
|
|
@@ -22081,6 +22203,7 @@ var init_runnerConsoleReporter = __esm({
|
|
|
22081
22203
|
{ width: 12 },
|
|
22082
22204
|
{ width: 24 }
|
|
22083
22205
|
];
|
|
22206
|
+
runnerWelcomeLine = `Welcome to the Linzumi runner (version ${linzumiCliVersion}).`;
|
|
22084
22207
|
runnerWelcomeHeaderLines = [
|
|
22085
22208
|
"\u2593\u2593\u2557 \u2593\u2593\u2557\u2593\u2593\u2593\u2557 \u2593\u2593\u2557\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2557\u2593\u2593\u2557 \u2593\u2593\u2557\u2593\u2593\u2593\u2557 \u2593\u2593\u2593\u2557\u2593\u2593\u2557",
|
|
22086
22209
|
"\u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2593\u2593\u2557 \u2593\u2593\u2551\u255A\u2550\u2550\u2593\u2593\u2593\u2554\u255D\u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2593\u2593\u2557 \u2593\u2593\u2593\u2593\u2551\u2593\u2593\u2551",
|
|
@@ -22097,7 +22220,7 @@ var init_runnerConsoleReporter = __esm({
|
|
|
22097
22220
|
" \u2551\u2551 \"/` ___ ;_________\u2551\u2551_.'",
|
|
22098
22221
|
" \u2551\u2551 ` ^^ ^^ \u2551\u2551",
|
|
22099
22222
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2568\u2568\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\xB7\xB7\u2500\u2500\u2500\u2500\xB7\xB7\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2568\u2568\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
22100
|
-
|
|
22223
|
+
runnerWelcomeLine,
|
|
22101
22224
|
"Codex is running on this computer and connected to Linzumi.",
|
|
22102
22225
|
"Active Codex and Claude Code jobs appear below as they run.",
|
|
22103
22226
|
"You can return to the Linzumi app now."
|
|
@@ -22106,7 +22229,7 @@ var init_runnerConsoleReporter = __esm({
|
|
|
22106
22229
|
"\u2593\u2593\u2557 LINZUMI RUNNER \u2593\u2593\u2557",
|
|
22107
22230
|
"\u2551\u2551 ()-().----. \u2551\u2551",
|
|
22108
22231
|
"\u2568\u2568\u2500\u2500 connected to Codex and Linzumi \u2500\u2500\u2568\u2568",
|
|
22109
|
-
|
|
22232
|
+
runnerWelcomeLine,
|
|
22110
22233
|
"Codex is running on this computer and connected to Linzumi.",
|
|
22111
22234
|
"Active Codex and Claude Code jobs appear below as they run.",
|
|
22112
22235
|
"You can return to the Linzumi app now."
|
|
@@ -24756,6 +24879,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24756
24879
|
);
|
|
24757
24880
|
threadRunnerProcesses.clear();
|
|
24758
24881
|
});
|
|
24882
|
+
const threadModelProviderBindings = /* @__PURE__ */ new Map();
|
|
24759
24883
|
const threadRebuildSerializer = createThreadRebuildSerializer();
|
|
24760
24884
|
const evictStaleDynamicChannelSession = async (codexThreadId, expectClient) => {
|
|
24761
24885
|
const session = dynamicChannelSessions.get(codexThreadId);
|
|
@@ -24843,6 +24967,14 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24843
24967
|
rebuildArgs.codexThreadId
|
|
24844
24968
|
);
|
|
24845
24969
|
let rebuilt = false;
|
|
24970
|
+
const rebuildProviderBinding = threadModelProviderBindings.get(rebuildArgs.kandanThreadId) ?? // Belt-and-suspenders: if the persisted map missed this thread
|
|
24971
|
+
// but the in-scope establishing `control` still carries the
|
|
24972
|
+
// provider (the server now re-injects it on reconnect_thread),
|
|
24973
|
+
// forward that directly so the rebuild never strands a wafer turn.
|
|
24974
|
+
(control.modelProvider !== void 0 || control.llmProxy !== void 0 ? {
|
|
24975
|
+
...control.modelProvider === void 0 ? {} : { modelProvider: control.modelProvider },
|
|
24976
|
+
...control.llmProxy === void 0 ? {} : { llmProxy: control.llmProxy }
|
|
24977
|
+
} : void 0);
|
|
24846
24978
|
for (const message of rebuildArgs.pendingMessages) {
|
|
24847
24979
|
const reconnectControl = {
|
|
24848
24980
|
type: "reconnect_thread",
|
|
@@ -24857,6 +24989,14 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24857
24989
|
// control built above at channelSession.linzumiContext).
|
|
24858
24990
|
...control.linzumiContext === void 0 ? {} : { linzumiContext: control.linzumiContext },
|
|
24859
24991
|
...integerValue(control.rootSeq) === void 0 ? {} : { rootSeq: integerValue(control.rootSeq) },
|
|
24992
|
+
// RESUME-CRITICAL: replay the persisted model-provider binding
|
|
24993
|
+
// (the in-scope `control` is a reconnect_thread that no longer
|
|
24994
|
+
// carries it) so the respawned wafer/GLM worker can load the
|
|
24995
|
+
// `linzumi` provider and resume instead of stranding the turn.
|
|
24996
|
+
...rebuildProviderBinding?.modelProvider === void 0 ? {} : {
|
|
24997
|
+
modelProvider: rebuildProviderBinding.modelProvider
|
|
24998
|
+
},
|
|
24999
|
+
...rebuildProviderBinding?.llmProxy === void 0 ? {} : { llmProxy: rebuildProviderBinding.llmProxy },
|
|
24860
25000
|
sourceSeq: message.seq,
|
|
24861
25001
|
workDescription: message.body,
|
|
24862
25002
|
...runtimeSettings.model === void 0 ? {} : { model: runtimeSettings.model },
|
|
@@ -25118,6 +25258,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25118
25258
|
return void 0;
|
|
25119
25259
|
}
|
|
25120
25260
|
const providerResolution = resolveControlCodexModelProvider(control);
|
|
25261
|
+
if (control.modelProvider !== void 0 || control.llmProxy !== void 0) {
|
|
25262
|
+
threadModelProviderBindings.set(kandanThreadId, {
|
|
25263
|
+
...control.modelProvider === void 0 ? {} : { modelProvider: control.modelProvider },
|
|
25264
|
+
...control.llmProxy === void 0 ? {} : { llmProxy: control.llmProxy }
|
|
25265
|
+
});
|
|
25266
|
+
}
|
|
25121
25267
|
const providerResolutionError = codexModelProviderResolutionError(providerResolution);
|
|
25122
25268
|
if (providerResolutionError !== void 0) {
|
|
25123
25269
|
return {
|
|
@@ -28425,8 +28571,8 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
28425
28571
|
codexThreadId: activeSessionId ?? args.resumeSessionId,
|
|
28426
28572
|
rootSeq
|
|
28427
28573
|
}),
|
|
28428
|
-
push: async (event, payload) => {
|
|
28429
|
-
const reply = await args.kandan.push(args.topic, event, payload);
|
|
28574
|
+
push: async (event, payload, _timeoutMs, onSent) => {
|
|
28575
|
+
const reply = await args.kandan.push(args.topic, event, payload, onSent);
|
|
28430
28576
|
if (isJsonObject(reply) && reply.status === "ok") {
|
|
28431
28577
|
return reply;
|
|
28432
28578
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linzumi/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Linzumi CLI
|
|
3
|
+
"version": "0.0.103-beta",
|
|
4
|
+
"description": "Linzumi CLI \u2014 point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"linzumi": "bin/linzumi.js"
|