@openscout/scout 0.2.58 → 0.2.61
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/client/assets/{arc.es-Bkj9Bkvx.js → arc.es-DgeMPL-I.js} +1 -1
- package/dist/client/assets/index-25TJx0iN.js +140 -0
- package/dist/client/assets/index-B1kglsyi.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/main.mjs +2576 -4579
- package/dist/pair-supervisor.mjs +1127 -2405
- package/dist/scout-control-plane-web.mjs +798 -2939
- package/package.json +2 -2
- package/dist/client/assets/index-BDyu05Hi.js +0 -140
- package/dist/client/assets/index-pbcZpgCR.css +0 -1
|
@@ -5501,116 +5501,6 @@ var init_mesh = () => {};
|
|
|
5501
5501
|
|
|
5502
5502
|
// packages/protocol/dist/conversations.js
|
|
5503
5503
|
var init_conversations = () => {};
|
|
5504
|
-
|
|
5505
|
-
// packages/protocol/dist/collaboration.js
|
|
5506
|
-
function isQuestionTerminalState(state) {
|
|
5507
|
-
return state === "closed" || state === "declined";
|
|
5508
|
-
}
|
|
5509
|
-
function isWorkItemTerminalState(state) {
|
|
5510
|
-
return state === "done" || state === "cancelled";
|
|
5511
|
-
}
|
|
5512
|
-
function collaborationRequiresNextMoveOwner(record) {
|
|
5513
|
-
if (record.kind === "question") {
|
|
5514
|
-
return !isQuestionTerminalState(record.state);
|
|
5515
|
-
}
|
|
5516
|
-
return !isWorkItemTerminalState(record.state);
|
|
5517
|
-
}
|
|
5518
|
-
function collaborationRequiresOwner(record) {
|
|
5519
|
-
return record.kind === "work_item" && !isWorkItemTerminalState(record.state);
|
|
5520
|
-
}
|
|
5521
|
-
function collaborationRequiresWaitingOn(record) {
|
|
5522
|
-
return record.kind === "work_item" && record.state === "waiting";
|
|
5523
|
-
}
|
|
5524
|
-
function collaborationRequiresAcceptance(record) {
|
|
5525
|
-
if (record.acceptanceState === "none") {
|
|
5526
|
-
return false;
|
|
5527
|
-
}
|
|
5528
|
-
if (record.kind === "question") {
|
|
5529
|
-
return Boolean(record.askedById && record.askedOfId);
|
|
5530
|
-
}
|
|
5531
|
-
return Boolean(record.requestedById);
|
|
5532
|
-
}
|
|
5533
|
-
function validateCollaborationRecord(record) {
|
|
5534
|
-
const errors = [];
|
|
5535
|
-
if (!record.id.trim()) {
|
|
5536
|
-
errors.push("collaboration record id is required");
|
|
5537
|
-
}
|
|
5538
|
-
if (!record.title.trim()) {
|
|
5539
|
-
errors.push("collaboration title is required");
|
|
5540
|
-
}
|
|
5541
|
-
if (!record.createdById.trim()) {
|
|
5542
|
-
errors.push("createdById is required");
|
|
5543
|
-
}
|
|
5544
|
-
if (record.parentId && record.parentId === record.id) {
|
|
5545
|
-
errors.push("parentId cannot reference the record itself");
|
|
5546
|
-
}
|
|
5547
|
-
if (record.createdAt > record.updatedAt) {
|
|
5548
|
-
errors.push("updatedAt must be greater than or equal to createdAt");
|
|
5549
|
-
}
|
|
5550
|
-
if (collaborationRequiresOwner(record) && !record.ownerId) {
|
|
5551
|
-
errors.push("non-terminal work items require ownerId");
|
|
5552
|
-
}
|
|
5553
|
-
if (collaborationRequiresNextMoveOwner(record) && !record.nextMoveOwnerId) {
|
|
5554
|
-
errors.push("non-terminal collaboration records require nextMoveOwnerId");
|
|
5555
|
-
}
|
|
5556
|
-
if (record.kind === "work_item" && collaborationRequiresWaitingOn(record) && !record.waitingOn) {
|
|
5557
|
-
errors.push("waiting work items require waitingOn");
|
|
5558
|
-
}
|
|
5559
|
-
if (record.kind === "question") {
|
|
5560
|
-
if (record.spawnedWorkItemId && record.spawnedWorkItemId === record.id) {
|
|
5561
|
-
errors.push("question spawnedWorkItemId cannot reference the question itself");
|
|
5562
|
-
}
|
|
5563
|
-
} else if (record.waitingOn?.targetId && record.waitingOn.targetId === record.id) {
|
|
5564
|
-
errors.push("waitingOn.targetId cannot reference the work item itself");
|
|
5565
|
-
}
|
|
5566
|
-
if (record.acceptanceState !== "none" && !collaborationRequiresAcceptance(record)) {
|
|
5567
|
-
errors.push("acceptanceState requires the corresponding requester and reviewer identities");
|
|
5568
|
-
}
|
|
5569
|
-
return errors;
|
|
5570
|
-
}
|
|
5571
|
-
function assertValidCollaborationRecord(record) {
|
|
5572
|
-
const errors = validateCollaborationRecord(record);
|
|
5573
|
-
if (errors.length > 0) {
|
|
5574
|
-
throw new Error(errors.join("; "));
|
|
5575
|
-
}
|
|
5576
|
-
}
|
|
5577
|
-
function validateCollaborationEvent(event, record) {
|
|
5578
|
-
const errors = [];
|
|
5579
|
-
if (!event.id.trim()) {
|
|
5580
|
-
errors.push("collaboration event id is required");
|
|
5581
|
-
}
|
|
5582
|
-
if (!event.recordId.trim()) {
|
|
5583
|
-
errors.push("collaboration event recordId is required");
|
|
5584
|
-
}
|
|
5585
|
-
if (!event.actorId.trim()) {
|
|
5586
|
-
errors.push("collaboration event actorId is required");
|
|
5587
|
-
}
|
|
5588
|
-
if (record) {
|
|
5589
|
-
if (record.id !== event.recordId) {
|
|
5590
|
-
errors.push("collaboration event recordId does not match the target record");
|
|
5591
|
-
}
|
|
5592
|
-
if (record.kind !== event.recordKind) {
|
|
5593
|
-
errors.push("collaboration event recordKind does not match the target record");
|
|
5594
|
-
}
|
|
5595
|
-
}
|
|
5596
|
-
if (event.kind === "answered" && event.recordKind !== "question") {
|
|
5597
|
-
errors.push("answered events only apply to questions");
|
|
5598
|
-
}
|
|
5599
|
-
if (event.kind === "declined" && event.recordKind !== "question") {
|
|
5600
|
-
errors.push("declined events only apply to questions");
|
|
5601
|
-
}
|
|
5602
|
-
if ((event.kind === "waiting" || event.kind === "progressed" || event.kind === "review_requested" || event.kind === "done" || event.kind === "cancelled") && event.recordKind !== "work_item") {
|
|
5603
|
-
errors.push(`${event.kind} events only apply to work items`);
|
|
5604
|
-
}
|
|
5605
|
-
return errors;
|
|
5606
|
-
}
|
|
5607
|
-
function assertValidCollaborationEvent(event, record) {
|
|
5608
|
-
const errors = validateCollaborationEvent(event, record);
|
|
5609
|
-
if (errors.length > 0) {
|
|
5610
|
-
throw new Error(errors.join("; "));
|
|
5611
|
-
}
|
|
5612
|
-
}
|
|
5613
|
-
|
|
5614
5504
|
// packages/protocol/dist/messages.js
|
|
5615
5505
|
var init_messages = () => {};
|
|
5616
5506
|
|
|
@@ -8902,8 +8792,107 @@ var init_claude_stream_json = __esm(() => {
|
|
|
8902
8792
|
|
|
8903
8793
|
// packages/runtime/src/codex-app-server.ts
|
|
8904
8794
|
import { spawn as spawn4 } from "child_process";
|
|
8905
|
-
import {
|
|
8795
|
+
import { constants as constants4 } from "fs";
|
|
8796
|
+
import { access as access3, appendFile as appendFile3, mkdir as mkdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
8906
8797
|
import { delimiter as delimiter3, join as join14 } from "path";
|
|
8798
|
+
function normalizeCodexModelValue(value) {
|
|
8799
|
+
const trimmed = value?.trim();
|
|
8800
|
+
return trimmed ? trimmed : null;
|
|
8801
|
+
}
|
|
8802
|
+
function encodeCodexModelConfig(model) {
|
|
8803
|
+
return `model=${JSON.stringify(model)}`;
|
|
8804
|
+
}
|
|
8805
|
+
function parseCodexModelConfig(value) {
|
|
8806
|
+
const trimmed = value?.trim();
|
|
8807
|
+
if (!trimmed) {
|
|
8808
|
+
return null;
|
|
8809
|
+
}
|
|
8810
|
+
const separatorIndex = trimmed.indexOf("=");
|
|
8811
|
+
if (separatorIndex <= 0) {
|
|
8812
|
+
return null;
|
|
8813
|
+
}
|
|
8814
|
+
const key = trimmed.slice(0, separatorIndex).trim();
|
|
8815
|
+
if (key !== "model") {
|
|
8816
|
+
return null;
|
|
8817
|
+
}
|
|
8818
|
+
const rawValue = trimmed.slice(separatorIndex + 1).trim();
|
|
8819
|
+
if (!rawValue) {
|
|
8820
|
+
return null;
|
|
8821
|
+
}
|
|
8822
|
+
if (rawValue.startsWith('"') && rawValue.endsWith('"') || rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
8823
|
+
return rawValue.slice(1, -1) || null;
|
|
8824
|
+
}
|
|
8825
|
+
return rawValue;
|
|
8826
|
+
}
|
|
8827
|
+
function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
8828
|
+
const args = Array.isArray(launchArgs) ? launchArgs.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
8829
|
+
const normalized = [];
|
|
8830
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
8831
|
+
const current = args[index] ?? "";
|
|
8832
|
+
if (current === "--model" || current === "-m") {
|
|
8833
|
+
const model = normalizeCodexModelValue(args[index + 1]);
|
|
8834
|
+
if (model) {
|
|
8835
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
8836
|
+
index += 1;
|
|
8837
|
+
continue;
|
|
8838
|
+
}
|
|
8839
|
+
normalized.push(current);
|
|
8840
|
+
continue;
|
|
8841
|
+
}
|
|
8842
|
+
if (current.startsWith("--model=")) {
|
|
8843
|
+
const model = normalizeCodexModelValue(current.slice("--model=".length));
|
|
8844
|
+
if (model) {
|
|
8845
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
8846
|
+
continue;
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
if (current.startsWith("-m=")) {
|
|
8850
|
+
const model = normalizeCodexModelValue(current.slice(3));
|
|
8851
|
+
if (model) {
|
|
8852
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
8853
|
+
continue;
|
|
8854
|
+
}
|
|
8855
|
+
}
|
|
8856
|
+
if (current === "-c" || current === "--config") {
|
|
8857
|
+
const next = args[index + 1];
|
|
8858
|
+
if (typeof next === "string") {
|
|
8859
|
+
const model = parseCodexModelConfig(next);
|
|
8860
|
+
normalized.push(current === "--config" ? "--config" : "-c", model ? encodeCodexModelConfig(model) : next);
|
|
8861
|
+
index += 1;
|
|
8862
|
+
continue;
|
|
8863
|
+
}
|
|
8864
|
+
}
|
|
8865
|
+
if (current.startsWith("--config=")) {
|
|
8866
|
+
const value = current.slice("--config=".length);
|
|
8867
|
+
const model = parseCodexModelConfig(value);
|
|
8868
|
+
normalized.push(model ? `--config=${encodeCodexModelConfig(model)}` : current);
|
|
8869
|
+
continue;
|
|
8870
|
+
}
|
|
8871
|
+
normalized.push(current);
|
|
8872
|
+
}
|
|
8873
|
+
return normalized;
|
|
8874
|
+
}
|
|
8875
|
+
function readCodexAppServerModelFromLaunchArgs(launchArgs) {
|
|
8876
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
8877
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
8878
|
+
const current = normalized[index] ?? "";
|
|
8879
|
+
if (current === "-c" || current === "--config") {
|
|
8880
|
+
const model = parseCodexModelConfig(normalized[index + 1]);
|
|
8881
|
+
if (model) {
|
|
8882
|
+
return model;
|
|
8883
|
+
}
|
|
8884
|
+
index += 1;
|
|
8885
|
+
continue;
|
|
8886
|
+
}
|
|
8887
|
+
if (current.startsWith("--config=")) {
|
|
8888
|
+
const model = parseCodexModelConfig(current.slice("--config=".length));
|
|
8889
|
+
if (model) {
|
|
8890
|
+
return model;
|
|
8891
|
+
}
|
|
8892
|
+
}
|
|
8893
|
+
}
|
|
8894
|
+
return null;
|
|
8895
|
+
}
|
|
8907
8896
|
function isCodexThreadGlobalMessage(message) {
|
|
8908
8897
|
const result = message.result;
|
|
8909
8898
|
const thread = result?.thread;
|
|
@@ -9706,6 +9695,13 @@ function isMissingCodexRolloutError2(error) {
|
|
|
9706
9695
|
const message = errorMessage3(error).toLowerCase();
|
|
9707
9696
|
return message.includes("no rollout found for thread id");
|
|
9708
9697
|
}
|
|
9698
|
+
function resolveCodexCompletionGraceMs() {
|
|
9699
|
+
const parsed = Number.parseInt(process.env.OPENSCOUT_CODEX_COMPLETION_GRACE_MS ?? "", 10);
|
|
9700
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
9701
|
+
return parsed;
|
|
9702
|
+
}
|
|
9703
|
+
return 60000;
|
|
9704
|
+
}
|
|
9709
9705
|
|
|
9710
9706
|
class CodexAppServerSession {
|
|
9711
9707
|
options;
|
|
@@ -9895,7 +9891,7 @@ class CodexAppServerSession {
|
|
|
9895
9891
|
systemPrompt: options.systemPrompt,
|
|
9896
9892
|
threadId: options.threadId ?? null,
|
|
9897
9893
|
requireExistingThread: options.requireExistingThread === true,
|
|
9898
|
-
launchArgs:
|
|
9894
|
+
launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
|
|
9899
9895
|
});
|
|
9900
9896
|
}
|
|
9901
9897
|
async ensureStarted() {
|
|
@@ -9917,6 +9913,7 @@ class CodexAppServerSession {
|
|
|
9917
9913
|
await mkdir5(this.options.logsDirectory, { recursive: true });
|
|
9918
9914
|
await writeFile5(join14(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
|
|
9919
9915
|
const codexExecutable = await resolveCodexExecutable2();
|
|
9916
|
+
const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
|
|
9920
9917
|
const env = buildManagedAgentEnvironment({
|
|
9921
9918
|
agentName: this.options.agentName,
|
|
9922
9919
|
currentDirectory: this.options.cwd,
|
|
@@ -9927,7 +9924,8 @@ class CodexAppServerSession {
|
|
|
9927
9924
|
...buildScoutMcpCodexLaunchArgs({
|
|
9928
9925
|
currentDirectory: this.options.cwd,
|
|
9929
9926
|
env
|
|
9930
|
-
})
|
|
9927
|
+
}),
|
|
9928
|
+
...launchArgs
|
|
9931
9929
|
], {
|
|
9932
9930
|
cwd: this.options.cwd,
|
|
9933
9931
|
env,
|
|
@@ -10025,6 +10023,8 @@ class CodexAppServerSession {
|
|
|
10025
10023
|
turnId: "",
|
|
10026
10024
|
startedAt: Date.now(),
|
|
10027
10025
|
timer: null,
|
|
10026
|
+
graceTimer: null,
|
|
10027
|
+
timedOutAt: null,
|
|
10028
10028
|
messageOrder: [],
|
|
10029
10029
|
messageByItemId: new Map,
|
|
10030
10030
|
resolve: resolve5,
|
|
@@ -10032,20 +10032,43 @@ class CodexAppServerSession {
|
|
|
10032
10032
|
watchers: []
|
|
10033
10033
|
};
|
|
10034
10034
|
turn.timer = setTimeout(() => {
|
|
10035
|
-
this.
|
|
10036
|
-
return;
|
|
10037
|
-
});
|
|
10038
|
-
if (this.activeTurn === turn) {
|
|
10039
|
-
this.activeTurn = null;
|
|
10040
|
-
}
|
|
10041
|
-
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
10042
|
-
watcher.reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
10043
|
-
}
|
|
10044
|
-
reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
10035
|
+
this.scheduleTurnTimeout(turn, timeoutMs);
|
|
10045
10036
|
}, timeoutMs);
|
|
10046
10037
|
this.activeTurn = turn;
|
|
10047
10038
|
return turn;
|
|
10048
10039
|
}
|
|
10040
|
+
buildTurnTimeoutError(timeoutMs) {
|
|
10041
|
+
return new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`);
|
|
10042
|
+
}
|
|
10043
|
+
scheduleTurnTimeout(turn, timeoutMs) {
|
|
10044
|
+
if (turn.timedOutAt !== null) {
|
|
10045
|
+
return;
|
|
10046
|
+
}
|
|
10047
|
+
turn.timedOutAt = Date.now();
|
|
10048
|
+
this.interrupt().catch(() => {
|
|
10049
|
+
return;
|
|
10050
|
+
});
|
|
10051
|
+
const graceMs = resolveCodexCompletionGraceMs();
|
|
10052
|
+
if (graceMs <= 0) {
|
|
10053
|
+
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
10054
|
+
return;
|
|
10055
|
+
}
|
|
10056
|
+
turn.graceTimer = setTimeout(() => {
|
|
10057
|
+
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
10058
|
+
}, graceMs);
|
|
10059
|
+
}
|
|
10060
|
+
rejectTimedOutTurn(turn, timeoutMs) {
|
|
10061
|
+
if (this.activeTurn !== turn) {
|
|
10062
|
+
this.clearActiveTurn(turn);
|
|
10063
|
+
return;
|
|
10064
|
+
}
|
|
10065
|
+
this.clearActiveTurn(turn);
|
|
10066
|
+
const error = this.buildTurnTimeoutError(timeoutMs);
|
|
10067
|
+
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
10068
|
+
watcher.reject(error);
|
|
10069
|
+
}
|
|
10070
|
+
turn.reject(error);
|
|
10071
|
+
}
|
|
10049
10072
|
addTurnWatcher(turn, resolve5, reject, timeoutMs) {
|
|
10050
10073
|
const watcher = {
|
|
10051
10074
|
resolve: resolve5,
|
|
@@ -10079,6 +10102,9 @@ class CodexAppServerSession {
|
|
|
10079
10102
|
if (turn.timer) {
|
|
10080
10103
|
clearTimeout(turn.timer);
|
|
10081
10104
|
}
|
|
10105
|
+
if (turn.graceTimer) {
|
|
10106
|
+
clearTimeout(turn.graceTimer);
|
|
10107
|
+
}
|
|
10082
10108
|
if (this.activeTurn === turn) {
|
|
10083
10109
|
this.activeTurn = null;
|
|
10084
10110
|
}
|
|
@@ -11454,6 +11480,128 @@ function normalizeLocalAgentCapabilities(value) {
|
|
|
11454
11480
|
function normalizeLocalAgentLaunchArgs(value) {
|
|
11455
11481
|
return Array.isArray(value) ? value.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
11456
11482
|
}
|
|
11483
|
+
function normalizeRequestedModel(value) {
|
|
11484
|
+
const trimmed = value?.trim();
|
|
11485
|
+
return trimmed ? trimmed : undefined;
|
|
11486
|
+
}
|
|
11487
|
+
function readClaudeLaunchModel(launchArgs) {
|
|
11488
|
+
for (let index = 0;index < launchArgs.length; index += 1) {
|
|
11489
|
+
const current = launchArgs[index] ?? "";
|
|
11490
|
+
if (current === "--model") {
|
|
11491
|
+
const next = launchArgs[index + 1]?.trim();
|
|
11492
|
+
return next || undefined;
|
|
11493
|
+
}
|
|
11494
|
+
if (current.startsWith("--model=")) {
|
|
11495
|
+
const next = current.slice("--model=".length).trim();
|
|
11496
|
+
return next || undefined;
|
|
11497
|
+
}
|
|
11498
|
+
}
|
|
11499
|
+
return;
|
|
11500
|
+
}
|
|
11501
|
+
function normalizeLaunchArgsForHarness(harness, value) {
|
|
11502
|
+
const normalized = normalizeLocalAgentLaunchArgs(value);
|
|
11503
|
+
if (harness === "codex") {
|
|
11504
|
+
return normalizeCodexAppServerLaunchArgs(normalized);
|
|
11505
|
+
}
|
|
11506
|
+
return normalized;
|
|
11507
|
+
}
|
|
11508
|
+
function readLaunchModelForHarness(harness, launchArgs) {
|
|
11509
|
+
if (harness === "codex") {
|
|
11510
|
+
return readCodexAppServerModelFromLaunchArgs(launchArgs) ?? undefined;
|
|
11511
|
+
}
|
|
11512
|
+
if (harness === "claude") {
|
|
11513
|
+
return readClaudeLaunchModel(launchArgs ?? []);
|
|
11514
|
+
}
|
|
11515
|
+
return;
|
|
11516
|
+
}
|
|
11517
|
+
function stripLaunchModelForHarness(harness, launchArgs) {
|
|
11518
|
+
if (harness === "codex") {
|
|
11519
|
+
const next = [];
|
|
11520
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
11521
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
11522
|
+
const current = normalized[index] ?? "";
|
|
11523
|
+
if (current === "-c" || current === "--config") {
|
|
11524
|
+
const value = normalized[index + 1];
|
|
11525
|
+
if (readCodexAppServerModelFromLaunchArgs(value ? [current, value] : [current])) {
|
|
11526
|
+
index += 1;
|
|
11527
|
+
continue;
|
|
11528
|
+
}
|
|
11529
|
+
next.push(current);
|
|
11530
|
+
if (value) {
|
|
11531
|
+
next.push(value);
|
|
11532
|
+
index += 1;
|
|
11533
|
+
}
|
|
11534
|
+
continue;
|
|
11535
|
+
}
|
|
11536
|
+
if (current.startsWith("--config=")) {
|
|
11537
|
+
if (readCodexAppServerModelFromLaunchArgs([current])) {
|
|
11538
|
+
continue;
|
|
11539
|
+
}
|
|
11540
|
+
}
|
|
11541
|
+
next.push(current);
|
|
11542
|
+
}
|
|
11543
|
+
return next;
|
|
11544
|
+
}
|
|
11545
|
+
if (harness === "claude") {
|
|
11546
|
+
const next = [];
|
|
11547
|
+
const normalized = normalizeLocalAgentLaunchArgs(launchArgs);
|
|
11548
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
11549
|
+
const current = normalized[index] ?? "";
|
|
11550
|
+
if (current === "--model") {
|
|
11551
|
+
index += 1;
|
|
11552
|
+
continue;
|
|
11553
|
+
}
|
|
11554
|
+
if (current.startsWith("--model=")) {
|
|
11555
|
+
continue;
|
|
11556
|
+
}
|
|
11557
|
+
next.push(current);
|
|
11558
|
+
}
|
|
11559
|
+
return next;
|
|
11560
|
+
}
|
|
11561
|
+
return normalizeLocalAgentLaunchArgs(launchArgs);
|
|
11562
|
+
}
|
|
11563
|
+
function buildLaunchArgsForRequestedModel(harness, model) {
|
|
11564
|
+
if (harness === "codex") {
|
|
11565
|
+
return normalizeCodexAppServerLaunchArgs(["--model", model]);
|
|
11566
|
+
}
|
|
11567
|
+
if (harness === "claude") {
|
|
11568
|
+
return ["--model", model];
|
|
11569
|
+
}
|
|
11570
|
+
return [];
|
|
11571
|
+
}
|
|
11572
|
+
function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
11573
|
+
const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
|
|
11574
|
+
const requestedModel = normalizeRequestedModel(model);
|
|
11575
|
+
if (!requestedModel) {
|
|
11576
|
+
return normalized;
|
|
11577
|
+
}
|
|
11578
|
+
return [
|
|
11579
|
+
...stripLaunchModelForHarness(harness, normalized),
|
|
11580
|
+
...buildLaunchArgsForRequestedModel(harness, requestedModel)
|
|
11581
|
+
];
|
|
11582
|
+
}
|
|
11583
|
+
function defaultHarnessForOverride(override, fallback = "claude") {
|
|
11584
|
+
return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
|
|
11585
|
+
}
|
|
11586
|
+
function launchArgsForOverrideHarness(override, harness) {
|
|
11587
|
+
const profileLaunchArgs = override.harnessProfiles?.[harness]?.launchArgs;
|
|
11588
|
+
if (profileLaunchArgs) {
|
|
11589
|
+
return normalizeLaunchArgsForHarness(harness, profileLaunchArgs);
|
|
11590
|
+
}
|
|
11591
|
+
if (harness === defaultHarnessForOverride(override, harness)) {
|
|
11592
|
+
return normalizeLaunchArgsForHarness(harness, override.launchArgs);
|
|
11593
|
+
}
|
|
11594
|
+
return [];
|
|
11595
|
+
}
|
|
11596
|
+
function overrideHarnessProfile(override, harness) {
|
|
11597
|
+
const profile = override.harnessProfiles?.[harness];
|
|
11598
|
+
return {
|
|
11599
|
+
cwd: normalizeProjectPath(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
|
|
11600
|
+
transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
|
|
11601
|
+
sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
|
|
11602
|
+
launchArgs: launchArgsForOverrideHarness(override, harness)
|
|
11603
|
+
};
|
|
11604
|
+
}
|
|
11457
11605
|
function normalizeManagedHarness2(value, fallback) {
|
|
11458
11606
|
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
11459
11607
|
}
|
|
@@ -11469,7 +11617,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11469
11617
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
11470
11618
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
11471
11619
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
11472
|
-
launchArgs:
|
|
11620
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
11473
11621
|
};
|
|
11474
11622
|
}
|
|
11475
11623
|
const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
|
|
@@ -11478,7 +11626,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11478
11626
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
11479
11627
|
transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
|
|
11480
11628
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
|
|
11481
|
-
launchArgs:
|
|
11629
|
+
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
|
|
11482
11630
|
};
|
|
11483
11631
|
}
|
|
11484
11632
|
if (!nextProfiles[defaultHarness]) {
|
|
@@ -11486,7 +11634,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11486
11634
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
11487
11635
|
transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
|
|
11488
11636
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
|
|
11489
|
-
launchArgs:
|
|
11637
|
+
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
|
|
11490
11638
|
};
|
|
11491
11639
|
}
|
|
11492
11640
|
for (const harness of ["claude", "codex"]) {
|
|
@@ -11498,7 +11646,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11498
11646
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
11499
11647
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
11500
11648
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
11501
|
-
launchArgs:
|
|
11649
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
11502
11650
|
};
|
|
11503
11651
|
}
|
|
11504
11652
|
return nextProfiles;
|
|
@@ -11522,14 +11670,14 @@ function recordForHarness(record, harnessOverride) {
|
|
|
11522
11670
|
tmuxSession: fallbackSessionId,
|
|
11523
11671
|
cwd: fallbackCwd,
|
|
11524
11672
|
transport: fallbackTransport,
|
|
11525
|
-
launchArgs:
|
|
11673
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
11526
11674
|
harnessProfiles: {
|
|
11527
11675
|
...normalized.harnessProfiles,
|
|
11528
11676
|
[selectedHarness]: {
|
|
11529
11677
|
cwd: fallbackCwd,
|
|
11530
11678
|
transport: fallbackTransport,
|
|
11531
11679
|
sessionId: fallbackSessionId,
|
|
11532
|
-
launchArgs:
|
|
11680
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
|
|
11533
11681
|
}
|
|
11534
11682
|
}
|
|
11535
11683
|
};
|
|
@@ -11571,7 +11719,7 @@ function normalizeLocalAgentRecord(agentId, record) {
|
|
|
11571
11719
|
harnessProfiles,
|
|
11572
11720
|
transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
|
|
11573
11721
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
11574
|
-
launchArgs: activeProfile?.launchArgs ??
|
|
11722
|
+
launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs)
|
|
11575
11723
|
};
|
|
11576
11724
|
}
|
|
11577
11725
|
function localAgentStatusFromRecord(agentId, record, source) {
|
|
@@ -11640,7 +11788,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
|
|
|
11640
11788
|
source: existing?.source && existing.source !== "inferred" ? existing.source : "manual",
|
|
11641
11789
|
startedAt: normalizedRecord.startedAt,
|
|
11642
11790
|
systemPrompt: normalizedRecord.systemPrompt,
|
|
11643
|
-
launchArgs:
|
|
11791
|
+
launchArgs: normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs),
|
|
11644
11792
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
11645
11793
|
defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
|
|
11646
11794
|
harnessProfiles: normalizedRecord.harnessProfiles,
|
|
@@ -11665,7 +11813,7 @@ function buildLocalAgentConfigState(agentId, record) {
|
|
|
11665
11813
|
sessionId: record.tmuxSession,
|
|
11666
11814
|
wakePolicy: "on_demand"
|
|
11667
11815
|
},
|
|
11668
|
-
launchArgs:
|
|
11816
|
+
launchArgs: normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs),
|
|
11669
11817
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
11670
11818
|
applyMode: "restart",
|
|
11671
11819
|
templateHint: LOCAL_AGENT_SYSTEM_PROMPT_TEMPLATE_HINT
|
|
@@ -11791,7 +11939,7 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
11791
11939
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
|
|
11792
11940
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
11793
11941
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
11794
|
-
launchArgs:
|
|
11942
|
+
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
|
|
11795
11943
|
};
|
|
11796
11944
|
}
|
|
11797
11945
|
function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
@@ -11802,7 +11950,7 @@ function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
11802
11950
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "claude_stream_json" }),
|
|
11803
11951
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
11804
11952
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
11805
|
-
launchArgs:
|
|
11953
|
+
launchArgs: normalizeLaunchArgsForHarness("claude", record.launchArgs)
|
|
11806
11954
|
};
|
|
11807
11955
|
}
|
|
11808
11956
|
function endpointMetadataString(endpoint, key) {
|
|
@@ -12007,7 +12155,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
|
|
|
12007
12155
|
return initialMessage;
|
|
12008
12156
|
}
|
|
12009
12157
|
function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
|
|
12010
|
-
const extraArgs = shellQuoteArguments(
|
|
12158
|
+
const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
|
|
12011
12159
|
if (normalizeLocalAgentHarness(record.harness) === "codex") {
|
|
12012
12160
|
return `exec bash ${JSON.stringify(workerScript ?? join16(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
|
|
12013
12161
|
}
|
|
@@ -12379,6 +12527,7 @@ async function startLocalAgent(input) {
|
|
|
12379
12527
|
const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
|
|
12380
12528
|
const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
|
|
12381
12529
|
const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
|
|
12530
|
+
const launchArgs = applyRequestedModelToLaunchArgs(effectiveHarness, [], input.model);
|
|
12382
12531
|
const existingForInstance = overrides[instance.id];
|
|
12383
12532
|
if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
|
|
12384
12533
|
throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
|
|
@@ -12392,13 +12541,14 @@ async function startLocalAgent(input) {
|
|
|
12392
12541
|
projectConfigPath: coldProjectConfigPath,
|
|
12393
12542
|
source: "manual",
|
|
12394
12543
|
startedAt: nowSeconds2(),
|
|
12544
|
+
launchArgs,
|
|
12395
12545
|
defaultHarness: effectiveHarness,
|
|
12396
12546
|
harnessProfiles: {
|
|
12397
12547
|
[effectiveHarness]: {
|
|
12398
12548
|
cwd: effectiveCwd ?? projectRoot,
|
|
12399
12549
|
transport,
|
|
12400
12550
|
sessionId,
|
|
12401
|
-
launchArgs
|
|
12551
|
+
launchArgs
|
|
12402
12552
|
}
|
|
12403
12553
|
},
|
|
12404
12554
|
runtime: {
|
|
@@ -12419,6 +12569,9 @@ async function startLocalAgent(input) {
|
|
|
12419
12569
|
throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
|
|
12420
12570
|
}
|
|
12421
12571
|
const resolvedHarness = preferredHarness ?? matchingOverride.runtime?.harness;
|
|
12572
|
+
const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
|
|
12573
|
+
const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
|
|
12574
|
+
const nextLaunchArgs = applyRequestedModelToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), input.model);
|
|
12422
12575
|
overrides[instance.id] = {
|
|
12423
12576
|
agentId: instance.id,
|
|
12424
12577
|
definitionId: requestedDefinitionId,
|
|
@@ -12429,10 +12582,16 @@ async function startLocalAgent(input) {
|
|
|
12429
12582
|
source: "manual",
|
|
12430
12583
|
startedAt: matchingOverride.startedAt ?? nowSeconds2(),
|
|
12431
12584
|
systemPrompt: matchingOverride.systemPrompt,
|
|
12432
|
-
launchArgs: matchingOverride.launchArgs,
|
|
12585
|
+
launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
12433
12586
|
capabilities: matchingOverride.capabilities,
|
|
12434
|
-
defaultHarness:
|
|
12435
|
-
harnessProfiles:
|
|
12587
|
+
defaultHarness: nextDefaultHarness,
|
|
12588
|
+
harnessProfiles: {
|
|
12589
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
12590
|
+
[nextHarness]: {
|
|
12591
|
+
...overrideHarnessProfile(matchingOverride, nextHarness),
|
|
12592
|
+
launchArgs: nextLaunchArgs
|
|
12593
|
+
}
|
|
12594
|
+
},
|
|
12436
12595
|
runtime: {
|
|
12437
12596
|
cwd: effectiveCwd ?? matchingOverride.runtime?.cwd ?? matchingProjectRoot,
|
|
12438
12597
|
harness: resolvedHarness,
|
|
@@ -12444,6 +12603,22 @@ async function startLocalAgent(input) {
|
|
|
12444
12603
|
await writeRelayAgentOverrides(overrides);
|
|
12445
12604
|
targetAgentId = instance.id;
|
|
12446
12605
|
} else {
|
|
12606
|
+
if (input.model) {
|
|
12607
|
+
const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
|
|
12608
|
+
const nextLaunchArgs = applyRequestedModelToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), input.model);
|
|
12609
|
+
overrides[matchingAgentId] = {
|
|
12610
|
+
...matchingOverride,
|
|
12611
|
+
launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
12612
|
+
harnessProfiles: {
|
|
12613
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
12614
|
+
[existingHarness]: {
|
|
12615
|
+
...overrideHarnessProfile(matchingOverride, existingHarness),
|
|
12616
|
+
launchArgs: nextLaunchArgs
|
|
12617
|
+
}
|
|
12618
|
+
}
|
|
12619
|
+
};
|
|
12620
|
+
await writeRelayAgentOverrides(overrides);
|
|
12621
|
+
}
|
|
12447
12622
|
targetAgentId = matchingAgentId;
|
|
12448
12623
|
}
|
|
12449
12624
|
await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
|
|
@@ -12556,6 +12731,7 @@ function readPersistedClaudeSessionId(agentName) {
|
|
|
12556
12731
|
}
|
|
12557
12732
|
function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
12558
12733
|
const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
|
|
12734
|
+
const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
|
|
12559
12735
|
const definitionId = normalizedRecord.definitionId ?? agentId;
|
|
12560
12736
|
const displayName = titleCaseLocalAgentName(definitionId);
|
|
12561
12737
|
const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
|
|
@@ -12580,7 +12756,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
12580
12756
|
defaultSelector: instance.defaultSelector,
|
|
12581
12757
|
nodeQualifier: instance.nodeQualifier,
|
|
12582
12758
|
workspaceQualifier: instance.workspaceQualifier,
|
|
12583
|
-
branch: instance.branch
|
|
12759
|
+
branch: instance.branch,
|
|
12760
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
12584
12761
|
}
|
|
12585
12762
|
},
|
|
12586
12763
|
agent: {
|
|
@@ -12607,7 +12784,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
12607
12784
|
defaultSelector: instance.defaultSelector,
|
|
12608
12785
|
nodeQualifier: instance.nodeQualifier,
|
|
12609
12786
|
workspaceQualifier: instance.workspaceQualifier,
|
|
12610
|
-
branch: instance.branch
|
|
12787
|
+
branch: instance.branch,
|
|
12788
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
12611
12789
|
},
|
|
12612
12790
|
agentClass: "general",
|
|
12613
12791
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
@@ -12640,6 +12818,7 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
12640
12818
|
nodeQualifier: instance.nodeQualifier,
|
|
12641
12819
|
workspaceQualifier: instance.workspaceQualifier,
|
|
12642
12820
|
branch: instance.branch,
|
|
12821
|
+
...configuredModel ? { model: configuredModel } : {},
|
|
12643
12822
|
...externalSessionId ? { externalSessionId } : {}
|
|
12644
12823
|
}
|
|
12645
12824
|
}
|
|
@@ -12841,11 +13020,11 @@ var init_local_agents = __esm(async () => {
|
|
|
12841
13020
|
|
|
12842
13021
|
// packages/web/server/index.ts
|
|
12843
13022
|
import { existsSync as existsSync15 } from "fs";
|
|
12844
|
-
import { dirname as
|
|
12845
|
-
import { fileURLToPath as
|
|
13023
|
+
import { dirname as dirname14, join as join20, resolve as resolve11 } from "path";
|
|
13024
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
12846
13025
|
|
|
12847
13026
|
// packages/web/server/create-openscout-web-server.ts
|
|
12848
|
-
import { existsSync as
|
|
13027
|
+
import { existsSync as existsSync13, rmSync as rmSync3 } from "fs";
|
|
12849
13028
|
import { dirname as dirname12, resolve as resolve9 } from "path";
|
|
12850
13029
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
12851
13030
|
|
|
@@ -15745,6 +15924,12 @@ function transientBrokerWorkingStatusPredicate(alias) {
|
|
|
15745
15924
|
AND ${alias}.body LIKE '% is working.'
|
|
15746
15925
|
)`;
|
|
15747
15926
|
}
|
|
15927
|
+
function staleFlightActivityPredicate(alias) {
|
|
15928
|
+
return `NOT (
|
|
15929
|
+
${alias}.kind = 'flight_updated'
|
|
15930
|
+
AND COALESCE(${alias}.summary, '') LIKE 'Stale running flight reconciled:%'
|
|
15931
|
+
)`;
|
|
15932
|
+
}
|
|
15748
15933
|
function workPhaseFromFlightState(state) {
|
|
15749
15934
|
switch (state) {
|
|
15750
15935
|
case "queued":
|
|
@@ -15908,10 +16093,7 @@ function queryActivity(limit = 60) {
|
|
|
15908
16093
|
FROM activity_items ai
|
|
15909
16094
|
LEFT JOIN actors ac ON ac.id = ai.actor_id
|
|
15910
16095
|
WHERE ai.kind != 'ask_replied'
|
|
15911
|
-
AND
|
|
15912
|
-
ai.kind = 'flight_updated'
|
|
15913
|
-
AND COALESCE(ai.summary, '') LIKE 'Stale running flight reconciled:%'
|
|
15914
|
-
)
|
|
16096
|
+
AND ${staleFlightActivityPredicate("ai")}
|
|
15915
16097
|
ORDER BY ai.ts DESC
|
|
15916
16098
|
LIMIT ?`).all(limit);
|
|
15917
16099
|
const items = rows.map((r) => ({
|
|
@@ -16637,6 +16819,7 @@ function queryFleetActivity(opts) {
|
|
|
16637
16819
|
filters.push("ai.conversation_id = ?");
|
|
16638
16820
|
params.push(opts.conversationId);
|
|
16639
16821
|
}
|
|
16822
|
+
const scopedFilters = sqlJoinClauses(filters, "OR");
|
|
16640
16823
|
const sql = `SELECT
|
|
16641
16824
|
ai.id,
|
|
16642
16825
|
ai.kind,
|
|
@@ -16655,7 +16838,10 @@ function queryFleetActivity(opts) {
|
|
|
16655
16838
|
ai.session_id
|
|
16656
16839
|
FROM activity_items ai
|
|
16657
16840
|
LEFT JOIN actors ac ON ac.id = ai.actor_id
|
|
16658
|
-
${sqlWhereClause(
|
|
16841
|
+
${sqlWhereClause([
|
|
16842
|
+
staleFlightActivityPredicate("ai"),
|
|
16843
|
+
scopedFilters ? `(${scopedFilters})` : null
|
|
16844
|
+
])}
|
|
16659
16845
|
ORDER BY ai.ts DESC
|
|
16660
16846
|
LIMIT ?`;
|
|
16661
16847
|
const rows = db().prepare(sql).all(...params, opts?.limit ?? 80);
|
|
@@ -16738,6 +16924,10 @@ function queryFleetAskRows(requesterIds, limit) {
|
|
|
16738
16924
|
)
|
|
16739
16925
|
LEFT JOIN collaboration_records cr ON cr.id = inv.collaboration_record_id
|
|
16740
16926
|
WHERE inv.requester_id IN (${requesterClause})
|
|
16927
|
+
AND NOT (
|
|
16928
|
+
COALESCE(f.state, '') = 'failed'
|
|
16929
|
+
AND COALESCE(f.error, '') LIKE 'Stale running flight reconciled:%'
|
|
16930
|
+
)
|
|
16741
16931
|
ORDER BY COALESCE(f.completed_at, f.started_at, inv.created_at) DESC
|
|
16742
16932
|
LIMIT ?`).all(...requesterIds, limit);
|
|
16743
16933
|
}
|
|
@@ -17828,2359 +18018,79 @@ function whoEntryState(endpoints, registrationKind) {
|
|
|
17828
18018
|
|
|
17829
18019
|
// packages/web/server/core/observe/service.ts
|
|
17830
18020
|
init_src();
|
|
18021
|
+
await init_local_agents();
|
|
17831
18022
|
import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
|
|
17832
18023
|
import { homedir as homedir12 } from "os";
|
|
17833
18024
|
import { join as join18, resolve as resolve8 } from "path";
|
|
17834
|
-
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
|
|
17841
|
-
|
|
17842
|
-
|
|
17843
|
-
|
|
17844
|
-
|
|
17845
|
-
|
|
17846
|
-
|
|
18025
|
+
var HISTORY_SNAPSHOT_CACHE_LIMIT = 128;
|
|
18026
|
+
var historySnapshotCache = new Map;
|
|
18027
|
+
var OBSERVE_SUMMARY_TAIL_SIZE = 8;
|
|
18028
|
+
function activeEndpoint(snapshot, agentId) {
|
|
18029
|
+
const candidates = Object.values(snapshot.endpoints ?? {}).filter((endpoint) => endpoint.agentId === agentId);
|
|
18030
|
+
const rank = (state) => {
|
|
18031
|
+
switch (state) {
|
|
18032
|
+
case "active":
|
|
18033
|
+
return 0;
|
|
18034
|
+
case "idle":
|
|
18035
|
+
return 1;
|
|
18036
|
+
case "waiting":
|
|
18037
|
+
return 2;
|
|
18038
|
+
case "offline":
|
|
18039
|
+
return 5;
|
|
18040
|
+
default:
|
|
18041
|
+
return 4;
|
|
18042
|
+
}
|
|
17847
18043
|
};
|
|
18044
|
+
return [...candidates].sort((left, right) => rank(left.state) - rank(right.state))[0] ?? null;
|
|
17848
18045
|
}
|
|
17849
|
-
|
|
17850
|
-
|
|
17851
|
-
|
|
17852
|
-
|
|
17853
|
-
// packages/runtime/src/planner.ts
|
|
17854
|
-
function createDeliveryId(messageId, targetId, reason, transport) {
|
|
17855
|
-
return `del-${messageId}-${targetId}-${reason}-${transport}`;
|
|
17856
|
-
}
|
|
17857
|
-
function unique(values) {
|
|
17858
|
-
return [...new Set(values)];
|
|
17859
|
-
}
|
|
17860
|
-
function resolveVisibilityAudience(message, conversation) {
|
|
17861
|
-
if (message.audience?.visibleTo?.length) {
|
|
17862
|
-
return unique(message.audience.visibleTo);
|
|
18046
|
+
function expandHome(value) {
|
|
18047
|
+
if (!value) {
|
|
18048
|
+
return null;
|
|
17863
18049
|
}
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
17871
|
-
function resolveInvocationAudience(message) {
|
|
17872
|
-
return unique((message.audience?.invoke ?? []).filter((actorId) => actorId !== message.actorId));
|
|
18050
|
+
if (value === "~") {
|
|
18051
|
+
return homedir12();
|
|
18052
|
+
}
|
|
18053
|
+
if (value.startsWith("~/")) {
|
|
18054
|
+
return join18(homedir12(), value.slice(2));
|
|
18055
|
+
}
|
|
18056
|
+
return value;
|
|
17873
18057
|
}
|
|
17874
|
-
function
|
|
17875
|
-
|
|
17876
|
-
|
|
17877
|
-
messageId: message.id,
|
|
17878
|
-
targetId: route.targetId,
|
|
17879
|
-
targetNodeId: route.nodeId,
|
|
17880
|
-
targetKind: route.targetKind,
|
|
17881
|
-
transport: route.transport,
|
|
17882
|
-
reason,
|
|
17883
|
-
policy,
|
|
17884
|
-
status: "pending",
|
|
17885
|
-
bindingId: route.bindingId
|
|
17886
|
-
};
|
|
18058
|
+
function encodeClaudeProjectsSlug2(absolutePath) {
|
|
18059
|
+
const normalized = resolve8(absolutePath);
|
|
18060
|
+
return `-${normalized.replace(/^\//u, "").replace(/\//gu, "-")}`;
|
|
17887
18061
|
}
|
|
17888
|
-
function
|
|
17889
|
-
const
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
const deliveries2 = new Map;
|
|
17893
|
-
for (const route of input.participantRoutes) {
|
|
17894
|
-
if (visibilityIds.has(route.targetId)) {
|
|
17895
|
-
const intent = planTargetDelivery(input.message, {
|
|
17896
|
-
...route,
|
|
17897
|
-
transport: route.nodeId && input.localNodeId && route.nodeId !== input.localNodeId ? "peer_broker" : route.transport
|
|
17898
|
-
}, input.conversation.kind === "direct" || input.conversation.kind === "group_direct" ? "direct_message" : "conversation_visibility", "durable");
|
|
17899
|
-
deliveries2.set(intent.id, intent);
|
|
17900
|
-
}
|
|
17901
|
-
if (notifyIds.has(route.targetId)) {
|
|
17902
|
-
const intent = planTargetDelivery(input.message, {
|
|
17903
|
-
...route,
|
|
17904
|
-
transport: route.nodeId && input.localNodeId && route.nodeId !== input.localNodeId ? "peer_broker" : route.transport
|
|
17905
|
-
}, "mention", "must_ack");
|
|
17906
|
-
deliveries2.set(intent.id, intent);
|
|
17907
|
-
}
|
|
17908
|
-
if (invokeIds.has(route.targetId)) {
|
|
17909
|
-
const intent = planTargetDelivery(input.message, {
|
|
17910
|
-
...route,
|
|
17911
|
-
transport: route.nodeId && input.localNodeId && route.nodeId !== input.localNodeId ? "peer_broker" : route.transport
|
|
17912
|
-
}, "invocation", "must_ack");
|
|
17913
|
-
deliveries2.set(intent.id, intent);
|
|
17914
|
-
}
|
|
17915
|
-
if (input.message.speech?.text && route.speechEnabled) {
|
|
17916
|
-
const speechIntent = planTargetDelivery(input.message, {
|
|
17917
|
-
...route,
|
|
17918
|
-
transport: route.transport === "local_socket" ? "native_voice" : "tts",
|
|
17919
|
-
targetKind: route.targetKind === "device" ? "voice_session" : route.targetKind
|
|
17920
|
-
}, "speech", "best_effort");
|
|
17921
|
-
deliveries2.set(speechIntent.id, speechIntent);
|
|
17922
|
-
}
|
|
17923
|
-
}
|
|
17924
|
-
for (const route of input.bindingRoutes ?? []) {
|
|
17925
|
-
const intent = planTargetDelivery(input.message, route, "bridge_outbound", "durable");
|
|
17926
|
-
deliveries2.set(intent.id, intent);
|
|
17927
|
-
}
|
|
17928
|
-
return [...deliveries2.values()];
|
|
17929
|
-
}
|
|
17930
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/entity.js
|
|
17931
|
-
var entityKind = Symbol.for("drizzle:entityKind");
|
|
17932
|
-
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
17933
|
-
function is(value, type) {
|
|
17934
|
-
if (!value || typeof value !== "object") {
|
|
17935
|
-
return false;
|
|
17936
|
-
}
|
|
17937
|
-
if (value instanceof type) {
|
|
17938
|
-
return true;
|
|
18062
|
+
function resolveClaudeHistoryPath(cwd, sessionId) {
|
|
18063
|
+
const normalizedCwd = expandHome(cwd)?.trim();
|
|
18064
|
+
if (!normalizedCwd) {
|
|
18065
|
+
return null;
|
|
17939
18066
|
}
|
|
17940
|
-
|
|
17941
|
-
|
|
18067
|
+
const projectDir = join18(homedir12(), ".claude", "projects", encodeClaudeProjectsSlug2(normalizedCwd));
|
|
18068
|
+
const normalizedSessionId = sessionId?.trim().replace(/\.jsonl$/u, "") || "";
|
|
18069
|
+
if (normalizedSessionId) {
|
|
18070
|
+
const exactPath = join18(projectDir, `${normalizedSessionId}.jsonl`);
|
|
18071
|
+
if (existsSync12(exactPath)) {
|
|
18072
|
+
return exactPath;
|
|
18073
|
+
}
|
|
17942
18074
|
}
|
|
17943
|
-
|
|
17944
|
-
|
|
17945
|
-
|
|
17946
|
-
|
|
17947
|
-
|
|
18075
|
+
return findMostRecentJsonl(projectDir);
|
|
18076
|
+
}
|
|
18077
|
+
function findMostRecentJsonl(dir) {
|
|
18078
|
+
if (!existsSync12(dir))
|
|
18079
|
+
return null;
|
|
18080
|
+
try {
|
|
18081
|
+
let best = null;
|
|
18082
|
+
for (const entry of readdirSync2(dir)) {
|
|
18083
|
+
if (!entry.endsWith(".jsonl"))
|
|
18084
|
+
continue;
|
|
18085
|
+
const full = join18(dir, entry);
|
|
18086
|
+
const st = statSync4(full);
|
|
18087
|
+
if (!best || st.mtimeMs > best.mtime) {
|
|
18088
|
+
best = { path: full, mtime: st.mtimeMs };
|
|
17948
18089
|
}
|
|
17949
|
-
cls = Object.getPrototypeOf(cls);
|
|
17950
18090
|
}
|
|
17951
|
-
|
|
17952
|
-
|
|
17953
|
-
|
|
17954
|
-
|
|
17955
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column.js
|
|
17956
|
-
class Column {
|
|
17957
|
-
constructor(table, config) {
|
|
17958
|
-
this.table = table;
|
|
17959
|
-
this.config = config;
|
|
17960
|
-
this.name = config.name;
|
|
17961
|
-
this.keyAsName = config.keyAsName;
|
|
17962
|
-
this.notNull = config.notNull;
|
|
17963
|
-
this.default = config.default;
|
|
17964
|
-
this.defaultFn = config.defaultFn;
|
|
17965
|
-
this.onUpdateFn = config.onUpdateFn;
|
|
17966
|
-
this.hasDefault = config.hasDefault;
|
|
17967
|
-
this.primary = config.primaryKey;
|
|
17968
|
-
this.isUnique = config.isUnique;
|
|
17969
|
-
this.uniqueName = config.uniqueName;
|
|
17970
|
-
this.uniqueType = config.uniqueType;
|
|
17971
|
-
this.dataType = config.dataType;
|
|
17972
|
-
this.columnType = config.columnType;
|
|
17973
|
-
this.generated = config.generated;
|
|
17974
|
-
this.generatedIdentity = config.generatedIdentity;
|
|
17975
|
-
}
|
|
17976
|
-
static [entityKind] = "Column";
|
|
17977
|
-
name;
|
|
17978
|
-
keyAsName;
|
|
17979
|
-
primary;
|
|
17980
|
-
notNull;
|
|
17981
|
-
default;
|
|
17982
|
-
defaultFn;
|
|
17983
|
-
onUpdateFn;
|
|
17984
|
-
hasDefault;
|
|
17985
|
-
isUnique;
|
|
17986
|
-
uniqueName;
|
|
17987
|
-
uniqueType;
|
|
17988
|
-
dataType;
|
|
17989
|
-
columnType;
|
|
17990
|
-
enumValues = undefined;
|
|
17991
|
-
generated = undefined;
|
|
17992
|
-
generatedIdentity = undefined;
|
|
17993
|
-
config;
|
|
17994
|
-
mapFromDriverValue(value) {
|
|
17995
|
-
return value;
|
|
17996
|
-
}
|
|
17997
|
-
mapToDriverValue(value) {
|
|
17998
|
-
return value;
|
|
17999
|
-
}
|
|
18000
|
-
shouldDisableInsert() {
|
|
18001
|
-
return this.config.generated !== undefined && this.config.generated.type !== "byDefault";
|
|
18002
|
-
}
|
|
18003
|
-
}
|
|
18004
|
-
|
|
18005
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/column-builder.js
|
|
18006
|
-
class ColumnBuilder {
|
|
18007
|
-
static [entityKind] = "ColumnBuilder";
|
|
18008
|
-
config;
|
|
18009
|
-
constructor(name, dataType, columnType) {
|
|
18010
|
-
this.config = {
|
|
18011
|
-
name,
|
|
18012
|
-
keyAsName: name === "",
|
|
18013
|
-
notNull: false,
|
|
18014
|
-
default: undefined,
|
|
18015
|
-
hasDefault: false,
|
|
18016
|
-
primaryKey: false,
|
|
18017
|
-
isUnique: false,
|
|
18018
|
-
uniqueName: undefined,
|
|
18019
|
-
uniqueType: undefined,
|
|
18020
|
-
dataType,
|
|
18021
|
-
columnType,
|
|
18022
|
-
generated: undefined
|
|
18023
|
-
};
|
|
18024
|
-
}
|
|
18025
|
-
$type() {
|
|
18026
|
-
return this;
|
|
18027
|
-
}
|
|
18028
|
-
notNull() {
|
|
18029
|
-
this.config.notNull = true;
|
|
18030
|
-
return this;
|
|
18031
|
-
}
|
|
18032
|
-
default(value) {
|
|
18033
|
-
this.config.default = value;
|
|
18034
|
-
this.config.hasDefault = true;
|
|
18035
|
-
return this;
|
|
18036
|
-
}
|
|
18037
|
-
$defaultFn(fn) {
|
|
18038
|
-
this.config.defaultFn = fn;
|
|
18039
|
-
this.config.hasDefault = true;
|
|
18040
|
-
return this;
|
|
18041
|
-
}
|
|
18042
|
-
$default = this.$defaultFn;
|
|
18043
|
-
$onUpdateFn(fn) {
|
|
18044
|
-
this.config.onUpdateFn = fn;
|
|
18045
|
-
this.config.hasDefault = true;
|
|
18046
|
-
return this;
|
|
18047
|
-
}
|
|
18048
|
-
$onUpdate = this.$onUpdateFn;
|
|
18049
|
-
primaryKey() {
|
|
18050
|
-
this.config.primaryKey = true;
|
|
18051
|
-
this.config.notNull = true;
|
|
18052
|
-
return this;
|
|
18053
|
-
}
|
|
18054
|
-
setName(name) {
|
|
18055
|
-
if (this.config.name !== "")
|
|
18056
|
-
return;
|
|
18057
|
-
this.config.name = name;
|
|
18058
|
-
}
|
|
18059
|
-
}
|
|
18060
|
-
|
|
18061
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.utils.js
|
|
18062
|
-
var TableName = Symbol.for("drizzle:Name");
|
|
18063
|
-
|
|
18064
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing-utils.js
|
|
18065
|
-
function iife(fn, ...args) {
|
|
18066
|
-
return fn(...args);
|
|
18067
|
-
}
|
|
18068
|
-
|
|
18069
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/unique-constraint.js
|
|
18070
|
-
function uniqueKeyName(table, columns) {
|
|
18071
|
-
return `${table[TableName]}_${columns.join("_")}_unique`;
|
|
18072
|
-
}
|
|
18073
|
-
|
|
18074
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/common.js
|
|
18075
|
-
class PgColumn extends Column {
|
|
18076
|
-
constructor(table, config) {
|
|
18077
|
-
if (!config.uniqueName) {
|
|
18078
|
-
config.uniqueName = uniqueKeyName(table, [config.name]);
|
|
18079
|
-
}
|
|
18080
|
-
super(table, config);
|
|
18081
|
-
this.table = table;
|
|
18082
|
-
}
|
|
18083
|
-
static [entityKind] = "PgColumn";
|
|
18084
|
-
}
|
|
18085
|
-
|
|
18086
|
-
class ExtraConfigColumn extends PgColumn {
|
|
18087
|
-
static [entityKind] = "ExtraConfigColumn";
|
|
18088
|
-
getSQLType() {
|
|
18089
|
-
return this.getSQLType();
|
|
18090
|
-
}
|
|
18091
|
-
indexConfig = {
|
|
18092
|
-
order: this.config.order ?? "asc",
|
|
18093
|
-
nulls: this.config.nulls ?? "last",
|
|
18094
|
-
opClass: this.config.opClass
|
|
18095
|
-
};
|
|
18096
|
-
defaultConfig = {
|
|
18097
|
-
order: "asc",
|
|
18098
|
-
nulls: "last",
|
|
18099
|
-
opClass: undefined
|
|
18100
|
-
};
|
|
18101
|
-
asc() {
|
|
18102
|
-
this.indexConfig.order = "asc";
|
|
18103
|
-
return this;
|
|
18104
|
-
}
|
|
18105
|
-
desc() {
|
|
18106
|
-
this.indexConfig.order = "desc";
|
|
18107
|
-
return this;
|
|
18108
|
-
}
|
|
18109
|
-
nullsFirst() {
|
|
18110
|
-
this.indexConfig.nulls = "first";
|
|
18111
|
-
return this;
|
|
18112
|
-
}
|
|
18113
|
-
nullsLast() {
|
|
18114
|
-
this.indexConfig.nulls = "last";
|
|
18115
|
-
return this;
|
|
18116
|
-
}
|
|
18117
|
-
op(opClass) {
|
|
18118
|
-
this.indexConfig.opClass = opClass;
|
|
18119
|
-
return this;
|
|
18120
|
-
}
|
|
18121
|
-
}
|
|
18122
|
-
|
|
18123
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/pg-core/columns/enum.js
|
|
18124
|
-
class PgEnumObjectColumn extends PgColumn {
|
|
18125
|
-
static [entityKind] = "PgEnumObjectColumn";
|
|
18126
|
-
enum;
|
|
18127
|
-
enumValues = this.config.enum.enumValues;
|
|
18128
|
-
constructor(table, config) {
|
|
18129
|
-
super(table, config);
|
|
18130
|
-
this.enum = config.enum;
|
|
18131
|
-
}
|
|
18132
|
-
getSQLType() {
|
|
18133
|
-
return this.enum.enumName;
|
|
18134
|
-
}
|
|
18135
|
-
}
|
|
18136
|
-
var isPgEnumSym = Symbol.for("drizzle:isPgEnum");
|
|
18137
|
-
function isPgEnum(obj) {
|
|
18138
|
-
return !!obj && typeof obj === "function" && isPgEnumSym in obj && obj[isPgEnumSym] === true;
|
|
18139
|
-
}
|
|
18140
|
-
class PgEnumColumn extends PgColumn {
|
|
18141
|
-
static [entityKind] = "PgEnumColumn";
|
|
18142
|
-
enum = this.config.enum;
|
|
18143
|
-
enumValues = this.config.enum.enumValues;
|
|
18144
|
-
constructor(table, config) {
|
|
18145
|
-
super(table, config);
|
|
18146
|
-
this.enum = config.enum;
|
|
18147
|
-
}
|
|
18148
|
-
getSQLType() {
|
|
18149
|
-
return this.enum.enumName;
|
|
18150
|
-
}
|
|
18151
|
-
}
|
|
18152
|
-
|
|
18153
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/subquery.js
|
|
18154
|
-
class Subquery {
|
|
18155
|
-
static [entityKind] = "Subquery";
|
|
18156
|
-
constructor(sql, fields, alias, isWith = false, usedTables = []) {
|
|
18157
|
-
this._ = {
|
|
18158
|
-
brand: "Subquery",
|
|
18159
|
-
sql,
|
|
18160
|
-
selectedFields: fields,
|
|
18161
|
-
alias,
|
|
18162
|
-
isWith,
|
|
18163
|
-
usedTables
|
|
18164
|
-
};
|
|
18165
|
-
}
|
|
18166
|
-
}
|
|
18167
|
-
|
|
18168
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/version.js
|
|
18169
|
-
var version = "0.45.2";
|
|
18170
|
-
|
|
18171
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/tracing.js
|
|
18172
|
-
var otel;
|
|
18173
|
-
var rawTracer;
|
|
18174
|
-
var tracer = {
|
|
18175
|
-
startActiveSpan(name, fn) {
|
|
18176
|
-
if (!otel) {
|
|
18177
|
-
return fn();
|
|
18178
|
-
}
|
|
18179
|
-
if (!rawTracer) {
|
|
18180
|
-
rawTracer = otel.trace.getTracer("drizzle-orm", version);
|
|
18181
|
-
}
|
|
18182
|
-
return iife((otel2, rawTracer2) => rawTracer2.startActiveSpan(name, (span) => {
|
|
18183
|
-
try {
|
|
18184
|
-
return fn(span);
|
|
18185
|
-
} catch (e) {
|
|
18186
|
-
span.setStatus({
|
|
18187
|
-
code: otel2.SpanStatusCode.ERROR,
|
|
18188
|
-
message: e instanceof Error ? e.message : "Unknown error"
|
|
18189
|
-
});
|
|
18190
|
-
throw e;
|
|
18191
|
-
} finally {
|
|
18192
|
-
span.end();
|
|
18193
|
-
}
|
|
18194
|
-
}), otel, rawTracer);
|
|
18195
|
-
}
|
|
18196
|
-
};
|
|
18197
|
-
|
|
18198
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/view-common.js
|
|
18199
|
-
var ViewBaseConfig = Symbol.for("drizzle:ViewBaseConfig");
|
|
18200
|
-
|
|
18201
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/table.js
|
|
18202
|
-
var Schema = Symbol.for("drizzle:Schema");
|
|
18203
|
-
var Columns = Symbol.for("drizzle:Columns");
|
|
18204
|
-
var ExtraConfigColumns = Symbol.for("drizzle:ExtraConfigColumns");
|
|
18205
|
-
var OriginalName = Symbol.for("drizzle:OriginalName");
|
|
18206
|
-
var BaseName = Symbol.for("drizzle:BaseName");
|
|
18207
|
-
var IsAlias = Symbol.for("drizzle:IsAlias");
|
|
18208
|
-
var ExtraConfigBuilder = Symbol.for("drizzle:ExtraConfigBuilder");
|
|
18209
|
-
var IsDrizzleTable = Symbol.for("drizzle:IsDrizzleTable");
|
|
18210
|
-
|
|
18211
|
-
class Table {
|
|
18212
|
-
static [entityKind] = "Table";
|
|
18213
|
-
static Symbol = {
|
|
18214
|
-
Name: TableName,
|
|
18215
|
-
Schema,
|
|
18216
|
-
OriginalName,
|
|
18217
|
-
Columns,
|
|
18218
|
-
ExtraConfigColumns,
|
|
18219
|
-
BaseName,
|
|
18220
|
-
IsAlias,
|
|
18221
|
-
ExtraConfigBuilder
|
|
18222
|
-
};
|
|
18223
|
-
[TableName];
|
|
18224
|
-
[OriginalName];
|
|
18225
|
-
[Schema];
|
|
18226
|
-
[Columns];
|
|
18227
|
-
[ExtraConfigColumns];
|
|
18228
|
-
[BaseName];
|
|
18229
|
-
[IsAlias] = false;
|
|
18230
|
-
[IsDrizzleTable] = true;
|
|
18231
|
-
[ExtraConfigBuilder] = undefined;
|
|
18232
|
-
constructor(name, schema, baseName) {
|
|
18233
|
-
this[TableName] = this[OriginalName] = name;
|
|
18234
|
-
this[Schema] = schema;
|
|
18235
|
-
this[BaseName] = baseName;
|
|
18236
|
-
}
|
|
18237
|
-
}
|
|
18238
|
-
|
|
18239
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sql/sql.js
|
|
18240
|
-
function isSQLWrapper(value) {
|
|
18241
|
-
return value !== null && value !== undefined && typeof value.getSQL === "function";
|
|
18242
|
-
}
|
|
18243
|
-
function mergeQueries(queries) {
|
|
18244
|
-
const result = { sql: "", params: [] };
|
|
18245
|
-
for (const query of queries) {
|
|
18246
|
-
result.sql += query.sql;
|
|
18247
|
-
result.params.push(...query.params);
|
|
18248
|
-
if (query.typings?.length) {
|
|
18249
|
-
if (!result.typings) {
|
|
18250
|
-
result.typings = [];
|
|
18251
|
-
}
|
|
18252
|
-
result.typings.push(...query.typings);
|
|
18253
|
-
}
|
|
18254
|
-
}
|
|
18255
|
-
return result;
|
|
18256
|
-
}
|
|
18257
|
-
|
|
18258
|
-
class StringChunk {
|
|
18259
|
-
static [entityKind] = "StringChunk";
|
|
18260
|
-
value;
|
|
18261
|
-
constructor(value) {
|
|
18262
|
-
this.value = Array.isArray(value) ? value : [value];
|
|
18263
|
-
}
|
|
18264
|
-
getSQL() {
|
|
18265
|
-
return new SQL([this]);
|
|
18266
|
-
}
|
|
18267
|
-
}
|
|
18268
|
-
|
|
18269
|
-
class SQL {
|
|
18270
|
-
constructor(queryChunks) {
|
|
18271
|
-
this.queryChunks = queryChunks;
|
|
18272
|
-
for (const chunk of queryChunks) {
|
|
18273
|
-
if (is(chunk, Table)) {
|
|
18274
|
-
const schemaName = chunk[Table.Symbol.Schema];
|
|
18275
|
-
this.usedTables.push(schemaName === undefined ? chunk[Table.Symbol.Name] : schemaName + "." + chunk[Table.Symbol.Name]);
|
|
18276
|
-
}
|
|
18277
|
-
}
|
|
18278
|
-
}
|
|
18279
|
-
static [entityKind] = "SQL";
|
|
18280
|
-
decoder = noopDecoder;
|
|
18281
|
-
shouldInlineParams = false;
|
|
18282
|
-
usedTables = [];
|
|
18283
|
-
append(query) {
|
|
18284
|
-
this.queryChunks.push(...query.queryChunks);
|
|
18285
|
-
return this;
|
|
18286
|
-
}
|
|
18287
|
-
toQuery(config) {
|
|
18288
|
-
return tracer.startActiveSpan("drizzle.buildSQL", (span) => {
|
|
18289
|
-
const query = this.buildQueryFromSourceParams(this.queryChunks, config);
|
|
18290
|
-
span?.setAttributes({
|
|
18291
|
-
"drizzle.query.text": query.sql,
|
|
18292
|
-
"drizzle.query.params": JSON.stringify(query.params)
|
|
18293
|
-
});
|
|
18294
|
-
return query;
|
|
18295
|
-
});
|
|
18296
|
-
}
|
|
18297
|
-
buildQueryFromSourceParams(chunks, _config) {
|
|
18298
|
-
const config = Object.assign({}, _config, {
|
|
18299
|
-
inlineParams: _config.inlineParams || this.shouldInlineParams,
|
|
18300
|
-
paramStartIndex: _config.paramStartIndex || { value: 0 }
|
|
18301
|
-
});
|
|
18302
|
-
const {
|
|
18303
|
-
casing,
|
|
18304
|
-
escapeName,
|
|
18305
|
-
escapeParam,
|
|
18306
|
-
prepareTyping,
|
|
18307
|
-
inlineParams,
|
|
18308
|
-
paramStartIndex
|
|
18309
|
-
} = config;
|
|
18310
|
-
return mergeQueries(chunks.map((chunk) => {
|
|
18311
|
-
if (is(chunk, StringChunk)) {
|
|
18312
|
-
return { sql: chunk.value.join(""), params: [] };
|
|
18313
|
-
}
|
|
18314
|
-
if (is(chunk, Name)) {
|
|
18315
|
-
return { sql: escapeName(chunk.value), params: [] };
|
|
18316
|
-
}
|
|
18317
|
-
if (chunk === undefined) {
|
|
18318
|
-
return { sql: "", params: [] };
|
|
18319
|
-
}
|
|
18320
|
-
if (Array.isArray(chunk)) {
|
|
18321
|
-
const result = [new StringChunk("(")];
|
|
18322
|
-
for (const [i, p] of chunk.entries()) {
|
|
18323
|
-
result.push(p);
|
|
18324
|
-
if (i < chunk.length - 1) {
|
|
18325
|
-
result.push(new StringChunk(", "));
|
|
18326
|
-
}
|
|
18327
|
-
}
|
|
18328
|
-
result.push(new StringChunk(")"));
|
|
18329
|
-
return this.buildQueryFromSourceParams(result, config);
|
|
18330
|
-
}
|
|
18331
|
-
if (is(chunk, SQL)) {
|
|
18332
|
-
return this.buildQueryFromSourceParams(chunk.queryChunks, {
|
|
18333
|
-
...config,
|
|
18334
|
-
inlineParams: inlineParams || chunk.shouldInlineParams
|
|
18335
|
-
});
|
|
18336
|
-
}
|
|
18337
|
-
if (is(chunk, Table)) {
|
|
18338
|
-
const schemaName = chunk[Table.Symbol.Schema];
|
|
18339
|
-
const tableName = chunk[Table.Symbol.Name];
|
|
18340
|
-
return {
|
|
18341
|
-
sql: schemaName === undefined || chunk[IsAlias] ? escapeName(tableName) : escapeName(schemaName) + "." + escapeName(tableName),
|
|
18342
|
-
params: []
|
|
18343
|
-
};
|
|
18344
|
-
}
|
|
18345
|
-
if (is(chunk, Column)) {
|
|
18346
|
-
const columnName = casing.getColumnCasing(chunk);
|
|
18347
|
-
if (_config.invokeSource === "indexes") {
|
|
18348
|
-
return { sql: escapeName(columnName), params: [] };
|
|
18349
|
-
}
|
|
18350
|
-
const schemaName = chunk.table[Table.Symbol.Schema];
|
|
18351
|
-
return {
|
|
18352
|
-
sql: chunk.table[IsAlias] || schemaName === undefined ? escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName) : escapeName(schemaName) + "." + escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName),
|
|
18353
|
-
params: []
|
|
18354
|
-
};
|
|
18355
|
-
}
|
|
18356
|
-
if (is(chunk, View)) {
|
|
18357
|
-
const schemaName = chunk[ViewBaseConfig].schema;
|
|
18358
|
-
const viewName = chunk[ViewBaseConfig].name;
|
|
18359
|
-
return {
|
|
18360
|
-
sql: schemaName === undefined || chunk[ViewBaseConfig].isAlias ? escapeName(viewName) : escapeName(schemaName) + "." + escapeName(viewName),
|
|
18361
|
-
params: []
|
|
18362
|
-
};
|
|
18363
|
-
}
|
|
18364
|
-
if (is(chunk, Param)) {
|
|
18365
|
-
if (is(chunk.value, Placeholder)) {
|
|
18366
|
-
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
|
|
18367
|
-
}
|
|
18368
|
-
const mappedValue = chunk.value === null ? null : chunk.encoder.mapToDriverValue(chunk.value);
|
|
18369
|
-
if (is(mappedValue, SQL)) {
|
|
18370
|
-
return this.buildQueryFromSourceParams([mappedValue], config);
|
|
18371
|
-
}
|
|
18372
|
-
if (inlineParams) {
|
|
18373
|
-
return { sql: this.mapInlineParam(mappedValue, config), params: [] };
|
|
18374
|
-
}
|
|
18375
|
-
let typings = ["none"];
|
|
18376
|
-
if (prepareTyping) {
|
|
18377
|
-
typings = [prepareTyping(chunk.encoder)];
|
|
18378
|
-
}
|
|
18379
|
-
return { sql: escapeParam(paramStartIndex.value++, mappedValue), params: [mappedValue], typings };
|
|
18380
|
-
}
|
|
18381
|
-
if (is(chunk, Placeholder)) {
|
|
18382
|
-
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
|
|
18383
|
-
}
|
|
18384
|
-
if (is(chunk, SQL.Aliased) && chunk.fieldAlias !== undefined) {
|
|
18385
|
-
return { sql: escapeName(chunk.fieldAlias), params: [] };
|
|
18386
|
-
}
|
|
18387
|
-
if (is(chunk, Subquery)) {
|
|
18388
|
-
if (chunk._.isWith) {
|
|
18389
|
-
return { sql: escapeName(chunk._.alias), params: [] };
|
|
18390
|
-
}
|
|
18391
|
-
return this.buildQueryFromSourceParams([
|
|
18392
|
-
new StringChunk("("),
|
|
18393
|
-
chunk._.sql,
|
|
18394
|
-
new StringChunk(") "),
|
|
18395
|
-
new Name(chunk._.alias)
|
|
18396
|
-
], config);
|
|
18397
|
-
}
|
|
18398
|
-
if (isPgEnum(chunk)) {
|
|
18399
|
-
if (chunk.schema) {
|
|
18400
|
-
return { sql: escapeName(chunk.schema) + "." + escapeName(chunk.enumName), params: [] };
|
|
18401
|
-
}
|
|
18402
|
-
return { sql: escapeName(chunk.enumName), params: [] };
|
|
18403
|
-
}
|
|
18404
|
-
if (isSQLWrapper(chunk)) {
|
|
18405
|
-
if (chunk.shouldOmitSQLParens?.()) {
|
|
18406
|
-
return this.buildQueryFromSourceParams([chunk.getSQL()], config);
|
|
18407
|
-
}
|
|
18408
|
-
return this.buildQueryFromSourceParams([
|
|
18409
|
-
new StringChunk("("),
|
|
18410
|
-
chunk.getSQL(),
|
|
18411
|
-
new StringChunk(")")
|
|
18412
|
-
], config);
|
|
18413
|
-
}
|
|
18414
|
-
if (inlineParams) {
|
|
18415
|
-
return { sql: this.mapInlineParam(chunk, config), params: [] };
|
|
18416
|
-
}
|
|
18417
|
-
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
|
|
18418
|
-
}));
|
|
18419
|
-
}
|
|
18420
|
-
mapInlineParam(chunk, { escapeString }) {
|
|
18421
|
-
if (chunk === null) {
|
|
18422
|
-
return "null";
|
|
18423
|
-
}
|
|
18424
|
-
if (typeof chunk === "number" || typeof chunk === "boolean") {
|
|
18425
|
-
return chunk.toString();
|
|
18426
|
-
}
|
|
18427
|
-
if (typeof chunk === "string") {
|
|
18428
|
-
return escapeString(chunk);
|
|
18429
|
-
}
|
|
18430
|
-
if (typeof chunk === "object") {
|
|
18431
|
-
const mappedValueAsString = chunk.toString();
|
|
18432
|
-
if (mappedValueAsString === "[object Object]") {
|
|
18433
|
-
return escapeString(JSON.stringify(chunk));
|
|
18434
|
-
}
|
|
18435
|
-
return escapeString(mappedValueAsString);
|
|
18436
|
-
}
|
|
18437
|
-
throw new Error("Unexpected param value: " + chunk);
|
|
18438
|
-
}
|
|
18439
|
-
getSQL() {
|
|
18440
|
-
return this;
|
|
18441
|
-
}
|
|
18442
|
-
as(alias) {
|
|
18443
|
-
if (alias === undefined) {
|
|
18444
|
-
return this;
|
|
18445
|
-
}
|
|
18446
|
-
return new SQL.Aliased(this, alias);
|
|
18447
|
-
}
|
|
18448
|
-
mapWith(decoder) {
|
|
18449
|
-
this.decoder = typeof decoder === "function" ? { mapFromDriverValue: decoder } : decoder;
|
|
18450
|
-
return this;
|
|
18451
|
-
}
|
|
18452
|
-
inlineParams() {
|
|
18453
|
-
this.shouldInlineParams = true;
|
|
18454
|
-
return this;
|
|
18455
|
-
}
|
|
18456
|
-
if(condition) {
|
|
18457
|
-
return condition ? this : undefined;
|
|
18458
|
-
}
|
|
18459
|
-
}
|
|
18460
|
-
|
|
18461
|
-
class Name {
|
|
18462
|
-
constructor(value) {
|
|
18463
|
-
this.value = value;
|
|
18464
|
-
}
|
|
18465
|
-
static [entityKind] = "Name";
|
|
18466
|
-
brand;
|
|
18467
|
-
getSQL() {
|
|
18468
|
-
return new SQL([this]);
|
|
18469
|
-
}
|
|
18470
|
-
}
|
|
18471
|
-
var noopDecoder = {
|
|
18472
|
-
mapFromDriverValue: (value) => value
|
|
18473
|
-
};
|
|
18474
|
-
var noopEncoder = {
|
|
18475
|
-
mapToDriverValue: (value) => value
|
|
18476
|
-
};
|
|
18477
|
-
var noopMapper = {
|
|
18478
|
-
...noopDecoder,
|
|
18479
|
-
...noopEncoder
|
|
18480
|
-
};
|
|
18481
|
-
|
|
18482
|
-
class Param {
|
|
18483
|
-
constructor(value, encoder = noopEncoder) {
|
|
18484
|
-
this.value = value;
|
|
18485
|
-
this.encoder = encoder;
|
|
18486
|
-
}
|
|
18487
|
-
static [entityKind] = "Param";
|
|
18488
|
-
brand;
|
|
18489
|
-
getSQL() {
|
|
18490
|
-
return new SQL([this]);
|
|
18491
|
-
}
|
|
18492
|
-
}
|
|
18493
|
-
function sql(strings, ...params) {
|
|
18494
|
-
const queryChunks = [];
|
|
18495
|
-
if (params.length > 0 || strings.length > 0 && strings[0] !== "") {
|
|
18496
|
-
queryChunks.push(new StringChunk(strings[0]));
|
|
18497
|
-
}
|
|
18498
|
-
for (const [paramIndex, param2] of params.entries()) {
|
|
18499
|
-
queryChunks.push(param2, new StringChunk(strings[paramIndex + 1]));
|
|
18500
|
-
}
|
|
18501
|
-
return new SQL(queryChunks);
|
|
18502
|
-
}
|
|
18503
|
-
((sql2) => {
|
|
18504
|
-
function empty() {
|
|
18505
|
-
return new SQL([]);
|
|
18506
|
-
}
|
|
18507
|
-
sql2.empty = empty;
|
|
18508
|
-
function fromList(list) {
|
|
18509
|
-
return new SQL(list);
|
|
18510
|
-
}
|
|
18511
|
-
sql2.fromList = fromList;
|
|
18512
|
-
function raw2(str) {
|
|
18513
|
-
return new SQL([new StringChunk(str)]);
|
|
18514
|
-
}
|
|
18515
|
-
sql2.raw = raw2;
|
|
18516
|
-
function join18(chunks, separator) {
|
|
18517
|
-
const result = [];
|
|
18518
|
-
for (const [i, chunk] of chunks.entries()) {
|
|
18519
|
-
if (i > 0 && separator !== undefined) {
|
|
18520
|
-
result.push(separator);
|
|
18521
|
-
}
|
|
18522
|
-
result.push(chunk);
|
|
18523
|
-
}
|
|
18524
|
-
return new SQL(result);
|
|
18525
|
-
}
|
|
18526
|
-
sql2.join = join18;
|
|
18527
|
-
function identifier(value) {
|
|
18528
|
-
return new Name(value);
|
|
18529
|
-
}
|
|
18530
|
-
sql2.identifier = identifier;
|
|
18531
|
-
function placeholder2(name2) {
|
|
18532
|
-
return new Placeholder(name2);
|
|
18533
|
-
}
|
|
18534
|
-
sql2.placeholder = placeholder2;
|
|
18535
|
-
function param2(value, encoder) {
|
|
18536
|
-
return new Param(value, encoder);
|
|
18537
|
-
}
|
|
18538
|
-
sql2.param = param2;
|
|
18539
|
-
})(sql || (sql = {}));
|
|
18540
|
-
((SQL2) => {
|
|
18541
|
-
|
|
18542
|
-
class Aliased {
|
|
18543
|
-
constructor(sql2, fieldAlias) {
|
|
18544
|
-
this.sql = sql2;
|
|
18545
|
-
this.fieldAlias = fieldAlias;
|
|
18546
|
-
}
|
|
18547
|
-
static [entityKind] = "SQL.Aliased";
|
|
18548
|
-
isSelectionField = false;
|
|
18549
|
-
getSQL() {
|
|
18550
|
-
return this.sql;
|
|
18551
|
-
}
|
|
18552
|
-
clone() {
|
|
18553
|
-
return new Aliased(this.sql, this.fieldAlias);
|
|
18554
|
-
}
|
|
18555
|
-
}
|
|
18556
|
-
SQL2.Aliased = Aliased;
|
|
18557
|
-
})(SQL || (SQL = {}));
|
|
18558
|
-
|
|
18559
|
-
class Placeholder {
|
|
18560
|
-
constructor(name2) {
|
|
18561
|
-
this.name = name2;
|
|
18562
|
-
}
|
|
18563
|
-
static [entityKind] = "Placeholder";
|
|
18564
|
-
getSQL() {
|
|
18565
|
-
return new SQL([this]);
|
|
18566
|
-
}
|
|
18567
|
-
}
|
|
18568
|
-
var IsDrizzleView = Symbol.for("drizzle:IsDrizzleView");
|
|
18569
|
-
|
|
18570
|
-
class View {
|
|
18571
|
-
static [entityKind] = "View";
|
|
18572
|
-
[ViewBaseConfig];
|
|
18573
|
-
[IsDrizzleView] = true;
|
|
18574
|
-
constructor({ name: name2, schema, selectedFields, query }) {
|
|
18575
|
-
this[ViewBaseConfig] = {
|
|
18576
|
-
name: name2,
|
|
18577
|
-
originalName: name2,
|
|
18578
|
-
schema,
|
|
18579
|
-
selectedFields,
|
|
18580
|
-
query,
|
|
18581
|
-
isExisting: !query,
|
|
18582
|
-
isAlias: false
|
|
18583
|
-
};
|
|
18584
|
-
}
|
|
18585
|
-
getSQL() {
|
|
18586
|
-
return new SQL([this]);
|
|
18587
|
-
}
|
|
18588
|
-
}
|
|
18589
|
-
Column.prototype.getSQL = function() {
|
|
18590
|
-
return new SQL([this]);
|
|
18591
|
-
};
|
|
18592
|
-
Table.prototype.getSQL = function() {
|
|
18593
|
-
return new SQL([this]);
|
|
18594
|
-
};
|
|
18595
|
-
Subquery.prototype.getSQL = function() {
|
|
18596
|
-
return new SQL([this]);
|
|
18597
|
-
};
|
|
18598
|
-
|
|
18599
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/utils.js
|
|
18600
|
-
function getColumnNameAndConfig(a, b) {
|
|
18601
|
-
return {
|
|
18602
|
-
name: typeof a === "string" && a.length > 0 ? a : "",
|
|
18603
|
-
config: typeof a === "object" ? a : b
|
|
18604
|
-
};
|
|
18605
|
-
}
|
|
18606
|
-
var textDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder;
|
|
18607
|
-
|
|
18608
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
|
|
18609
|
-
class ForeignKeyBuilder {
|
|
18610
|
-
static [entityKind] = "SQLiteForeignKeyBuilder";
|
|
18611
|
-
reference;
|
|
18612
|
-
_onUpdate;
|
|
18613
|
-
_onDelete;
|
|
18614
|
-
constructor(config, actions) {
|
|
18615
|
-
this.reference = () => {
|
|
18616
|
-
const { name, columns, foreignColumns } = config();
|
|
18617
|
-
return { name, columns, foreignTable: foreignColumns[0].table, foreignColumns };
|
|
18618
|
-
};
|
|
18619
|
-
if (actions) {
|
|
18620
|
-
this._onUpdate = actions.onUpdate;
|
|
18621
|
-
this._onDelete = actions.onDelete;
|
|
18622
|
-
}
|
|
18623
|
-
}
|
|
18624
|
-
onUpdate(action) {
|
|
18625
|
-
this._onUpdate = action;
|
|
18626
|
-
return this;
|
|
18627
|
-
}
|
|
18628
|
-
onDelete(action) {
|
|
18629
|
-
this._onDelete = action;
|
|
18630
|
-
return this;
|
|
18631
|
-
}
|
|
18632
|
-
build(table) {
|
|
18633
|
-
return new ForeignKey(table, this);
|
|
18634
|
-
}
|
|
18635
|
-
}
|
|
18636
|
-
|
|
18637
|
-
class ForeignKey {
|
|
18638
|
-
constructor(table, builder) {
|
|
18639
|
-
this.table = table;
|
|
18640
|
-
this.reference = builder.reference;
|
|
18641
|
-
this.onUpdate = builder._onUpdate;
|
|
18642
|
-
this.onDelete = builder._onDelete;
|
|
18643
|
-
}
|
|
18644
|
-
static [entityKind] = "SQLiteForeignKey";
|
|
18645
|
-
reference;
|
|
18646
|
-
onUpdate;
|
|
18647
|
-
onDelete;
|
|
18648
|
-
getName() {
|
|
18649
|
-
const { name, columns, foreignColumns } = this.reference();
|
|
18650
|
-
const columnNames = columns.map((column) => column.name);
|
|
18651
|
-
const foreignColumnNames = foreignColumns.map((column) => column.name);
|
|
18652
|
-
const chunks = [
|
|
18653
|
-
this.table[TableName],
|
|
18654
|
-
...columnNames,
|
|
18655
|
-
foreignColumns[0].table[TableName],
|
|
18656
|
-
...foreignColumnNames
|
|
18657
|
-
];
|
|
18658
|
-
return name ?? `${chunks.join("_")}_fk`;
|
|
18659
|
-
}
|
|
18660
|
-
}
|
|
18661
|
-
|
|
18662
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/unique-constraint.js
|
|
18663
|
-
function uniqueKeyName2(table, columns) {
|
|
18664
|
-
return `${table[TableName]}_${columns.join("_")}_unique`;
|
|
18665
|
-
}
|
|
18666
|
-
|
|
18667
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/common.js
|
|
18668
|
-
class SQLiteColumnBuilder extends ColumnBuilder {
|
|
18669
|
-
static [entityKind] = "SQLiteColumnBuilder";
|
|
18670
|
-
foreignKeyConfigs = [];
|
|
18671
|
-
references(ref, actions = {}) {
|
|
18672
|
-
this.foreignKeyConfigs.push({ ref, actions });
|
|
18673
|
-
return this;
|
|
18674
|
-
}
|
|
18675
|
-
unique(name) {
|
|
18676
|
-
this.config.isUnique = true;
|
|
18677
|
-
this.config.uniqueName = name;
|
|
18678
|
-
return this;
|
|
18679
|
-
}
|
|
18680
|
-
generatedAlwaysAs(as, config) {
|
|
18681
|
-
this.config.generated = {
|
|
18682
|
-
as,
|
|
18683
|
-
type: "always",
|
|
18684
|
-
mode: config?.mode ?? "virtual"
|
|
18685
|
-
};
|
|
18686
|
-
return this;
|
|
18687
|
-
}
|
|
18688
|
-
buildForeignKeys(column, table) {
|
|
18689
|
-
return this.foreignKeyConfigs.map(({ ref, actions }) => {
|
|
18690
|
-
return ((ref2, actions2) => {
|
|
18691
|
-
const builder = new ForeignKeyBuilder(() => {
|
|
18692
|
-
const foreignColumn = ref2();
|
|
18693
|
-
return { columns: [column], foreignColumns: [foreignColumn] };
|
|
18694
|
-
});
|
|
18695
|
-
if (actions2.onUpdate) {
|
|
18696
|
-
builder.onUpdate(actions2.onUpdate);
|
|
18697
|
-
}
|
|
18698
|
-
if (actions2.onDelete) {
|
|
18699
|
-
builder.onDelete(actions2.onDelete);
|
|
18700
|
-
}
|
|
18701
|
-
return builder.build(table);
|
|
18702
|
-
})(ref, actions);
|
|
18703
|
-
});
|
|
18704
|
-
}
|
|
18705
|
-
}
|
|
18706
|
-
|
|
18707
|
-
class SQLiteColumn extends Column {
|
|
18708
|
-
constructor(table, config) {
|
|
18709
|
-
if (!config.uniqueName) {
|
|
18710
|
-
config.uniqueName = uniqueKeyName2(table, [config.name]);
|
|
18711
|
-
}
|
|
18712
|
-
super(table, config);
|
|
18713
|
-
this.table = table;
|
|
18714
|
-
}
|
|
18715
|
-
static [entityKind] = "SQLiteColumn";
|
|
18716
|
-
}
|
|
18717
|
-
|
|
18718
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/blob.js
|
|
18719
|
-
class SQLiteBigIntBuilder extends SQLiteColumnBuilder {
|
|
18720
|
-
static [entityKind] = "SQLiteBigIntBuilder";
|
|
18721
|
-
constructor(name) {
|
|
18722
|
-
super(name, "bigint", "SQLiteBigInt");
|
|
18723
|
-
}
|
|
18724
|
-
build(table) {
|
|
18725
|
-
return new SQLiteBigInt(table, this.config);
|
|
18726
|
-
}
|
|
18727
|
-
}
|
|
18728
|
-
|
|
18729
|
-
class SQLiteBigInt extends SQLiteColumn {
|
|
18730
|
-
static [entityKind] = "SQLiteBigInt";
|
|
18731
|
-
getSQLType() {
|
|
18732
|
-
return "blob";
|
|
18733
|
-
}
|
|
18734
|
-
mapFromDriverValue(value) {
|
|
18735
|
-
if (typeof Buffer !== "undefined" && Buffer.from) {
|
|
18736
|
-
const buf = Buffer.isBuffer(value) ? value : value instanceof ArrayBuffer ? Buffer.from(value) : value.buffer ? Buffer.from(value.buffer, value.byteOffset, value.byteLength) : Buffer.from(value);
|
|
18737
|
-
return BigInt(buf.toString("utf8"));
|
|
18738
|
-
}
|
|
18739
|
-
return BigInt(textDecoder.decode(value));
|
|
18740
|
-
}
|
|
18741
|
-
mapToDriverValue(value) {
|
|
18742
|
-
return Buffer.from(value.toString());
|
|
18743
|
-
}
|
|
18744
|
-
}
|
|
18745
|
-
|
|
18746
|
-
class SQLiteBlobJsonBuilder extends SQLiteColumnBuilder {
|
|
18747
|
-
static [entityKind] = "SQLiteBlobJsonBuilder";
|
|
18748
|
-
constructor(name) {
|
|
18749
|
-
super(name, "json", "SQLiteBlobJson");
|
|
18750
|
-
}
|
|
18751
|
-
build(table) {
|
|
18752
|
-
return new SQLiteBlobJson(table, this.config);
|
|
18753
|
-
}
|
|
18754
|
-
}
|
|
18755
|
-
|
|
18756
|
-
class SQLiteBlobJson extends SQLiteColumn {
|
|
18757
|
-
static [entityKind] = "SQLiteBlobJson";
|
|
18758
|
-
getSQLType() {
|
|
18759
|
-
return "blob";
|
|
18760
|
-
}
|
|
18761
|
-
mapFromDriverValue(value) {
|
|
18762
|
-
if (typeof Buffer !== "undefined" && Buffer.from) {
|
|
18763
|
-
const buf = Buffer.isBuffer(value) ? value : value instanceof ArrayBuffer ? Buffer.from(value) : value.buffer ? Buffer.from(value.buffer, value.byteOffset, value.byteLength) : Buffer.from(value);
|
|
18764
|
-
return JSON.parse(buf.toString("utf8"));
|
|
18765
|
-
}
|
|
18766
|
-
return JSON.parse(textDecoder.decode(value));
|
|
18767
|
-
}
|
|
18768
|
-
mapToDriverValue(value) {
|
|
18769
|
-
return Buffer.from(JSON.stringify(value));
|
|
18770
|
-
}
|
|
18771
|
-
}
|
|
18772
|
-
|
|
18773
|
-
class SQLiteBlobBufferBuilder extends SQLiteColumnBuilder {
|
|
18774
|
-
static [entityKind] = "SQLiteBlobBufferBuilder";
|
|
18775
|
-
constructor(name) {
|
|
18776
|
-
super(name, "buffer", "SQLiteBlobBuffer");
|
|
18777
|
-
}
|
|
18778
|
-
build(table) {
|
|
18779
|
-
return new SQLiteBlobBuffer(table, this.config);
|
|
18780
|
-
}
|
|
18781
|
-
}
|
|
18782
|
-
|
|
18783
|
-
class SQLiteBlobBuffer extends SQLiteColumn {
|
|
18784
|
-
static [entityKind] = "SQLiteBlobBuffer";
|
|
18785
|
-
mapFromDriverValue(value) {
|
|
18786
|
-
if (Buffer.isBuffer(value)) {
|
|
18787
|
-
return value;
|
|
18788
|
-
}
|
|
18789
|
-
return Buffer.from(value);
|
|
18790
|
-
}
|
|
18791
|
-
getSQLType() {
|
|
18792
|
-
return "blob";
|
|
18793
|
-
}
|
|
18794
|
-
}
|
|
18795
|
-
function blob(a, b) {
|
|
18796
|
-
const { name, config } = getColumnNameAndConfig(a, b);
|
|
18797
|
-
if (config?.mode === "json") {
|
|
18798
|
-
return new SQLiteBlobJsonBuilder(name);
|
|
18799
|
-
}
|
|
18800
|
-
if (config?.mode === "bigint") {
|
|
18801
|
-
return new SQLiteBigIntBuilder(name);
|
|
18802
|
-
}
|
|
18803
|
-
return new SQLiteBlobBufferBuilder(name);
|
|
18804
|
-
}
|
|
18805
|
-
|
|
18806
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/custom.js
|
|
18807
|
-
class SQLiteCustomColumnBuilder extends SQLiteColumnBuilder {
|
|
18808
|
-
static [entityKind] = "SQLiteCustomColumnBuilder";
|
|
18809
|
-
constructor(name, fieldConfig, customTypeParams) {
|
|
18810
|
-
super(name, "custom", "SQLiteCustomColumn");
|
|
18811
|
-
this.config.fieldConfig = fieldConfig;
|
|
18812
|
-
this.config.customTypeParams = customTypeParams;
|
|
18813
|
-
}
|
|
18814
|
-
build(table) {
|
|
18815
|
-
return new SQLiteCustomColumn(table, this.config);
|
|
18816
|
-
}
|
|
18817
|
-
}
|
|
18818
|
-
|
|
18819
|
-
class SQLiteCustomColumn extends SQLiteColumn {
|
|
18820
|
-
static [entityKind] = "SQLiteCustomColumn";
|
|
18821
|
-
sqlName;
|
|
18822
|
-
mapTo;
|
|
18823
|
-
mapFrom;
|
|
18824
|
-
constructor(table, config) {
|
|
18825
|
-
super(table, config);
|
|
18826
|
-
this.sqlName = config.customTypeParams.dataType(config.fieldConfig);
|
|
18827
|
-
this.mapTo = config.customTypeParams.toDriver;
|
|
18828
|
-
this.mapFrom = config.customTypeParams.fromDriver;
|
|
18829
|
-
}
|
|
18830
|
-
getSQLType() {
|
|
18831
|
-
return this.sqlName;
|
|
18832
|
-
}
|
|
18833
|
-
mapFromDriverValue(value) {
|
|
18834
|
-
return typeof this.mapFrom === "function" ? this.mapFrom(value) : value;
|
|
18835
|
-
}
|
|
18836
|
-
mapToDriverValue(value) {
|
|
18837
|
-
return typeof this.mapTo === "function" ? this.mapTo(value) : value;
|
|
18838
|
-
}
|
|
18839
|
-
}
|
|
18840
|
-
function customType(customTypeParams) {
|
|
18841
|
-
return (a, b) => {
|
|
18842
|
-
const { name, config } = getColumnNameAndConfig(a, b);
|
|
18843
|
-
return new SQLiteCustomColumnBuilder(name, config, customTypeParams);
|
|
18844
|
-
};
|
|
18845
|
-
}
|
|
18846
|
-
|
|
18847
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/integer.js
|
|
18848
|
-
class SQLiteBaseIntegerBuilder extends SQLiteColumnBuilder {
|
|
18849
|
-
static [entityKind] = "SQLiteBaseIntegerBuilder";
|
|
18850
|
-
constructor(name, dataType, columnType) {
|
|
18851
|
-
super(name, dataType, columnType);
|
|
18852
|
-
this.config.autoIncrement = false;
|
|
18853
|
-
}
|
|
18854
|
-
primaryKey(config) {
|
|
18855
|
-
if (config?.autoIncrement) {
|
|
18856
|
-
this.config.autoIncrement = true;
|
|
18857
|
-
}
|
|
18858
|
-
this.config.hasDefault = true;
|
|
18859
|
-
return super.primaryKey();
|
|
18860
|
-
}
|
|
18861
|
-
}
|
|
18862
|
-
|
|
18863
|
-
class SQLiteBaseInteger extends SQLiteColumn {
|
|
18864
|
-
static [entityKind] = "SQLiteBaseInteger";
|
|
18865
|
-
autoIncrement = this.config.autoIncrement;
|
|
18866
|
-
getSQLType() {
|
|
18867
|
-
return "integer";
|
|
18868
|
-
}
|
|
18869
|
-
}
|
|
18870
|
-
|
|
18871
|
-
class SQLiteIntegerBuilder extends SQLiteBaseIntegerBuilder {
|
|
18872
|
-
static [entityKind] = "SQLiteIntegerBuilder";
|
|
18873
|
-
constructor(name) {
|
|
18874
|
-
super(name, "number", "SQLiteInteger");
|
|
18875
|
-
}
|
|
18876
|
-
build(table) {
|
|
18877
|
-
return new SQLiteInteger(table, this.config);
|
|
18878
|
-
}
|
|
18879
|
-
}
|
|
18880
|
-
|
|
18881
|
-
class SQLiteInteger extends SQLiteBaseInteger {
|
|
18882
|
-
static [entityKind] = "SQLiteInteger";
|
|
18883
|
-
}
|
|
18884
|
-
|
|
18885
|
-
class SQLiteTimestampBuilder extends SQLiteBaseIntegerBuilder {
|
|
18886
|
-
static [entityKind] = "SQLiteTimestampBuilder";
|
|
18887
|
-
constructor(name, mode) {
|
|
18888
|
-
super(name, "date", "SQLiteTimestamp");
|
|
18889
|
-
this.config.mode = mode;
|
|
18890
|
-
}
|
|
18891
|
-
defaultNow() {
|
|
18892
|
-
return this.default(sql`(cast((julianday('now') - 2440587.5)*86400000 as integer))`);
|
|
18893
|
-
}
|
|
18894
|
-
build(table) {
|
|
18895
|
-
return new SQLiteTimestamp(table, this.config);
|
|
18896
|
-
}
|
|
18897
|
-
}
|
|
18898
|
-
|
|
18899
|
-
class SQLiteTimestamp extends SQLiteBaseInteger {
|
|
18900
|
-
static [entityKind] = "SQLiteTimestamp";
|
|
18901
|
-
mode = this.config.mode;
|
|
18902
|
-
mapFromDriverValue(value) {
|
|
18903
|
-
if (this.config.mode === "timestamp") {
|
|
18904
|
-
return new Date(value * 1000);
|
|
18905
|
-
}
|
|
18906
|
-
return new Date(value);
|
|
18907
|
-
}
|
|
18908
|
-
mapToDriverValue(value) {
|
|
18909
|
-
const unix = value.getTime();
|
|
18910
|
-
if (this.config.mode === "timestamp") {
|
|
18911
|
-
return Math.floor(unix / 1000);
|
|
18912
|
-
}
|
|
18913
|
-
return unix;
|
|
18914
|
-
}
|
|
18915
|
-
}
|
|
18916
|
-
|
|
18917
|
-
class SQLiteBooleanBuilder extends SQLiteBaseIntegerBuilder {
|
|
18918
|
-
static [entityKind] = "SQLiteBooleanBuilder";
|
|
18919
|
-
constructor(name, mode) {
|
|
18920
|
-
super(name, "boolean", "SQLiteBoolean");
|
|
18921
|
-
this.config.mode = mode;
|
|
18922
|
-
}
|
|
18923
|
-
build(table) {
|
|
18924
|
-
return new SQLiteBoolean(table, this.config);
|
|
18925
|
-
}
|
|
18926
|
-
}
|
|
18927
|
-
|
|
18928
|
-
class SQLiteBoolean extends SQLiteBaseInteger {
|
|
18929
|
-
static [entityKind] = "SQLiteBoolean";
|
|
18930
|
-
mode = this.config.mode;
|
|
18931
|
-
mapFromDriverValue(value) {
|
|
18932
|
-
return Number(value) === 1;
|
|
18933
|
-
}
|
|
18934
|
-
mapToDriverValue(value) {
|
|
18935
|
-
return value ? 1 : 0;
|
|
18936
|
-
}
|
|
18937
|
-
}
|
|
18938
|
-
function integer(a, b) {
|
|
18939
|
-
const { name, config } = getColumnNameAndConfig(a, b);
|
|
18940
|
-
if (config?.mode === "timestamp" || config?.mode === "timestamp_ms") {
|
|
18941
|
-
return new SQLiteTimestampBuilder(name, config.mode);
|
|
18942
|
-
}
|
|
18943
|
-
if (config?.mode === "boolean") {
|
|
18944
|
-
return new SQLiteBooleanBuilder(name, config.mode);
|
|
18945
|
-
}
|
|
18946
|
-
return new SQLiteIntegerBuilder(name);
|
|
18947
|
-
}
|
|
18948
|
-
|
|
18949
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/numeric.js
|
|
18950
|
-
class SQLiteNumericBuilder extends SQLiteColumnBuilder {
|
|
18951
|
-
static [entityKind] = "SQLiteNumericBuilder";
|
|
18952
|
-
constructor(name) {
|
|
18953
|
-
super(name, "string", "SQLiteNumeric");
|
|
18954
|
-
}
|
|
18955
|
-
build(table) {
|
|
18956
|
-
return new SQLiteNumeric(table, this.config);
|
|
18957
|
-
}
|
|
18958
|
-
}
|
|
18959
|
-
|
|
18960
|
-
class SQLiteNumeric extends SQLiteColumn {
|
|
18961
|
-
static [entityKind] = "SQLiteNumeric";
|
|
18962
|
-
mapFromDriverValue(value) {
|
|
18963
|
-
if (typeof value === "string")
|
|
18964
|
-
return value;
|
|
18965
|
-
return String(value);
|
|
18966
|
-
}
|
|
18967
|
-
getSQLType() {
|
|
18968
|
-
return "numeric";
|
|
18969
|
-
}
|
|
18970
|
-
}
|
|
18971
|
-
|
|
18972
|
-
class SQLiteNumericNumberBuilder extends SQLiteColumnBuilder {
|
|
18973
|
-
static [entityKind] = "SQLiteNumericNumberBuilder";
|
|
18974
|
-
constructor(name) {
|
|
18975
|
-
super(name, "number", "SQLiteNumericNumber");
|
|
18976
|
-
}
|
|
18977
|
-
build(table) {
|
|
18978
|
-
return new SQLiteNumericNumber(table, this.config);
|
|
18979
|
-
}
|
|
18980
|
-
}
|
|
18981
|
-
|
|
18982
|
-
class SQLiteNumericNumber extends SQLiteColumn {
|
|
18983
|
-
static [entityKind] = "SQLiteNumericNumber";
|
|
18984
|
-
mapFromDriverValue(value) {
|
|
18985
|
-
if (typeof value === "number")
|
|
18986
|
-
return value;
|
|
18987
|
-
return Number(value);
|
|
18988
|
-
}
|
|
18989
|
-
mapToDriverValue = String;
|
|
18990
|
-
getSQLType() {
|
|
18991
|
-
return "numeric";
|
|
18992
|
-
}
|
|
18993
|
-
}
|
|
18994
|
-
|
|
18995
|
-
class SQLiteNumericBigIntBuilder extends SQLiteColumnBuilder {
|
|
18996
|
-
static [entityKind] = "SQLiteNumericBigIntBuilder";
|
|
18997
|
-
constructor(name) {
|
|
18998
|
-
super(name, "bigint", "SQLiteNumericBigInt");
|
|
18999
|
-
}
|
|
19000
|
-
build(table) {
|
|
19001
|
-
return new SQLiteNumericBigInt(table, this.config);
|
|
19002
|
-
}
|
|
19003
|
-
}
|
|
19004
|
-
|
|
19005
|
-
class SQLiteNumericBigInt extends SQLiteColumn {
|
|
19006
|
-
static [entityKind] = "SQLiteNumericBigInt";
|
|
19007
|
-
mapFromDriverValue = BigInt;
|
|
19008
|
-
mapToDriverValue = String;
|
|
19009
|
-
getSQLType() {
|
|
19010
|
-
return "numeric";
|
|
19011
|
-
}
|
|
19012
|
-
}
|
|
19013
|
-
function numeric(a, b) {
|
|
19014
|
-
const { name, config } = getColumnNameAndConfig(a, b);
|
|
19015
|
-
const mode = config?.mode;
|
|
19016
|
-
return mode === "number" ? new SQLiteNumericNumberBuilder(name) : mode === "bigint" ? new SQLiteNumericBigIntBuilder(name) : new SQLiteNumericBuilder(name);
|
|
19017
|
-
}
|
|
19018
|
-
|
|
19019
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/real.js
|
|
19020
|
-
class SQLiteRealBuilder extends SQLiteColumnBuilder {
|
|
19021
|
-
static [entityKind] = "SQLiteRealBuilder";
|
|
19022
|
-
constructor(name) {
|
|
19023
|
-
super(name, "number", "SQLiteReal");
|
|
19024
|
-
}
|
|
19025
|
-
build(table) {
|
|
19026
|
-
return new SQLiteReal(table, this.config);
|
|
19027
|
-
}
|
|
19028
|
-
}
|
|
19029
|
-
|
|
19030
|
-
class SQLiteReal extends SQLiteColumn {
|
|
19031
|
-
static [entityKind] = "SQLiteReal";
|
|
19032
|
-
getSQLType() {
|
|
19033
|
-
return "real";
|
|
19034
|
-
}
|
|
19035
|
-
}
|
|
19036
|
-
function real(name) {
|
|
19037
|
-
return new SQLiteRealBuilder(name ?? "");
|
|
19038
|
-
}
|
|
19039
|
-
|
|
19040
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/text.js
|
|
19041
|
-
class SQLiteTextBuilder extends SQLiteColumnBuilder {
|
|
19042
|
-
static [entityKind] = "SQLiteTextBuilder";
|
|
19043
|
-
constructor(name, config) {
|
|
19044
|
-
super(name, "string", "SQLiteText");
|
|
19045
|
-
this.config.enumValues = config.enum;
|
|
19046
|
-
this.config.length = config.length;
|
|
19047
|
-
}
|
|
19048
|
-
build(table) {
|
|
19049
|
-
return new SQLiteText(table, this.config);
|
|
19050
|
-
}
|
|
19051
|
-
}
|
|
19052
|
-
|
|
19053
|
-
class SQLiteText extends SQLiteColumn {
|
|
19054
|
-
static [entityKind] = "SQLiteText";
|
|
19055
|
-
enumValues = this.config.enumValues;
|
|
19056
|
-
length = this.config.length;
|
|
19057
|
-
constructor(table, config) {
|
|
19058
|
-
super(table, config);
|
|
19059
|
-
}
|
|
19060
|
-
getSQLType() {
|
|
19061
|
-
return `text${this.config.length ? `(${this.config.length})` : ""}`;
|
|
19062
|
-
}
|
|
19063
|
-
}
|
|
19064
|
-
|
|
19065
|
-
class SQLiteTextJsonBuilder extends SQLiteColumnBuilder {
|
|
19066
|
-
static [entityKind] = "SQLiteTextJsonBuilder";
|
|
19067
|
-
constructor(name) {
|
|
19068
|
-
super(name, "json", "SQLiteTextJson");
|
|
19069
|
-
}
|
|
19070
|
-
build(table) {
|
|
19071
|
-
return new SQLiteTextJson(table, this.config);
|
|
19072
|
-
}
|
|
19073
|
-
}
|
|
19074
|
-
|
|
19075
|
-
class SQLiteTextJson extends SQLiteColumn {
|
|
19076
|
-
static [entityKind] = "SQLiteTextJson";
|
|
19077
|
-
getSQLType() {
|
|
19078
|
-
return "text";
|
|
19079
|
-
}
|
|
19080
|
-
mapFromDriverValue(value) {
|
|
19081
|
-
return JSON.parse(value);
|
|
19082
|
-
}
|
|
19083
|
-
mapToDriverValue(value) {
|
|
19084
|
-
return JSON.stringify(value);
|
|
19085
|
-
}
|
|
19086
|
-
}
|
|
19087
|
-
function text(a, b = {}) {
|
|
19088
|
-
const { name, config } = getColumnNameAndConfig(a, b);
|
|
19089
|
-
if (config.mode === "json") {
|
|
19090
|
-
return new SQLiteTextJsonBuilder(name);
|
|
19091
|
-
}
|
|
19092
|
-
return new SQLiteTextBuilder(name, config);
|
|
19093
|
-
}
|
|
19094
|
-
|
|
19095
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/columns/all.js
|
|
19096
|
-
function getSQLiteColumnBuilders() {
|
|
19097
|
-
return {
|
|
19098
|
-
blob,
|
|
19099
|
-
customType,
|
|
19100
|
-
integer,
|
|
19101
|
-
numeric,
|
|
19102
|
-
real,
|
|
19103
|
-
text
|
|
19104
|
-
};
|
|
19105
|
-
}
|
|
19106
|
-
|
|
19107
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/table.js
|
|
19108
|
-
var InlineForeignKeys = Symbol.for("drizzle:SQLiteInlineForeignKeys");
|
|
19109
|
-
|
|
19110
|
-
class SQLiteTable extends Table {
|
|
19111
|
-
static [entityKind] = "SQLiteTable";
|
|
19112
|
-
static Symbol = Object.assign({}, Table.Symbol, {
|
|
19113
|
-
InlineForeignKeys
|
|
19114
|
-
});
|
|
19115
|
-
[Table.Symbol.Columns];
|
|
19116
|
-
[InlineForeignKeys] = [];
|
|
19117
|
-
[Table.Symbol.ExtraConfigBuilder] = undefined;
|
|
19118
|
-
}
|
|
19119
|
-
function sqliteTableBase(name, columns, extraConfig, schema, baseName = name) {
|
|
19120
|
-
const rawTable = new SQLiteTable(name, schema, baseName);
|
|
19121
|
-
const parsedColumns = typeof columns === "function" ? columns(getSQLiteColumnBuilders()) : columns;
|
|
19122
|
-
const builtColumns = Object.fromEntries(Object.entries(parsedColumns).map(([name2, colBuilderBase]) => {
|
|
19123
|
-
const colBuilder = colBuilderBase;
|
|
19124
|
-
colBuilder.setName(name2);
|
|
19125
|
-
const column = colBuilder.build(rawTable);
|
|
19126
|
-
rawTable[InlineForeignKeys].push(...colBuilder.buildForeignKeys(column, rawTable));
|
|
19127
|
-
return [name2, column];
|
|
19128
|
-
}));
|
|
19129
|
-
const table = Object.assign(rawTable, builtColumns);
|
|
19130
|
-
table[Table.Symbol.Columns] = builtColumns;
|
|
19131
|
-
table[Table.Symbol.ExtraConfigColumns] = builtColumns;
|
|
19132
|
-
if (extraConfig) {
|
|
19133
|
-
table[SQLiteTable.Symbol.ExtraConfigBuilder] = extraConfig;
|
|
19134
|
-
}
|
|
19135
|
-
return table;
|
|
19136
|
-
}
|
|
19137
|
-
var sqliteTable = (name, columns, extraConfig) => {
|
|
19138
|
-
return sqliteTableBase(name, columns, extraConfig);
|
|
19139
|
-
};
|
|
19140
|
-
|
|
19141
|
-
// node_modules/.bun/drizzle-orm@0.45.2+29ab9a45addfc34e/node_modules/drizzle-orm/sqlite-core/indexes.js
|
|
19142
|
-
class IndexBuilderOn {
|
|
19143
|
-
constructor(name, unique2) {
|
|
19144
|
-
this.name = name;
|
|
19145
|
-
this.unique = unique2;
|
|
19146
|
-
}
|
|
19147
|
-
static [entityKind] = "SQLiteIndexBuilderOn";
|
|
19148
|
-
on(...columns) {
|
|
19149
|
-
return new IndexBuilder(this.name, columns, this.unique);
|
|
19150
|
-
}
|
|
19151
|
-
}
|
|
19152
|
-
|
|
19153
|
-
class IndexBuilder {
|
|
19154
|
-
static [entityKind] = "SQLiteIndexBuilder";
|
|
19155
|
-
config;
|
|
19156
|
-
constructor(name, columns, unique2) {
|
|
19157
|
-
this.config = {
|
|
19158
|
-
name,
|
|
19159
|
-
columns,
|
|
19160
|
-
unique: unique2,
|
|
19161
|
-
where: undefined
|
|
19162
|
-
};
|
|
19163
|
-
}
|
|
19164
|
-
where(condition) {
|
|
19165
|
-
this.config.where = condition;
|
|
19166
|
-
return this;
|
|
19167
|
-
}
|
|
19168
|
-
build(table) {
|
|
19169
|
-
return new Index(this.config, table);
|
|
19170
|
-
}
|
|
19171
|
-
}
|
|
19172
|
-
|
|
19173
|
-
class Index {
|
|
19174
|
-
static [entityKind] = "SQLiteIndex";
|
|
19175
|
-
config;
|
|
19176
|
-
constructor(config, table) {
|
|
19177
|
-
this.config = { ...config, table };
|
|
19178
|
-
}
|
|
19179
|
-
}
|
|
19180
|
-
function index(name) {
|
|
19181
|
-
return new IndexBuilderOn(name, false);
|
|
19182
|
-
}
|
|
19183
|
-
|
|
19184
|
-
// packages/runtime/src/drizzle-schema.ts
|
|
19185
|
-
var deliveriesTable = sqliteTable("deliveries", {
|
|
19186
|
-
id: text("id").primaryKey(),
|
|
19187
|
-
messageId: text("message_id"),
|
|
19188
|
-
invocationId: text("invocation_id"),
|
|
19189
|
-
targetId: text("target_id").notNull(),
|
|
19190
|
-
targetNodeId: text("target_node_id"),
|
|
19191
|
-
targetKind: text("target_kind").$type().notNull(),
|
|
19192
|
-
transport: text("transport").$type().notNull(),
|
|
19193
|
-
reason: text("reason").$type().notNull(),
|
|
19194
|
-
policy: text("policy").$type().notNull(),
|
|
19195
|
-
status: text("status").$type().notNull(),
|
|
19196
|
-
bindingId: text("binding_id"),
|
|
19197
|
-
leaseOwner: text("lease_owner"),
|
|
19198
|
-
leaseExpiresAt: integer("lease_expires_at"),
|
|
19199
|
-
metadataJson: text("metadata_json"),
|
|
19200
|
-
createdAt: integer("created_at").notNull().default(sql`(unixepoch())`)
|
|
19201
|
-
}, (table) => [
|
|
19202
|
-
index("idx_deliveries_status_transport").on(table.status, table.transport)
|
|
19203
|
-
]);
|
|
19204
|
-
var deliveryAttemptsTable = sqliteTable("delivery_attempts", {
|
|
19205
|
-
id: text("id").primaryKey(),
|
|
19206
|
-
deliveryId: text("delivery_id").notNull(),
|
|
19207
|
-
attempt: integer("attempt").notNull(),
|
|
19208
|
-
status: text("status").$type().notNull(),
|
|
19209
|
-
error: text("error"),
|
|
19210
|
-
externalRef: text("external_ref"),
|
|
19211
|
-
metadataJson: text("metadata_json"),
|
|
19212
|
-
createdAt: integer("created_at").notNull()
|
|
19213
|
-
});
|
|
19214
|
-
// packages/runtime/src/broker.ts
|
|
19215
|
-
init_dist2();
|
|
19216
|
-
function createRuntimeId(prefix) {
|
|
19217
|
-
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
19218
|
-
}
|
|
19219
|
-
function toTargetKind(actor) {
|
|
19220
|
-
if (!actor)
|
|
19221
|
-
return "participant";
|
|
19222
|
-
if (actor.kind === "agent")
|
|
19223
|
-
return "agent";
|
|
19224
|
-
if (actor.kind === "device")
|
|
19225
|
-
return "device";
|
|
19226
|
-
if (actor.kind === "bridge")
|
|
19227
|
-
return "bridge";
|
|
19228
|
-
return "participant";
|
|
19229
|
-
}
|
|
19230
|
-
function defaultTransportForActor(actor) {
|
|
19231
|
-
if (!actor)
|
|
19232
|
-
return "local_socket";
|
|
19233
|
-
switch (actor.kind) {
|
|
19234
|
-
case "bridge":
|
|
19235
|
-
return "webhook";
|
|
19236
|
-
case "device":
|
|
19237
|
-
return "local_socket";
|
|
19238
|
-
case "agent":
|
|
19239
|
-
return "local_socket";
|
|
19240
|
-
default:
|
|
19241
|
-
return "local_socket";
|
|
19242
|
-
}
|
|
19243
|
-
}
|
|
19244
|
-
function endpointTransportRank(transport) {
|
|
19245
|
-
switch (transport) {
|
|
19246
|
-
case "codex_app_server":
|
|
19247
|
-
return 0;
|
|
19248
|
-
case "claude_stream_json":
|
|
19249
|
-
return 1;
|
|
19250
|
-
case "tmux":
|
|
19251
|
-
return 2;
|
|
19252
|
-
case "local_socket":
|
|
19253
|
-
return 3;
|
|
19254
|
-
default:
|
|
19255
|
-
return 4;
|
|
19256
|
-
}
|
|
19257
|
-
}
|
|
19258
|
-
function shouldBridgeMessageToBinding(binding, message) {
|
|
19259
|
-
if (binding.platform !== "telegram") {
|
|
19260
|
-
return true;
|
|
19261
|
-
}
|
|
19262
|
-
const source = typeof message.metadata?.source === "string" ? String(message.metadata.source) : "";
|
|
19263
|
-
if (source === "telegram") {
|
|
19264
|
-
return false;
|
|
19265
|
-
}
|
|
19266
|
-
const outboundMode = typeof binding.metadata?.outboundMode === "string" ? String(binding.metadata.outboundMode) : "operator_only";
|
|
19267
|
-
const operatorId = typeof binding.metadata?.operatorId === "string" ? String(binding.metadata.operatorId) : "operator";
|
|
19268
|
-
const allowedActorIds = Array.isArray(binding.metadata?.allowedActorIds) ? binding.metadata.allowedActorIds.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
19269
|
-
if (outboundMode === "all") {
|
|
19270
|
-
return true;
|
|
19271
|
-
}
|
|
19272
|
-
if (outboundMode === "allowlist") {
|
|
19273
|
-
return allowedActorIds.includes(message.actorId);
|
|
19274
|
-
}
|
|
19275
|
-
return message.actorId === operatorId;
|
|
19276
|
-
}
|
|
19277
|
-
function resolveBindingRoutes(bindings, message) {
|
|
19278
|
-
return bindings.filter((binding) => shouldBridgeMessageToBinding(binding, message)).map((binding) => ({
|
|
19279
|
-
targetId: binding.id,
|
|
19280
|
-
targetKind: "bridge",
|
|
19281
|
-
transport: binding.platform === "telegram" ? "telegram" : binding.platform === "discord" ? "discord" : "webhook",
|
|
19282
|
-
bindingId: binding.id
|
|
19283
|
-
}));
|
|
19284
|
-
}
|
|
19285
|
-
function preferredEndpoint(endpoints) {
|
|
19286
|
-
let preferred;
|
|
19287
|
-
let preferredRank = Number.POSITIVE_INFINITY;
|
|
19288
|
-
for (const endpoint of endpoints) {
|
|
19289
|
-
const rank = endpointTransportRank(endpoint.transport);
|
|
19290
|
-
if (!preferred || rank < preferredRank) {
|
|
19291
|
-
preferred = endpoint;
|
|
19292
|
-
preferredRank = rank;
|
|
19293
|
-
}
|
|
19294
|
-
}
|
|
19295
|
-
return preferred;
|
|
19296
|
-
}
|
|
19297
|
-
|
|
19298
|
-
class InMemoryControlRuntime {
|
|
19299
|
-
registry;
|
|
19300
|
-
listeners = new Set;
|
|
19301
|
-
eventBuffer = [];
|
|
19302
|
-
localNodeId;
|
|
19303
|
-
endpointIdsByAgentId = new Map;
|
|
19304
|
-
bindingIdsByConversationId = new Map;
|
|
19305
|
-
flightIdByInvocationId = new Map;
|
|
19306
|
-
constructor(initial = {}, options = {}) {
|
|
19307
|
-
this.registry = createRuntimeRegistrySnapshot(initial);
|
|
19308
|
-
this.localNodeId = options.localNodeId;
|
|
19309
|
-
this.rebuildIndexes();
|
|
19310
|
-
}
|
|
19311
|
-
snapshot() {
|
|
19312
|
-
return createRuntimeRegistrySnapshot({
|
|
19313
|
-
nodes: { ...this.registry.nodes },
|
|
19314
|
-
actors: { ...this.registry.actors },
|
|
19315
|
-
agents: { ...this.registry.agents },
|
|
19316
|
-
endpoints: { ...this.registry.endpoints },
|
|
19317
|
-
conversations: { ...this.registry.conversations },
|
|
19318
|
-
bindings: { ...this.registry.bindings },
|
|
19319
|
-
messages: { ...this.registry.messages },
|
|
19320
|
-
flights: { ...this.registry.flights },
|
|
19321
|
-
collaborationRecords: { ...this.registry.collaborationRecords }
|
|
19322
|
-
});
|
|
19323
|
-
}
|
|
19324
|
-
peek() {
|
|
19325
|
-
return this.registry;
|
|
19326
|
-
}
|
|
19327
|
-
node(nodeId) {
|
|
19328
|
-
return this.registry.nodes[nodeId];
|
|
19329
|
-
}
|
|
19330
|
-
agent(agentId) {
|
|
19331
|
-
return this.registry.agents[agentId];
|
|
19332
|
-
}
|
|
19333
|
-
conversation(conversationId) {
|
|
19334
|
-
return this.registry.conversations[conversationId];
|
|
19335
|
-
}
|
|
19336
|
-
message(messageId) {
|
|
19337
|
-
return this.registry.messages[messageId];
|
|
19338
|
-
}
|
|
19339
|
-
collaborationRecord(recordId) {
|
|
19340
|
-
return this.registry.collaborationRecords[recordId];
|
|
19341
|
-
}
|
|
19342
|
-
flightForInvocation(invocationId) {
|
|
19343
|
-
const flightId = this.flightIdByInvocationId.get(invocationId);
|
|
19344
|
-
return flightId ? this.registry.flights[flightId] : undefined;
|
|
19345
|
-
}
|
|
19346
|
-
bindingsForConversation(conversationId) {
|
|
19347
|
-
const bindingIds = this.bindingIdsByConversationId.get(conversationId);
|
|
19348
|
-
if (!bindingIds) {
|
|
19349
|
-
return [];
|
|
19350
|
-
}
|
|
19351
|
-
const bindings = [];
|
|
19352
|
-
for (const bindingId of bindingIds) {
|
|
19353
|
-
const binding = this.registry.bindings[bindingId];
|
|
19354
|
-
if (binding) {
|
|
19355
|
-
bindings.push(binding);
|
|
19356
|
-
}
|
|
19357
|
-
}
|
|
19358
|
-
return bindings;
|
|
19359
|
-
}
|
|
19360
|
-
endpointsForAgent(agentId, options = {}) {
|
|
19361
|
-
const endpointIds = this.endpointIdsByAgentId.get(agentId);
|
|
19362
|
-
if (!endpointIds) {
|
|
19363
|
-
return [];
|
|
19364
|
-
}
|
|
19365
|
-
const endpoints = [];
|
|
19366
|
-
for (const endpointId of endpointIds) {
|
|
19367
|
-
const endpoint = this.registry.endpoints[endpointId];
|
|
19368
|
-
if (!endpoint)
|
|
19369
|
-
continue;
|
|
19370
|
-
if (!options.includeOffline && endpoint.state === "offline")
|
|
19371
|
-
continue;
|
|
19372
|
-
if (options.nodeId && endpoint.nodeId !== options.nodeId)
|
|
19373
|
-
continue;
|
|
19374
|
-
if (options.harness && endpoint.harness !== options.harness)
|
|
19375
|
-
continue;
|
|
19376
|
-
endpoints.push(endpoint);
|
|
19377
|
-
}
|
|
19378
|
-
return endpoints;
|
|
19379
|
-
}
|
|
19380
|
-
recentEvents(limit = 100) {
|
|
19381
|
-
return this.eventBuffer.slice(-limit);
|
|
19382
|
-
}
|
|
19383
|
-
subscribe(listener) {
|
|
19384
|
-
this.listeners.add(listener);
|
|
19385
|
-
return () => {
|
|
19386
|
-
this.listeners.delete(listener);
|
|
19387
|
-
};
|
|
19388
|
-
}
|
|
19389
|
-
async dispatch(command) {
|
|
19390
|
-
switch (command.kind) {
|
|
19391
|
-
case "node.upsert":
|
|
19392
|
-
await this.upsertNode(command.node);
|
|
19393
|
-
return;
|
|
19394
|
-
case "actor.upsert":
|
|
19395
|
-
await this.upsertActor(command.actor);
|
|
19396
|
-
return;
|
|
19397
|
-
case "agent.upsert":
|
|
19398
|
-
await this.upsertAgent(command.agent);
|
|
19399
|
-
return;
|
|
19400
|
-
case "agent.endpoint.upsert":
|
|
19401
|
-
await this.upsertEndpoint(command.endpoint);
|
|
19402
|
-
return;
|
|
19403
|
-
case "conversation.upsert":
|
|
19404
|
-
await this.upsertConversation(command.conversation);
|
|
19405
|
-
return;
|
|
19406
|
-
case "binding.upsert":
|
|
19407
|
-
await this.upsertBinding(command.binding);
|
|
19408
|
-
return;
|
|
19409
|
-
case "collaboration.upsert":
|
|
19410
|
-
await this.upsertCollaboration(command.record);
|
|
19411
|
-
return;
|
|
19412
|
-
case "collaboration.event.append":
|
|
19413
|
-
await this.appendCollaborationEvent(command.event);
|
|
19414
|
-
return;
|
|
19415
|
-
case "conversation.post":
|
|
19416
|
-
await this.postMessage(command.message);
|
|
19417
|
-
return;
|
|
19418
|
-
case "agent.invoke":
|
|
19419
|
-
await this.invokeAgent(command.invocation);
|
|
19420
|
-
return;
|
|
19421
|
-
case "agent.ensure_awake":
|
|
19422
|
-
this.emit({
|
|
19423
|
-
id: createRuntimeId("evt"),
|
|
19424
|
-
kind: "flight.updated",
|
|
19425
|
-
ts: Date.now(),
|
|
19426
|
-
actorId: command.requesterId,
|
|
19427
|
-
nodeId: this.localNodeId,
|
|
19428
|
-
payload: {
|
|
19429
|
-
flight: {
|
|
19430
|
-
id: createRuntimeId("flt"),
|
|
19431
|
-
invocationId: command.agentId,
|
|
19432
|
-
requesterId: command.requesterId,
|
|
19433
|
-
targetAgentId: command.agentId,
|
|
19434
|
-
state: "waking",
|
|
19435
|
-
summary: command.reason
|
|
19436
|
-
}
|
|
19437
|
-
}
|
|
19438
|
-
});
|
|
19439
|
-
return;
|
|
19440
|
-
case "stream.subscribe":
|
|
19441
|
-
return;
|
|
19442
|
-
default: {
|
|
19443
|
-
const exhaustive = command;
|
|
19444
|
-
return exhaustive;
|
|
19445
|
-
}
|
|
19446
|
-
}
|
|
19447
|
-
}
|
|
19448
|
-
async upsertNode(node) {
|
|
19449
|
-
this.registry.nodes[node.id] = node;
|
|
19450
|
-
this.emit({
|
|
19451
|
-
id: createRuntimeId("evt"),
|
|
19452
|
-
kind: "node.upserted",
|
|
19453
|
-
ts: Date.now(),
|
|
19454
|
-
actorId: node.id,
|
|
19455
|
-
nodeId: node.id,
|
|
19456
|
-
payload: { node }
|
|
19457
|
-
});
|
|
19458
|
-
}
|
|
19459
|
-
async upsertActor(actor) {
|
|
19460
|
-
this.registry.actors[actor.id] = {
|
|
19461
|
-
id: actor.id,
|
|
19462
|
-
kind: actor.kind,
|
|
19463
|
-
displayName: actor.displayName,
|
|
19464
|
-
handle: actor.handle,
|
|
19465
|
-
labels: actor.labels,
|
|
19466
|
-
metadata: actor.metadata
|
|
19467
|
-
};
|
|
19468
|
-
this.emit({
|
|
19469
|
-
id: createRuntimeId("evt"),
|
|
19470
|
-
kind: "actor.registered",
|
|
19471
|
-
ts: Date.now(),
|
|
19472
|
-
actorId: actor.id,
|
|
19473
|
-
nodeId: this.localNodeId,
|
|
19474
|
-
payload: { actor }
|
|
19475
|
-
});
|
|
19476
|
-
}
|
|
19477
|
-
async upsertAgent(agent) {
|
|
19478
|
-
if (!this.registry.actors[agent.id]) {
|
|
19479
|
-
this.registry.actors[agent.id] = {
|
|
19480
|
-
id: agent.id,
|
|
19481
|
-
kind: agent.kind,
|
|
19482
|
-
displayName: agent.displayName,
|
|
19483
|
-
handle: agent.handle,
|
|
19484
|
-
labels: agent.labels,
|
|
19485
|
-
metadata: agent.metadata
|
|
19486
|
-
};
|
|
19487
|
-
}
|
|
19488
|
-
this.registry.agents[agent.id] = agent;
|
|
19489
|
-
this.emit({
|
|
19490
|
-
id: createRuntimeId("evt"),
|
|
19491
|
-
kind: "agent.registered",
|
|
19492
|
-
ts: Date.now(),
|
|
19493
|
-
actorId: agent.id,
|
|
19494
|
-
nodeId: agent.authorityNodeId,
|
|
19495
|
-
payload: { agent }
|
|
19496
|
-
});
|
|
19497
|
-
}
|
|
19498
|
-
async upsertEndpoint(endpoint) {
|
|
19499
|
-
const previous = this.registry.endpoints[endpoint.id];
|
|
19500
|
-
if (previous) {
|
|
19501
|
-
this.unindexEndpoint(previous);
|
|
19502
|
-
}
|
|
19503
|
-
this.registry.endpoints[endpoint.id] = endpoint;
|
|
19504
|
-
this.indexEndpoint(endpoint);
|
|
19505
|
-
this.emit({
|
|
19506
|
-
id: createRuntimeId("evt"),
|
|
19507
|
-
kind: "agent.endpoint.upserted",
|
|
19508
|
-
ts: Date.now(),
|
|
19509
|
-
actorId: endpoint.agentId,
|
|
19510
|
-
nodeId: endpoint.nodeId,
|
|
19511
|
-
payload: { endpoint }
|
|
19512
|
-
});
|
|
19513
|
-
}
|
|
19514
|
-
async upsertConversation(conversation) {
|
|
19515
|
-
this.registry.conversations[conversation.id] = conversation;
|
|
19516
|
-
this.emit({
|
|
19517
|
-
id: createRuntimeId("evt"),
|
|
19518
|
-
kind: "conversation.upserted",
|
|
19519
|
-
ts: Date.now(),
|
|
19520
|
-
actorId: "system",
|
|
19521
|
-
nodeId: conversation.authorityNodeId,
|
|
19522
|
-
payload: { conversation }
|
|
19523
|
-
});
|
|
19524
|
-
}
|
|
19525
|
-
async upsertBinding(binding) {
|
|
19526
|
-
const previous = this.registry.bindings[binding.id];
|
|
19527
|
-
if (previous) {
|
|
19528
|
-
this.unindexBinding(previous);
|
|
19529
|
-
}
|
|
19530
|
-
this.registry.bindings[binding.id] = binding;
|
|
19531
|
-
this.indexBinding(binding);
|
|
19532
|
-
this.emit({
|
|
19533
|
-
id: createRuntimeId("evt"),
|
|
19534
|
-
kind: "binding.upserted",
|
|
19535
|
-
ts: Date.now(),
|
|
19536
|
-
actorId: "system",
|
|
19537
|
-
nodeId: this.localNodeId,
|
|
19538
|
-
payload: { binding }
|
|
19539
|
-
});
|
|
19540
|
-
}
|
|
19541
|
-
async upsertCollaboration(record) {
|
|
19542
|
-
assertValidCollaborationRecord(record);
|
|
19543
|
-
this.registry.collaborationRecords[record.id] = record;
|
|
19544
|
-
this.emit({
|
|
19545
|
-
id: createRuntimeId("evt"),
|
|
19546
|
-
kind: "collaboration.upserted",
|
|
19547
|
-
ts: Date.now(),
|
|
19548
|
-
actorId: record.createdById,
|
|
19549
|
-
nodeId: this.localNodeId,
|
|
19550
|
-
payload: { record }
|
|
19551
|
-
});
|
|
19552
|
-
}
|
|
19553
|
-
async appendCollaborationEvent(event) {
|
|
19554
|
-
const record = this.registry.collaborationRecords[event.recordId];
|
|
19555
|
-
if (!record) {
|
|
19556
|
-
throw new Error(`unknown collaboration record: ${event.recordId}`);
|
|
19557
|
-
}
|
|
19558
|
-
assertValidCollaborationEvent(event, record);
|
|
19559
|
-
this.emit({
|
|
19560
|
-
id: createRuntimeId("evt"),
|
|
19561
|
-
kind: "collaboration.event.appended",
|
|
19562
|
-
ts: Date.now(),
|
|
19563
|
-
actorId: event.actorId,
|
|
19564
|
-
nodeId: this.localNodeId,
|
|
19565
|
-
payload: { event }
|
|
19566
|
-
});
|
|
19567
|
-
}
|
|
19568
|
-
async upsertFlight(flight) {
|
|
19569
|
-
const previous = this.registry.flights[flight.id];
|
|
19570
|
-
if (previous && this.flightIdByInvocationId.get(previous.invocationId) === previous.id) {
|
|
19571
|
-
this.flightIdByInvocationId.delete(previous.invocationId);
|
|
19572
|
-
}
|
|
19573
|
-
this.registry.flights[flight.id] = flight;
|
|
19574
|
-
this.flightIdByInvocationId.set(flight.invocationId, flight.id);
|
|
19575
|
-
this.emit({
|
|
19576
|
-
id: createRuntimeId("evt"),
|
|
19577
|
-
kind: "flight.updated",
|
|
19578
|
-
ts: Date.now(),
|
|
19579
|
-
actorId: flight.requesterId,
|
|
19580
|
-
nodeId: this.localNodeId,
|
|
19581
|
-
payload: { flight }
|
|
19582
|
-
});
|
|
19583
|
-
}
|
|
19584
|
-
async postMessage(message, options = {}) {
|
|
19585
|
-
const deliveries2 = this.planMessage(message, options);
|
|
19586
|
-
await this.commitMessage(message, deliveries2);
|
|
19587
|
-
return deliveries2;
|
|
19588
|
-
}
|
|
19589
|
-
planMessage(message, options = {}) {
|
|
19590
|
-
const conversation = this.registry.conversations[message.conversationId];
|
|
19591
|
-
if (!conversation) {
|
|
19592
|
-
throw new Error(`unknown conversation: ${message.conversationId}`);
|
|
19593
|
-
}
|
|
19594
|
-
const bindingRoutes = resolveBindingRoutes(this.bindingsForConversation(conversation.id), message);
|
|
19595
|
-
const plannedParticipantRoutes = this.resolveParticipantRoutes(conversation.participantIds);
|
|
19596
|
-
const participantRoutes = options.localOnly ? plannedParticipantRoutes.filter((route) => !route.nodeId || route.nodeId === this.localNodeId) : plannedParticipantRoutes;
|
|
19597
|
-
const deliveries2 = planMessageDeliveries({
|
|
19598
|
-
localNodeId: this.localNodeId,
|
|
19599
|
-
message,
|
|
19600
|
-
conversation,
|
|
19601
|
-
participantRoutes,
|
|
19602
|
-
bindingRoutes: options.localOnly ? [] : bindingRoutes
|
|
19603
|
-
});
|
|
19604
|
-
return deliveries2;
|
|
19605
|
-
}
|
|
19606
|
-
async commitMessage(message, deliveries2) {
|
|
19607
|
-
this.registry.messages[message.id] = message;
|
|
19608
|
-
this.emit({
|
|
19609
|
-
id: createRuntimeId("evt"),
|
|
19610
|
-
kind: "message.posted",
|
|
19611
|
-
ts: Date.now(),
|
|
19612
|
-
actorId: message.actorId,
|
|
19613
|
-
nodeId: message.originNodeId,
|
|
19614
|
-
payload: { message }
|
|
19615
|
-
});
|
|
19616
|
-
for (const delivery of deliveries2) {
|
|
19617
|
-
this.emit({
|
|
19618
|
-
id: createRuntimeId("evt"),
|
|
19619
|
-
kind: "delivery.planned",
|
|
19620
|
-
ts: Date.now(),
|
|
19621
|
-
actorId: message.actorId,
|
|
19622
|
-
nodeId: message.originNodeId,
|
|
19623
|
-
payload: { delivery }
|
|
19624
|
-
});
|
|
19625
|
-
}
|
|
19626
|
-
}
|
|
19627
|
-
async invokeAgent(invocation) {
|
|
19628
|
-
const flight = this.planInvocation(invocation);
|
|
19629
|
-
await this.commitInvocation(invocation, flight);
|
|
19630
|
-
return flight;
|
|
19631
|
-
}
|
|
19632
|
-
planInvocation(invocation) {
|
|
19633
|
-
const targetAgent = this.registry.agents[invocation.targetAgentId];
|
|
19634
|
-
if (!targetAgent) {
|
|
19635
|
-
throw new Error(`unknown agent: ${invocation.targetAgentId}`);
|
|
19636
|
-
}
|
|
19637
|
-
const targetEndpoints = this.endpointsForAgent(invocation.targetAgentId, {
|
|
19638
|
-
nodeId: targetAgent.authorityNodeId,
|
|
19639
|
-
harness: invocation.execution?.harness
|
|
19640
|
-
});
|
|
19641
|
-
const isLocalAuthority = !this.localNodeId || targetAgent.authorityNodeId === this.localNodeId;
|
|
19642
|
-
const startedAt = Date.now();
|
|
19643
|
-
let state = invocation.ensureAwake ? "waking" : "queued";
|
|
19644
|
-
let summary;
|
|
19645
|
-
let error;
|
|
19646
|
-
let completedAt;
|
|
19647
|
-
if (isLocalAuthority) {
|
|
19648
|
-
if (targetEndpoints.length == 0) {
|
|
19649
|
-
state = invocation.ensureAwake ? "waking" : "queued";
|
|
19650
|
-
summary = invocation.ensureAwake ? invocation.execution?.harness ? `${targetAgent.displayName} waking on ${invocation.execution.harness}.` : `${targetAgent.displayName} waking.` : `Message stored for ${targetAgent.displayName}. Will deliver when online.`;
|
|
19651
|
-
} else {
|
|
19652
|
-
state = "queued";
|
|
19653
|
-
summary = `${targetAgent.displayName} queued for local execution.`;
|
|
19654
|
-
}
|
|
19655
|
-
}
|
|
19656
|
-
const flight = {
|
|
19657
|
-
id: createRuntimeId("flt"),
|
|
19658
|
-
invocationId: invocation.id,
|
|
19659
|
-
requesterId: invocation.requesterId,
|
|
19660
|
-
targetAgentId: invocation.targetAgentId,
|
|
19661
|
-
state,
|
|
19662
|
-
summary,
|
|
19663
|
-
error,
|
|
19664
|
-
startedAt,
|
|
19665
|
-
completedAt,
|
|
19666
|
-
metadata: invocation.metadata
|
|
19667
|
-
};
|
|
19668
|
-
return flight;
|
|
19669
|
-
}
|
|
19670
|
-
async commitInvocation(invocation, flight) {
|
|
19671
|
-
const previous = this.registry.flights[flight.id];
|
|
19672
|
-
if (previous && this.flightIdByInvocationId.get(previous.invocationId) === previous.id) {
|
|
19673
|
-
this.flightIdByInvocationId.delete(previous.invocationId);
|
|
19674
|
-
}
|
|
19675
|
-
this.registry.flights[flight.id] = flight;
|
|
19676
|
-
this.flightIdByInvocationId.set(flight.invocationId, flight.id);
|
|
19677
|
-
const targetAgent = this.registry.agents[invocation.targetAgentId];
|
|
19678
|
-
this.emit({
|
|
19679
|
-
id: createRuntimeId("evt"),
|
|
19680
|
-
kind: "invocation.requested",
|
|
19681
|
-
ts: Date.now(),
|
|
19682
|
-
actorId: invocation.requesterId,
|
|
19683
|
-
nodeId: invocation.requesterNodeId,
|
|
19684
|
-
payload: { invocation }
|
|
19685
|
-
});
|
|
19686
|
-
this.emit({
|
|
19687
|
-
id: createRuntimeId("evt"),
|
|
19688
|
-
kind: "flight.updated",
|
|
19689
|
-
ts: Date.now(),
|
|
19690
|
-
actorId: invocation.requesterId,
|
|
19691
|
-
nodeId: targetAgent?.authorityNodeId,
|
|
19692
|
-
payload: { flight }
|
|
19693
|
-
});
|
|
19694
|
-
}
|
|
19695
|
-
rebuildIndexes() {
|
|
19696
|
-
for (const endpoint of Object.values(this.registry.endpoints)) {
|
|
19697
|
-
this.indexEndpoint(endpoint);
|
|
19698
|
-
}
|
|
19699
|
-
for (const binding of Object.values(this.registry.bindings)) {
|
|
19700
|
-
this.indexBinding(binding);
|
|
19701
|
-
}
|
|
19702
|
-
for (const flight of Object.values(this.registry.flights)) {
|
|
19703
|
-
this.flightIdByInvocationId.set(flight.invocationId, flight.id);
|
|
19704
|
-
}
|
|
19705
|
-
}
|
|
19706
|
-
indexEndpoint(endpoint) {
|
|
19707
|
-
this.addIndexedId(this.endpointIdsByAgentId, endpoint.agentId, endpoint.id);
|
|
19708
|
-
}
|
|
19709
|
-
unindexEndpoint(endpoint) {
|
|
19710
|
-
this.removeIndexedId(this.endpointIdsByAgentId, endpoint.agentId, endpoint.id);
|
|
19711
|
-
}
|
|
19712
|
-
indexBinding(binding) {
|
|
19713
|
-
this.addIndexedId(this.bindingIdsByConversationId, binding.conversationId, binding.id);
|
|
19714
|
-
}
|
|
19715
|
-
unindexBinding(binding) {
|
|
19716
|
-
this.removeIndexedId(this.bindingIdsByConversationId, binding.conversationId, binding.id);
|
|
19717
|
-
}
|
|
19718
|
-
addIndexedId(index2, key, value) {
|
|
19719
|
-
const ids = index2.get(key) ?? new Set;
|
|
19720
|
-
ids.add(value);
|
|
19721
|
-
index2.set(key, ids);
|
|
19722
|
-
}
|
|
19723
|
-
removeIndexedId(index2, key, value) {
|
|
19724
|
-
const ids = index2.get(key);
|
|
19725
|
-
if (!ids) {
|
|
19726
|
-
return;
|
|
19727
|
-
}
|
|
19728
|
-
ids.delete(value);
|
|
19729
|
-
if (ids.size === 0) {
|
|
19730
|
-
index2.delete(key);
|
|
19731
|
-
}
|
|
19732
|
-
}
|
|
19733
|
-
resolveParticipantRoutes(participantIds) {
|
|
19734
|
-
const routes = [];
|
|
19735
|
-
for (const participantId of participantIds) {
|
|
19736
|
-
const actor = this.registry.actors[participantId];
|
|
19737
|
-
const agent = this.registry.agents[participantId];
|
|
19738
|
-
const targetIdentity = actor ?? agent;
|
|
19739
|
-
const endpoints = this.endpointsForAgent(participantId);
|
|
19740
|
-
const endpoint = preferredEndpoint(endpoints);
|
|
19741
|
-
if (!endpoint) {
|
|
19742
|
-
if (agent?.authorityNodeId && agent.authorityNodeId !== this.localNodeId) {
|
|
19743
|
-
routes.push({
|
|
19744
|
-
targetId: participantId,
|
|
19745
|
-
nodeId: agent.authorityNodeId,
|
|
19746
|
-
targetKind: toTargetKind(targetIdentity),
|
|
19747
|
-
transport: defaultTransportForActor(targetIdentity),
|
|
19748
|
-
speechEnabled: false
|
|
19749
|
-
});
|
|
19750
|
-
} else if (agent) {
|
|
19751
|
-
routes.push({
|
|
19752
|
-
targetId: participantId,
|
|
19753
|
-
nodeId: agent.authorityNodeId ?? this.localNodeId,
|
|
19754
|
-
targetKind: toTargetKind(targetIdentity),
|
|
19755
|
-
transport: defaultTransportForActor(targetIdentity),
|
|
19756
|
-
speechEnabled: false
|
|
19757
|
-
});
|
|
19758
|
-
}
|
|
19759
|
-
continue;
|
|
19760
|
-
}
|
|
19761
|
-
routes.push({
|
|
19762
|
-
targetId: participantId,
|
|
19763
|
-
nodeId: endpoint.nodeId ?? agent?.authorityNodeId,
|
|
19764
|
-
targetKind: toTargetKind(targetIdentity),
|
|
19765
|
-
transport: endpoint.transport ?? defaultTransportForActor(targetIdentity),
|
|
19766
|
-
speechEnabled: Boolean(actor?.kind === "device" || endpoints.some((candidate) => candidate.transport === "local_socket" || candidate.transport === "websocket"))
|
|
19767
|
-
});
|
|
19768
|
-
}
|
|
19769
|
-
return routes;
|
|
19770
|
-
}
|
|
19771
|
-
emit(event) {
|
|
19772
|
-
this.eventBuffer.push(event);
|
|
19773
|
-
if (this.eventBuffer.length > 500) {
|
|
19774
|
-
this.eventBuffer.shift();
|
|
19775
|
-
}
|
|
19776
|
-
for (const listener of this.listeners) {
|
|
19777
|
-
listener(event);
|
|
19778
|
-
}
|
|
19779
|
-
}
|
|
19780
|
-
}
|
|
19781
|
-
// packages/runtime/src/tailscale.ts
|
|
19782
|
-
import { readFile as readFile7 } from "fs/promises";
|
|
19783
|
-
import { execFile } from "child_process";
|
|
19784
|
-
import { promisify } from "util";
|
|
19785
|
-
var execFileAsync = promisify(execFile);
|
|
19786
|
-
function parseStatusJson(raw2) {
|
|
19787
|
-
return JSON.parse(raw2);
|
|
19788
|
-
}
|
|
19789
|
-
function parsePeers(status) {
|
|
19790
|
-
const peers = Object.entries(status.Peer ?? {});
|
|
19791
|
-
return peers.map(([fallbackId, peer]) => ({
|
|
19792
|
-
id: peer.ID ?? fallbackId,
|
|
19793
|
-
name: peer.HostName ?? peer.DNSName ?? fallbackId,
|
|
19794
|
-
dnsName: peer.DNSName,
|
|
19795
|
-
addresses: peer.TailscaleIPs ?? [],
|
|
19796
|
-
online: peer.Online ?? false,
|
|
19797
|
-
hostName: peer.HostName,
|
|
19798
|
-
os: peer.OS,
|
|
19799
|
-
tags: peer.Tags ?? []
|
|
19800
|
-
}));
|
|
19801
|
-
}
|
|
19802
|
-
function parseSelf(status) {
|
|
19803
|
-
const self = status.Self;
|
|
19804
|
-
if (!self) {
|
|
19805
|
-
return null;
|
|
19806
|
-
}
|
|
19807
|
-
return {
|
|
19808
|
-
id: self.ID ?? self.DNSName ?? self.HostName ?? "self",
|
|
19809
|
-
name: self.HostName ?? self.DNSName ?? "self",
|
|
19810
|
-
dnsName: self.DNSName,
|
|
19811
|
-
addresses: self.TailscaleIPs ?? [],
|
|
19812
|
-
online: self.Online ?? true,
|
|
19813
|
-
hostName: self.HostName,
|
|
19814
|
-
os: self.OS,
|
|
19815
|
-
tailnetName: status.CurrentTailnet?.Name,
|
|
19816
|
-
magicDnsSuffix: status.CurrentTailnet?.MagicDNSSuffix
|
|
19817
|
-
};
|
|
19818
|
-
}
|
|
19819
|
-
function isBackendRunning(status) {
|
|
19820
|
-
return (status.BackendState ?? "").trim().toLowerCase() === "running";
|
|
19821
|
-
}
|
|
19822
|
-
async function readStatusJsonFromFile(filePath) {
|
|
19823
|
-
const raw2 = await readFile7(filePath, "utf8");
|
|
19824
|
-
return parseStatusJson(raw2);
|
|
19825
|
-
}
|
|
19826
|
-
async function readStatusJson() {
|
|
19827
|
-
const fixturePath = process.env.OPENSCOUT_TAILSCALE_STATUS_JSON;
|
|
19828
|
-
if (fixturePath) {
|
|
19829
|
-
return readStatusJsonFromFile(fixturePath);
|
|
19830
|
-
}
|
|
19831
|
-
try {
|
|
19832
|
-
const tailscaleBin = process.env.OPENSCOUT_TAILSCALE_BIN ?? "tailscale";
|
|
19833
|
-
const { stdout } = await execFileAsync(tailscaleBin, ["status", "--json"]);
|
|
19834
|
-
return parseStatusJson(stdout);
|
|
19835
|
-
} catch {
|
|
19836
|
-
return null;
|
|
19837
|
-
}
|
|
19838
|
-
}
|
|
19839
|
-
async function readTailscaleSelf() {
|
|
19840
|
-
const summary = await readTailscaleStatusSummary();
|
|
19841
|
-
if (!summary) {
|
|
19842
|
-
return null;
|
|
19843
|
-
}
|
|
19844
|
-
return summary.self;
|
|
19845
|
-
}
|
|
19846
|
-
async function readTailscaleStatusSummary() {
|
|
19847
|
-
const status = await readStatusJson();
|
|
19848
|
-
if (!status) {
|
|
19849
|
-
return null;
|
|
19850
|
-
}
|
|
19851
|
-
return {
|
|
19852
|
-
backendState: status.BackendState ?? null,
|
|
19853
|
-
running: isBackendRunning(status),
|
|
19854
|
-
health: status.Health ?? [],
|
|
19855
|
-
peers: parsePeers(status),
|
|
19856
|
-
self: parseSelf(status)
|
|
19857
|
-
};
|
|
19858
|
-
}
|
|
19859
|
-
|
|
19860
|
-
// packages/runtime/src/index.ts
|
|
19861
|
-
init_broker_service();
|
|
19862
|
-
init_local_agents();
|
|
19863
|
-
|
|
19864
|
-
// packages/runtime/src/scout-broker.ts
|
|
19865
|
-
init_dist2();
|
|
19866
|
-
init_setup();
|
|
19867
|
-
init_support_paths();
|
|
19868
|
-
await __promiseAll([
|
|
19869
|
-
init_local_agents(),
|
|
19870
|
-
init_broker_service()
|
|
19871
|
-
]);
|
|
19872
|
-
|
|
19873
|
-
// packages/runtime/src/index.ts
|
|
19874
|
-
init_codex_app_server();
|
|
19875
|
-
init_setup();
|
|
19876
|
-
init_support_paths();
|
|
19877
|
-
|
|
19878
|
-
// packages/runtime/src/scout-agent-cards.ts
|
|
19879
|
-
init_dist2();
|
|
19880
|
-
// packages/runtime/src/thread-events.ts
|
|
19881
|
-
class ThreadWatchProtocolError extends Error {
|
|
19882
|
-
status;
|
|
19883
|
-
body;
|
|
19884
|
-
constructor(status, body) {
|
|
19885
|
-
super(body.message);
|
|
19886
|
-
this.status = status;
|
|
19887
|
-
this.body = body;
|
|
19888
|
-
}
|
|
19889
|
-
}
|
|
19890
|
-
function watchKey(conversationId, watcherNodeId, watcherId) {
|
|
19891
|
-
return `${conversationId}:${watcherNodeId}:${watcherId}`;
|
|
19892
|
-
}
|
|
19893
|
-
function writeSse(response, eventName, payload) {
|
|
19894
|
-
response.write(`event: ${eventName}
|
|
19895
|
-
data: ${JSON.stringify(payload)}
|
|
19896
|
-
|
|
19897
|
-
`);
|
|
19898
|
-
}
|
|
19899
|
-
|
|
19900
|
-
class ThreadEventPlane {
|
|
19901
|
-
options;
|
|
19902
|
-
watches = new Map;
|
|
19903
|
-
watchIdsByKey = new Map;
|
|
19904
|
-
watchIdsByConversation = new Map;
|
|
19905
|
-
constructor(options) {
|
|
19906
|
-
this.options = options;
|
|
19907
|
-
}
|
|
19908
|
-
async openWatch(request) {
|
|
19909
|
-
const conversation = this.requireConversationAuthority(request.conversationId);
|
|
19910
|
-
this.requireRemoteWatchAllowed(conversation);
|
|
19911
|
-
const afterSeq = Math.max(0, request.afterSeq ?? 0);
|
|
19912
|
-
const oldestSeq = await this.options.projection.oldestThreadSeq(conversation.id);
|
|
19913
|
-
const latestSeq = await this.options.projection.latestThreadSeq(conversation.id);
|
|
19914
|
-
if (latestSeq > 0 && oldestSeq > 0 && afterSeq < oldestSeq - 1) {
|
|
19915
|
-
throw new ThreadWatchProtocolError(409, {
|
|
19916
|
-
code: "cursor_out_of_range",
|
|
19917
|
-
message: `thread cursor ${afterSeq} is older than retained seq ${oldestSeq}`
|
|
19918
|
-
});
|
|
19919
|
-
}
|
|
19920
|
-
const key = watchKey(conversation.id, request.watcherNodeId, request.watcherId);
|
|
19921
|
-
const existingWatchId = this.watchIdsByKey.get(key);
|
|
19922
|
-
if (existingWatchId) {
|
|
19923
|
-
this.closeWatchInternal(existingWatchId);
|
|
19924
|
-
}
|
|
19925
|
-
const watchId = `thread-watch:${conversation.id}:${request.watcherNodeId}:${request.watcherId}`;
|
|
19926
|
-
const leaseExpiresAt = Date.now() + this.normalizeLeaseMs(request.leaseMs);
|
|
19927
|
-
const watch = {
|
|
19928
|
-
watchId,
|
|
19929
|
-
conversationId: conversation.id,
|
|
19930
|
-
watcherNodeId: request.watcherNodeId,
|
|
19931
|
-
watcherId: request.watcherId,
|
|
19932
|
-
acceptedAfterSeq: afterSeq,
|
|
19933
|
-
leaseExpiresAt,
|
|
19934
|
-
mode: conversation.shareMode === "summary" ? "summary" : "shared",
|
|
19935
|
-
clients: new Set
|
|
19936
|
-
};
|
|
19937
|
-
this.watches.set(watchId, watch);
|
|
19938
|
-
this.watchIdsByKey.set(key, watchId);
|
|
19939
|
-
this.indexWatch(watch);
|
|
19940
|
-
return {
|
|
19941
|
-
watchId,
|
|
19942
|
-
conversationId: conversation.id,
|
|
19943
|
-
authorityNodeId: conversation.authorityNodeId,
|
|
19944
|
-
acceptedAfterSeq: afterSeq,
|
|
19945
|
-
latestSeq,
|
|
19946
|
-
leaseExpiresAt,
|
|
19947
|
-
mode: watch.mode
|
|
19948
|
-
};
|
|
19949
|
-
}
|
|
19950
|
-
async renewWatch(request) {
|
|
19951
|
-
const watch = this.requireLiveWatch(request.watchId);
|
|
19952
|
-
watch.leaseExpiresAt = Date.now() + this.normalizeLeaseMs(request.leaseMs);
|
|
19953
|
-
return {
|
|
19954
|
-
watchId: watch.watchId,
|
|
19955
|
-
leaseExpiresAt: watch.leaseExpiresAt
|
|
19956
|
-
};
|
|
19957
|
-
}
|
|
19958
|
-
async closeWatch(request) {
|
|
19959
|
-
this.closeWatchInternal(request.watchId);
|
|
19960
|
-
}
|
|
19961
|
-
async replay(options) {
|
|
19962
|
-
const conversation = this.requireConversationAuthority(options.conversationId);
|
|
19963
|
-
this.requireRemoteWatchAllowed(conversation);
|
|
19964
|
-
const oldestSeq = await this.options.projection.oldestThreadSeq(conversation.id);
|
|
19965
|
-
const latestSeq = await this.options.projection.latestThreadSeq(conversation.id);
|
|
19966
|
-
if (latestSeq > 0 && oldestSeq > 0 && options.afterSeq < oldestSeq - 1) {
|
|
19967
|
-
throw new ThreadWatchProtocolError(409, {
|
|
19968
|
-
code: "cursor_out_of_range",
|
|
19969
|
-
message: `thread cursor ${options.afterSeq} is older than retained seq ${oldestSeq}`
|
|
19970
|
-
});
|
|
19971
|
-
}
|
|
19972
|
-
return this.options.projection.listThreadEvents({
|
|
19973
|
-
conversationId: conversation.id,
|
|
19974
|
-
afterSeq: options.afterSeq,
|
|
19975
|
-
limit: Math.min(options.limit ?? 500, this.options.maxReplayLimit ?? 2000)
|
|
19976
|
-
});
|
|
19977
|
-
}
|
|
19978
|
-
async snapshot(conversationId) {
|
|
19979
|
-
const conversation = this.requireConversationAuthority(conversationId);
|
|
19980
|
-
this.requireRemoteWatchAllowed(conversation);
|
|
19981
|
-
const snapshot = await this.options.projection.getThreadSnapshot(conversation.id);
|
|
19982
|
-
if (!snapshot) {
|
|
19983
|
-
throw new ThreadWatchProtocolError(404, {
|
|
19984
|
-
code: "unknown_conversation",
|
|
19985
|
-
message: `unknown conversation ${conversation.id}`
|
|
19986
|
-
});
|
|
19987
|
-
}
|
|
19988
|
-
return snapshot;
|
|
19989
|
-
}
|
|
19990
|
-
async streamWatch(watchId, request, response) {
|
|
19991
|
-
const watch = this.requireLiveWatch(watchId);
|
|
19992
|
-
const backlog = await this.options.projection.listThreadEvents({
|
|
19993
|
-
conversationId: watch.conversationId,
|
|
19994
|
-
afterSeq: watch.acceptedAfterSeq,
|
|
19995
|
-
limit: this.options.maxReplayLimit ?? 2000
|
|
19996
|
-
});
|
|
19997
|
-
response.writeHead(200, {
|
|
19998
|
-
"content-type": "text/event-stream",
|
|
19999
|
-
"cache-control": "no-cache, no-transform",
|
|
20000
|
-
connection: "keep-alive"
|
|
20001
|
-
});
|
|
20002
|
-
writeSse(response, "hello", {
|
|
20003
|
-
watchId: watch.watchId,
|
|
20004
|
-
conversationId: watch.conversationId,
|
|
20005
|
-
leaseExpiresAt: watch.leaseExpiresAt
|
|
20006
|
-
});
|
|
20007
|
-
for (const event of backlog) {
|
|
20008
|
-
writeSse(response, "thread.event", event);
|
|
20009
|
-
}
|
|
20010
|
-
watch.clients.add(response);
|
|
20011
|
-
request.on("close", () => {
|
|
20012
|
-
watch.clients.delete(response);
|
|
20013
|
-
response.end();
|
|
20014
|
-
});
|
|
20015
|
-
}
|
|
20016
|
-
publish(events2) {
|
|
20017
|
-
for (const event of events2) {
|
|
20018
|
-
const watchIds = this.watchIdsByConversation.get(event.conversationId);
|
|
20019
|
-
if (!watchIds || watchIds.size === 0) {
|
|
20020
|
-
continue;
|
|
20021
|
-
}
|
|
20022
|
-
for (const watchId of [...watchIds]) {
|
|
20023
|
-
const watch = this.watches.get(watchId);
|
|
20024
|
-
if (!watch) {
|
|
20025
|
-
watchIds.delete(watchId);
|
|
20026
|
-
continue;
|
|
20027
|
-
}
|
|
20028
|
-
if (watch.leaseExpiresAt <= Date.now()) {
|
|
20029
|
-
this.closeWatchInternal(watchId);
|
|
20030
|
-
continue;
|
|
20031
|
-
}
|
|
20032
|
-
for (const client of watch.clients) {
|
|
20033
|
-
writeSse(client, "thread.event", event);
|
|
20034
|
-
}
|
|
20035
|
-
}
|
|
20036
|
-
}
|
|
20037
|
-
}
|
|
20038
|
-
normalizeLeaseMs(requestedLeaseMs) {
|
|
20039
|
-
const defaultLeaseMs = this.options.defaultLeaseMs ?? 30000;
|
|
20040
|
-
const maxLeaseMs = this.options.maxLeaseMs ?? 300000;
|
|
20041
|
-
if (!requestedLeaseMs || !Number.isFinite(requestedLeaseMs) || requestedLeaseMs <= 0) {
|
|
20042
|
-
return defaultLeaseMs;
|
|
20043
|
-
}
|
|
20044
|
-
return Math.min(Math.max(requestedLeaseMs, 5000), maxLeaseMs);
|
|
20045
|
-
}
|
|
20046
|
-
requireConversationAuthority(conversationId) {
|
|
20047
|
-
const conversation = this.options.runtime.conversation(conversationId);
|
|
20048
|
-
if (!conversation) {
|
|
20049
|
-
throw new ThreadWatchProtocolError(404, {
|
|
20050
|
-
code: "unknown_conversation",
|
|
20051
|
-
message: `unknown conversation ${conversationId}`
|
|
20052
|
-
});
|
|
20053
|
-
}
|
|
20054
|
-
if (conversation.authorityNodeId !== this.options.nodeId) {
|
|
20055
|
-
throw new ThreadWatchProtocolError(409, {
|
|
20056
|
-
code: "no_responder",
|
|
20057
|
-
message: `conversation ${conversationId} is owned by ${conversation.authorityNodeId}`
|
|
20058
|
-
});
|
|
20059
|
-
}
|
|
20060
|
-
return conversation;
|
|
20061
|
-
}
|
|
20062
|
-
requireRemoteWatchAllowed(conversation) {
|
|
20063
|
-
if (conversation.shareMode === "local") {
|
|
20064
|
-
throw new ThreadWatchProtocolError(403, {
|
|
20065
|
-
code: "forbidden",
|
|
20066
|
-
message: `conversation ${conversation.id} does not allow remote watches`
|
|
20067
|
-
});
|
|
20068
|
-
}
|
|
20069
|
-
}
|
|
20070
|
-
requireLiveWatch(watchId) {
|
|
20071
|
-
const watch = this.watches.get(watchId);
|
|
20072
|
-
if (!watch) {
|
|
20073
|
-
throw new ThreadWatchProtocolError(404, {
|
|
20074
|
-
code: "invalid_request",
|
|
20075
|
-
message: `unknown watch ${watchId}`
|
|
20076
|
-
});
|
|
20077
|
-
}
|
|
20078
|
-
if (watch.leaseExpiresAt <= Date.now()) {
|
|
20079
|
-
this.closeWatchInternal(watchId);
|
|
20080
|
-
throw new ThreadWatchProtocolError(410, {
|
|
20081
|
-
code: "lease_expired",
|
|
20082
|
-
message: `watch ${watchId} lease expired`
|
|
20083
|
-
});
|
|
20084
|
-
}
|
|
20085
|
-
return watch;
|
|
20086
|
-
}
|
|
20087
|
-
indexWatch(watch) {
|
|
20088
|
-
const ids = this.watchIdsByConversation.get(watch.conversationId) ?? new Set;
|
|
20089
|
-
ids.add(watch.watchId);
|
|
20090
|
-
this.watchIdsByConversation.set(watch.conversationId, ids);
|
|
20091
|
-
}
|
|
20092
|
-
closeWatchInternal(watchId) {
|
|
20093
|
-
const watch = this.watches.get(watchId);
|
|
20094
|
-
if (!watch) {
|
|
20095
|
-
return;
|
|
20096
|
-
}
|
|
20097
|
-
this.watches.delete(watchId);
|
|
20098
|
-
this.watchIdsByKey.delete(watchKey(watch.conversationId, watch.watcherNodeId, watch.watcherId));
|
|
20099
|
-
const ids = this.watchIdsByConversation.get(watch.conversationId);
|
|
20100
|
-
if (ids) {
|
|
20101
|
-
ids.delete(watchId);
|
|
20102
|
-
if (ids.size === 0) {
|
|
20103
|
-
this.watchIdsByConversation.delete(watch.conversationId);
|
|
20104
|
-
}
|
|
20105
|
-
}
|
|
20106
|
-
for (const client of watch.clients) {
|
|
20107
|
-
client.end();
|
|
20108
|
-
}
|
|
20109
|
-
watch.clients.clear();
|
|
20110
|
-
}
|
|
20111
|
-
}
|
|
20112
|
-
// packages/runtime/src/mobile-push.ts
|
|
20113
|
-
init_support_paths();
|
|
20114
|
-
// packages/web/server/core/observe/service.ts
|
|
20115
|
-
var HISTORY_SNAPSHOT_CACHE_LIMIT = 128;
|
|
20116
|
-
var historySnapshotCache = new Map;
|
|
20117
|
-
var OBSERVE_SUMMARY_TAIL_SIZE = 8;
|
|
20118
|
-
function activeEndpoint(snapshot, agentId) {
|
|
20119
|
-
const candidates = Object.values(snapshot.endpoints ?? {}).filter((endpoint) => endpoint.agentId === agentId);
|
|
20120
|
-
const rank = (state) => {
|
|
20121
|
-
switch (state) {
|
|
20122
|
-
case "active":
|
|
20123
|
-
return 0;
|
|
20124
|
-
case "idle":
|
|
20125
|
-
return 1;
|
|
20126
|
-
case "waiting":
|
|
20127
|
-
return 2;
|
|
20128
|
-
case "offline":
|
|
20129
|
-
return 5;
|
|
20130
|
-
default:
|
|
20131
|
-
return 4;
|
|
20132
|
-
}
|
|
20133
|
-
};
|
|
20134
|
-
return [...candidates].sort((left, right) => rank(left.state) - rank(right.state))[0] ?? null;
|
|
20135
|
-
}
|
|
20136
|
-
function expandHome(value) {
|
|
20137
|
-
if (!value) {
|
|
20138
|
-
return null;
|
|
20139
|
-
}
|
|
20140
|
-
if (value === "~") {
|
|
20141
|
-
return homedir12();
|
|
20142
|
-
}
|
|
20143
|
-
if (value.startsWith("~/")) {
|
|
20144
|
-
return join18(homedir12(), value.slice(2));
|
|
20145
|
-
}
|
|
20146
|
-
return value;
|
|
20147
|
-
}
|
|
20148
|
-
function encodeClaudeProjectsSlug2(absolutePath) {
|
|
20149
|
-
const normalized = resolve8(absolutePath);
|
|
20150
|
-
return `-${normalized.replace(/^\//u, "").replace(/\//gu, "-")}`;
|
|
20151
|
-
}
|
|
20152
|
-
function resolveClaudeHistoryPath(cwd, sessionId) {
|
|
20153
|
-
const normalizedCwd = expandHome(cwd)?.trim();
|
|
20154
|
-
if (!normalizedCwd) {
|
|
20155
|
-
return null;
|
|
20156
|
-
}
|
|
20157
|
-
const projectDir = join18(homedir12(), ".claude", "projects", encodeClaudeProjectsSlug2(normalizedCwd));
|
|
20158
|
-
const normalizedSessionId = sessionId?.trim().replace(/\.jsonl$/u, "") || "";
|
|
20159
|
-
if (normalizedSessionId) {
|
|
20160
|
-
const exactPath = join18(projectDir, `${normalizedSessionId}.jsonl`);
|
|
20161
|
-
if (existsSync12(exactPath)) {
|
|
20162
|
-
return exactPath;
|
|
20163
|
-
}
|
|
20164
|
-
}
|
|
20165
|
-
return findMostRecentJsonl(projectDir);
|
|
20166
|
-
}
|
|
20167
|
-
function findMostRecentJsonl(dir) {
|
|
20168
|
-
if (!existsSync12(dir))
|
|
20169
|
-
return null;
|
|
20170
|
-
try {
|
|
20171
|
-
let best = null;
|
|
20172
|
-
for (const entry of readdirSync2(dir)) {
|
|
20173
|
-
if (!entry.endsWith(".jsonl"))
|
|
20174
|
-
continue;
|
|
20175
|
-
const full = join18(dir, entry);
|
|
20176
|
-
const st = statSync4(full);
|
|
20177
|
-
if (!best || st.mtimeMs > best.mtime) {
|
|
20178
|
-
best = { path: full, mtime: st.mtimeMs };
|
|
20179
|
-
}
|
|
20180
|
-
}
|
|
20181
|
-
return best?.path ?? null;
|
|
20182
|
-
} catch {
|
|
20183
|
-
return null;
|
|
18091
|
+
return best?.path ?? null;
|
|
18092
|
+
} catch {
|
|
18093
|
+
return null;
|
|
20184
18094
|
}
|
|
20185
18095
|
}
|
|
20186
18096
|
function historyAdapterAlias(value) {
|
|
@@ -20498,8 +18408,8 @@ function syntheticContextUsage(eventCount) {
|
|
|
20498
18408
|
return [];
|
|
20499
18409
|
}
|
|
20500
18410
|
const length = Math.max(2, Math.min(eventCount, 24));
|
|
20501
|
-
return Array.from({ length }, (_,
|
|
20502
|
-
const progress = length <= 1 ? 1 :
|
|
18411
|
+
return Array.from({ length }, (_, index) => {
|
|
18412
|
+
const progress = length <= 1 ? 1 : index / (length - 1);
|
|
20503
18413
|
return Math.min(0.92, 0.08 + progress * 0.66);
|
|
20504
18414
|
});
|
|
20505
18415
|
}
|
|
@@ -20630,9 +18540,9 @@ function buildObserveDataFromSnapshot(snapshot, timedEvents = [], live = false)
|
|
|
20630
18540
|
}
|
|
20631
18541
|
}
|
|
20632
18542
|
const seconds = timelineSeconds(eventDrafts.map((draft) => draft.timestampMs), timing.baseTimestampMs);
|
|
20633
|
-
const builtEntries = eventDrafts.map((draft,
|
|
18543
|
+
const builtEntries = eventDrafts.map((draft, index) => ({
|
|
20634
18544
|
draft,
|
|
20635
|
-
event: draft.build(seconds[
|
|
18545
|
+
event: draft.build(seconds[index] ?? 0)
|
|
20636
18546
|
})).filter(({ event }) => event.text.length > 0 || event.kind === "tool" || event.kind === "boot");
|
|
20637
18547
|
const events2 = builtEntries.map((entry) => entry.event);
|
|
20638
18548
|
for (const { draft, event } of builtEntries) {
|
|
@@ -20663,8 +18573,8 @@ function unavailableObserveData(agent) {
|
|
|
20663
18573
|
live: false
|
|
20664
18574
|
};
|
|
20665
18575
|
}
|
|
20666
|
-
async function resolveSnapshotSource(agent,
|
|
20667
|
-
const endpoint =
|
|
18576
|
+
async function resolveSnapshotSource(agent, broker) {
|
|
18577
|
+
const endpoint = broker ? activeEndpoint(broker.snapshot, agent.id) : null;
|
|
20668
18578
|
const liveSnapshot = await readLiveSnapshot(endpoint);
|
|
20669
18579
|
const live = Boolean(liveSnapshot && (liveSnapshot.currentTurnId || liveSnapshot.session.status === "active"));
|
|
20670
18580
|
const historyCandidate = resolveHistoryCandidate(agent, liveSnapshot);
|
|
@@ -20707,8 +18617,8 @@ async function resolveSnapshotSource(agent, broker2) {
|
|
|
20707
18617
|
sessionId: null
|
|
20708
18618
|
};
|
|
20709
18619
|
}
|
|
20710
|
-
async function buildAgentObservePayload(agent,
|
|
20711
|
-
const source = await resolveSnapshotSource(agent,
|
|
18620
|
+
async function buildAgentObservePayload(agent, broker) {
|
|
18621
|
+
const source = await resolveSnapshotSource(agent, broker);
|
|
20712
18622
|
if (source.source === "unavailable") {
|
|
20713
18623
|
return {
|
|
20714
18624
|
agentId: agent.id,
|
|
@@ -20737,8 +18647,8 @@ async function loadAgentObservePayloadsInternal(agentIds) {
|
|
|
20737
18647
|
if (filteredAgents.length === 0) {
|
|
20738
18648
|
return [];
|
|
20739
18649
|
}
|
|
20740
|
-
const
|
|
20741
|
-
return await Promise.all(filteredAgents.map((agent) => buildAgentObservePayload(agent,
|
|
18650
|
+
const broker = await loadScoutBrokerContext();
|
|
18651
|
+
return await Promise.all(filteredAgents.map((agent) => buildAgentObservePayload(agent, broker)));
|
|
20742
18652
|
}
|
|
20743
18653
|
function summarizeAgentObservePayload(payload) {
|
|
20744
18654
|
return {
|
|
@@ -20750,19 +18660,100 @@ function summarizeAgentObservePayload(payload) {
|
|
|
20750
18660
|
}
|
|
20751
18661
|
};
|
|
20752
18662
|
}
|
|
20753
|
-
async function loadAgentObserveSummaries(agentIds) {
|
|
20754
|
-
const payloads = await loadAgentObservePayloadsInternal(agentIds);
|
|
20755
|
-
return payloads.map(summarizeAgentObservePayload);
|
|
20756
|
-
}
|
|
20757
|
-
async function loadAgentObservePayload(agentId) {
|
|
20758
|
-
const payloads = await loadAgentObservePayloadsInternal([agentId]);
|
|
20759
|
-
return payloads[0] ?? null;
|
|
20760
|
-
}
|
|
18663
|
+
async function loadAgentObserveSummaries(agentIds) {
|
|
18664
|
+
const payloads = await loadAgentObservePayloadsInternal(agentIds);
|
|
18665
|
+
return payloads.map(summarizeAgentObservePayload);
|
|
18666
|
+
}
|
|
18667
|
+
async function loadAgentObservePayload(agentId) {
|
|
18668
|
+
const payloads = await loadAgentObservePayloadsInternal([agentId]);
|
|
18669
|
+
return payloads[0] ?? null;
|
|
18670
|
+
}
|
|
18671
|
+
|
|
18672
|
+
// packages/web/server/core/mesh/service.ts
|
|
18673
|
+
await init_broker_service();
|
|
18674
|
+
import { execFile as execFile2 } from "child_process";
|
|
18675
|
+
import { promisify as promisify2 } from "util";
|
|
18676
|
+
|
|
18677
|
+
// packages/runtime/src/tailscale.ts
|
|
18678
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
18679
|
+
import { execFile } from "child_process";
|
|
18680
|
+
import { promisify } from "util";
|
|
18681
|
+
var execFileAsync = promisify(execFile);
|
|
18682
|
+
function parseStatusJson(raw2) {
|
|
18683
|
+
return JSON.parse(raw2);
|
|
18684
|
+
}
|
|
18685
|
+
function parsePeers(status) {
|
|
18686
|
+
const peers = Object.entries(status.Peer ?? {});
|
|
18687
|
+
return peers.map(([fallbackId, peer]) => ({
|
|
18688
|
+
id: peer.ID ?? fallbackId,
|
|
18689
|
+
name: peer.HostName ?? peer.DNSName ?? fallbackId,
|
|
18690
|
+
dnsName: peer.DNSName,
|
|
18691
|
+
addresses: peer.TailscaleIPs ?? [],
|
|
18692
|
+
online: peer.Online ?? false,
|
|
18693
|
+
hostName: peer.HostName,
|
|
18694
|
+
os: peer.OS,
|
|
18695
|
+
tags: peer.Tags ?? []
|
|
18696
|
+
}));
|
|
18697
|
+
}
|
|
18698
|
+
function parseSelf(status) {
|
|
18699
|
+
const self = status.Self;
|
|
18700
|
+
if (!self) {
|
|
18701
|
+
return null;
|
|
18702
|
+
}
|
|
18703
|
+
return {
|
|
18704
|
+
id: self.ID ?? self.DNSName ?? self.HostName ?? "self",
|
|
18705
|
+
name: self.HostName ?? self.DNSName ?? "self",
|
|
18706
|
+
dnsName: self.DNSName,
|
|
18707
|
+
addresses: self.TailscaleIPs ?? [],
|
|
18708
|
+
online: self.Online ?? true,
|
|
18709
|
+
hostName: self.HostName,
|
|
18710
|
+
os: self.OS,
|
|
18711
|
+
tailnetName: status.CurrentTailnet?.Name,
|
|
18712
|
+
magicDnsSuffix: status.CurrentTailnet?.MagicDNSSuffix
|
|
18713
|
+
};
|
|
18714
|
+
}
|
|
18715
|
+
function isBackendRunning(status) {
|
|
18716
|
+
return (status.BackendState ?? "").trim().toLowerCase() === "running";
|
|
18717
|
+
}
|
|
18718
|
+
async function readStatusJsonFromFile(filePath) {
|
|
18719
|
+
const raw2 = await readFile7(filePath, "utf8");
|
|
18720
|
+
return parseStatusJson(raw2);
|
|
18721
|
+
}
|
|
18722
|
+
async function readStatusJson() {
|
|
18723
|
+
const fixturePath = process.env.OPENSCOUT_TAILSCALE_STATUS_JSON;
|
|
18724
|
+
if (fixturePath) {
|
|
18725
|
+
return readStatusJsonFromFile(fixturePath);
|
|
18726
|
+
}
|
|
18727
|
+
try {
|
|
18728
|
+
const tailscaleBin = process.env.OPENSCOUT_TAILSCALE_BIN ?? "tailscale";
|
|
18729
|
+
const { stdout } = await execFileAsync(tailscaleBin, ["status", "--json"]);
|
|
18730
|
+
return parseStatusJson(stdout);
|
|
18731
|
+
} catch {
|
|
18732
|
+
return null;
|
|
18733
|
+
}
|
|
18734
|
+
}
|
|
18735
|
+
async function readTailscaleSelf() {
|
|
18736
|
+
const summary = await readTailscaleStatusSummary();
|
|
18737
|
+
if (!summary) {
|
|
18738
|
+
return null;
|
|
18739
|
+
}
|
|
18740
|
+
return summary.self;
|
|
18741
|
+
}
|
|
18742
|
+
async function readTailscaleStatusSummary() {
|
|
18743
|
+
const status = await readStatusJson();
|
|
18744
|
+
if (!status) {
|
|
18745
|
+
return null;
|
|
18746
|
+
}
|
|
18747
|
+
return {
|
|
18748
|
+
backendState: status.BackendState ?? null,
|
|
18749
|
+
running: isBackendRunning(status),
|
|
18750
|
+
health: status.Health ?? [],
|
|
18751
|
+
peers: parsePeers(status),
|
|
18752
|
+
self: parseSelf(status)
|
|
18753
|
+
};
|
|
18754
|
+
}
|
|
20761
18755
|
|
|
20762
18756
|
// packages/web/server/core/mesh/service.ts
|
|
20763
|
-
await init_broker_service();
|
|
20764
|
-
import { execFile as execFile2 } from "child_process";
|
|
20765
|
-
import { promisify as promisify2 } from "util";
|
|
20766
18757
|
var execFileAsync2 = promisify2(execFile2);
|
|
20767
18758
|
function normalizeHost(host) {
|
|
20768
18759
|
return host.trim().replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
|
|
@@ -20808,7 +18799,7 @@ async function readTailscaleStatus() {
|
|
|
20808
18799
|
function formatIssueWarning(issue) {
|
|
20809
18800
|
return issue.action ? `${issue.title} \u2014 ${issue.summary} ${issue.action}` : `${issue.title} \u2014 ${issue.summary}`;
|
|
20810
18801
|
}
|
|
20811
|
-
function computeIssues(health, localNode, nodes,
|
|
18802
|
+
function computeIssues(health, localNode, nodes, tailscale) {
|
|
20812
18803
|
const issues = [];
|
|
20813
18804
|
if (!health.reachable) {
|
|
20814
18805
|
issues.push({
|
|
@@ -20821,7 +18812,7 @@ function computeIssues(health, localNode, nodes, tailscale2) {
|
|
|
20821
18812
|
});
|
|
20822
18813
|
return issues;
|
|
20823
18814
|
}
|
|
20824
|
-
if (
|
|
18815
|
+
if (tailscale.available && !tailscale.running) {
|
|
20825
18816
|
issues.push({
|
|
20826
18817
|
code: "tailscale_stopped",
|
|
20827
18818
|
severity: "warning",
|
|
@@ -20851,7 +18842,7 @@ function computeIssues(health, localNode, nodes, tailscale2) {
|
|
|
20851
18842
|
});
|
|
20852
18843
|
}
|
|
20853
18844
|
const remoteNodes = Object.values(nodes).filter((n) => n.id !== localNode?.id);
|
|
20854
|
-
if (!
|
|
18845
|
+
if (!tailscale.available && remoteNodes.length === 0) {
|
|
20855
18846
|
issues.push({
|
|
20856
18847
|
code: "discovery_unconfigured",
|
|
20857
18848
|
severity: "warning",
|
|
@@ -20940,7 +18931,7 @@ function filterCurrentMeshNodes(allNodes, meshId, localNodeId, now) {
|
|
|
20940
18931
|
}
|
|
20941
18932
|
async function loadMeshStatus() {
|
|
20942
18933
|
const brokerUrl = resolveScoutBrokerUrl();
|
|
20943
|
-
const [health, context,
|
|
18934
|
+
const [health, context, tailscale] = await Promise.all([
|
|
20944
18935
|
readScoutBrokerHealth(brokerUrl),
|
|
20945
18936
|
loadScoutBrokerContext(brokerUrl),
|
|
20946
18937
|
readTailscaleStatus()
|
|
@@ -20950,9 +18941,9 @@ async function loadMeshStatus() {
|
|
|
20950
18941
|
const meshId = health.meshId ?? localNode?.meshId ?? null;
|
|
20951
18942
|
const identity = computeIdentitySummary(health, localNode, meshId);
|
|
20952
18943
|
const nodes = filterCurrentMeshNodes(allNodes, meshId, localNode?.id, Date.now());
|
|
20953
|
-
const issues = computeIssues(health, localNode, nodes,
|
|
18944
|
+
const issues = computeIssues(health, localNode, nodes, tailscale);
|
|
20954
18945
|
const warnings = issues.map(formatIssueWarning);
|
|
20955
|
-
return { brokerUrl, health, localNode, meshId, identity, nodes, tailscale
|
|
18946
|
+
return { brokerUrl, health, localNode, meshId, identity, nodes, tailscale, issues, warnings };
|
|
20956
18947
|
}
|
|
20957
18948
|
function preferredAnnounceHost(self, currentBrokerUrl) {
|
|
20958
18949
|
const dnsName = stripTrailingDot(self?.dnsName);
|
|
@@ -21055,415 +19046,6 @@ init_setup();
|
|
|
21055
19046
|
init_support_paths();
|
|
21056
19047
|
init_claude_stream_json();
|
|
21057
19048
|
init_harness_catalog();
|
|
21058
|
-
|
|
21059
|
-
// ../hudson/packages/hudson-relay/src/relay/session.ts
|
|
21060
|
-
import { createRequire } from "module";
|
|
21061
|
-
import { execSync as execSync2 } from "child_process";
|
|
21062
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
21063
|
-
import { join as join19, dirname as pathDirname } from "path";
|
|
21064
|
-
var require2 = createRequire(import.meta.url);
|
|
21065
|
-
var pty = require2("node-pty");
|
|
21066
|
-
var DEFAULT_ORPHAN_TTL_MS = 30 * 60 * 1000;
|
|
21067
|
-
var MAX_BUFFER_SIZE = 512 * 1024;
|
|
21068
|
-
var sessions3 = new Map;
|
|
21069
|
-
function generateId() {
|
|
21070
|
-
return Math.random().toString(36).slice(2, 10);
|
|
21071
|
-
}
|
|
21072
|
-
function send(ws, data) {
|
|
21073
|
-
if (ws.readyState === 1) {
|
|
21074
|
-
ws.send(JSON.stringify(data));
|
|
21075
|
-
}
|
|
21076
|
-
}
|
|
21077
|
-
function resolveCwd(raw2) {
|
|
21078
|
-
const home = process.env.HOME || "/tmp";
|
|
21079
|
-
const expanded = (raw2 || home).replace(/^~/, home);
|
|
21080
|
-
if (existsSync13(expanded))
|
|
21081
|
-
return expanded;
|
|
21082
|
-
try {
|
|
21083
|
-
mkdirSync6(expanded, { recursive: true });
|
|
21084
|
-
console.log(`[relay] Created missing cwd: ${expanded}`);
|
|
21085
|
-
return expanded;
|
|
21086
|
-
} catch {
|
|
21087
|
-
console.warn(`[relay] Could not create cwd ${expanded}, falling back to ${home}`);
|
|
21088
|
-
return home;
|
|
21089
|
-
}
|
|
21090
|
-
}
|
|
21091
|
-
function findBin(name, envOverride) {
|
|
21092
|
-
if (envOverride && process.env[envOverride])
|
|
21093
|
-
return process.env[envOverride];
|
|
21094
|
-
try {
|
|
21095
|
-
return execSync2(`which ${name}`, { encoding: "utf8" }).trim() || null;
|
|
21096
|
-
} catch {
|
|
21097
|
-
return null;
|
|
21098
|
-
}
|
|
21099
|
-
}
|
|
21100
|
-
function findClaudeBin() {
|
|
21101
|
-
return findBin("claude", "CLAUDE_BIN");
|
|
21102
|
-
}
|
|
21103
|
-
function findPiBin() {
|
|
21104
|
-
return findBin("pi", "PI_BIN");
|
|
21105
|
-
}
|
|
21106
|
-
function tmuxSessionExists2(name) {
|
|
21107
|
-
try {
|
|
21108
|
-
execSync2(`tmux has-session -t ${name} 2>/dev/null`, { encoding: "utf8" });
|
|
21109
|
-
return true;
|
|
21110
|
-
} catch {
|
|
21111
|
-
return false;
|
|
21112
|
-
}
|
|
21113
|
-
}
|
|
21114
|
-
function bootstrapFiles(cwd, files, sessionId) {
|
|
21115
|
-
for (const [relPath, content] of Object.entries(files)) {
|
|
21116
|
-
const absPath = join19(cwd, relPath);
|
|
21117
|
-
if (!existsSync13(absPath)) {
|
|
21118
|
-
try {
|
|
21119
|
-
const dir = pathDirname(absPath);
|
|
21120
|
-
if (!existsSync13(dir))
|
|
21121
|
-
mkdirSync6(dir, { recursive: true });
|
|
21122
|
-
writeFileSync6(absPath, content, "utf-8");
|
|
21123
|
-
console.log(`[relay] Session ${sessionId}: bootstrapped ${relPath}`);
|
|
21124
|
-
} catch (err) {
|
|
21125
|
-
console.warn(`[relay] Session ${sessionId}: failed to bootstrap ${relPath}:`, err);
|
|
21126
|
-
}
|
|
21127
|
-
}
|
|
21128
|
-
}
|
|
21129
|
-
}
|
|
21130
|
-
function spawnTmuxSession(tmuxName, cols, rows, cwd, claudeBin, claudeArgs, env) {
|
|
21131
|
-
const exists = tmuxSessionExists2(tmuxName);
|
|
21132
|
-
if (!exists) {
|
|
21133
|
-
const shellCmd = [claudeBin, ...claudeArgs].map((a) => a.includes(" ") ? `'${a}'` : a).join(" ");
|
|
21134
|
-
execSync2(`tmux new-session -d -s ${tmuxName} -x ${cols} -y ${rows} -c '${cwd}' '${shellCmd}'`, { env });
|
|
21135
|
-
console.log(`[relay] Created tmux session: ${tmuxName}`);
|
|
21136
|
-
} else {
|
|
21137
|
-
try {
|
|
21138
|
-
execSync2(`tmux resize-window -t ${tmuxName} -x ${cols} -y ${rows} 2>/dev/null`);
|
|
21139
|
-
} catch {}
|
|
21140
|
-
console.log(`[relay] Attaching to existing tmux session: ${tmuxName}`);
|
|
21141
|
-
}
|
|
21142
|
-
return pty.spawn("tmux", ["attach", "-t", tmuxName], {
|
|
21143
|
-
name: "xterm-256color",
|
|
21144
|
-
cols,
|
|
21145
|
-
rows,
|
|
21146
|
-
cwd,
|
|
21147
|
-
env
|
|
21148
|
-
});
|
|
21149
|
-
}
|
|
21150
|
-
function createSession(ws, msg) {
|
|
21151
|
-
const id = generateId();
|
|
21152
|
-
const cols = Math.max(msg.cols || 80, 20);
|
|
21153
|
-
const rows = Math.max(msg.rows || 24, 4);
|
|
21154
|
-
const backend = msg.backend || "pty";
|
|
21155
|
-
const tmuxName = msg.tmuxSession || `hudson-${id}`;
|
|
21156
|
-
const agent = msg.agent || "claude";
|
|
21157
|
-
let agentBin;
|
|
21158
|
-
if (agent === "pi") {
|
|
21159
|
-
agentBin = findPiBin();
|
|
21160
|
-
if (!agentBin) {
|
|
21161
|
-
const reason = "pi CLI not found. Install it with: npm install -g @mariozechner/pi-coding-agent";
|
|
21162
|
-
console.error(`[relay] Session ${id} failed: ${reason}`);
|
|
21163
|
-
send(ws, { type: "session:error", error: reason });
|
|
21164
|
-
return null;
|
|
21165
|
-
}
|
|
21166
|
-
} else {
|
|
21167
|
-
agentBin = findClaudeBin();
|
|
21168
|
-
if (!agentBin) {
|
|
21169
|
-
const reason = "Claude CLI not found. Install it with: npm install -g @anthropic-ai/claude-code";
|
|
21170
|
-
console.error(`[relay] Session ${id} failed: ${reason}`);
|
|
21171
|
-
send(ws, { type: "session:error", error: reason });
|
|
21172
|
-
return null;
|
|
21173
|
-
}
|
|
21174
|
-
}
|
|
21175
|
-
if (!existsSync13(agentBin)) {
|
|
21176
|
-
const reason = `${agent} binary not found at ${agentBin}`;
|
|
21177
|
-
console.error(`[relay] Session ${id} failed: ${reason}`);
|
|
21178
|
-
send(ws, { type: "session:error", error: reason });
|
|
21179
|
-
return null;
|
|
21180
|
-
}
|
|
21181
|
-
if (backend === "tmux" && !findBin("tmux")) {
|
|
21182
|
-
const reason = "tmux not found. Install it with: brew install tmux";
|
|
21183
|
-
console.error(`[relay] Session ${id} failed: ${reason}`);
|
|
21184
|
-
send(ws, { type: "session:error", error: reason });
|
|
21185
|
-
return null;
|
|
21186
|
-
}
|
|
21187
|
-
const cwd = resolveCwd(msg.cwd);
|
|
21188
|
-
if (msg.workspaceFiles) {
|
|
21189
|
-
bootstrapFiles(cwd, msg.workspaceFiles, id);
|
|
21190
|
-
}
|
|
21191
|
-
let agentArgs;
|
|
21192
|
-
if (agent === "pi") {
|
|
21193
|
-
agentArgs = ["--verbose"];
|
|
21194
|
-
if (msg.provider)
|
|
21195
|
-
agentArgs.push("--provider", msg.provider);
|
|
21196
|
-
if (msg.model)
|
|
21197
|
-
agentArgs.push("--model", msg.model);
|
|
21198
|
-
if (msg.systemPrompt)
|
|
21199
|
-
agentArgs.push("--system-prompt", msg.systemPrompt);
|
|
21200
|
-
} else {
|
|
21201
|
-
agentArgs = ["--verbose"];
|
|
21202
|
-
if (msg.systemPrompt)
|
|
21203
|
-
agentArgs.push("--system-prompt", msg.systemPrompt);
|
|
21204
|
-
}
|
|
21205
|
-
const env = { ...process.env, TERM: "xterm-256color", FORCE_COLOR: "1" };
|
|
21206
|
-
delete env.CLAUDECODE;
|
|
21207
|
-
let ptyProcess;
|
|
21208
|
-
try {
|
|
21209
|
-
if (backend === "tmux") {
|
|
21210
|
-
console.log(`[relay] Session ${id}: tmux backend (session: ${tmuxName}) in ${cwd} [agent: ${agent}]`);
|
|
21211
|
-
ptyProcess = spawnTmuxSession(tmuxName, cols, rows, cwd, agentBin, agentArgs, env);
|
|
21212
|
-
} else {
|
|
21213
|
-
console.log(`[relay] Session ${id}: pty backend, spawning ${agentBin} in ${cwd} [agent: ${agent}]`);
|
|
21214
|
-
ptyProcess = pty.spawn(agentBin, agentArgs, {
|
|
21215
|
-
name: "xterm-256color",
|
|
21216
|
-
cols,
|
|
21217
|
-
rows,
|
|
21218
|
-
cwd,
|
|
21219
|
-
env
|
|
21220
|
-
});
|
|
21221
|
-
}
|
|
21222
|
-
} catch (err) {
|
|
21223
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
21224
|
-
console.error(`[relay] Session ${id}: failed to spawn PTY \u2014 ${message}`);
|
|
21225
|
-
send(ws, { type: "session:error", error: `Failed to spawn terminal: ${message}` });
|
|
21226
|
-
return null;
|
|
21227
|
-
}
|
|
21228
|
-
const orphanTTL = msg.orphanTTL && msg.orphanTTL > 0 ? msg.orphanTTL : DEFAULT_ORPHAN_TTL_MS;
|
|
21229
|
-
const session = {
|
|
21230
|
-
id,
|
|
21231
|
-
pty: ptyProcess,
|
|
21232
|
-
ws,
|
|
21233
|
-
outputBuffer: "",
|
|
21234
|
-
cols,
|
|
21235
|
-
rows,
|
|
21236
|
-
reapTimer: null,
|
|
21237
|
-
orphanTTL,
|
|
21238
|
-
backend,
|
|
21239
|
-
...backend === "tmux" ? { tmuxSession: tmuxName } : {},
|
|
21240
|
-
exited: false,
|
|
21241
|
-
exitCode: null
|
|
21242
|
-
};
|
|
21243
|
-
const startTime = Date.now();
|
|
21244
|
-
ptyProcess.onData((data) => {
|
|
21245
|
-
session.outputBuffer += data;
|
|
21246
|
-
if (session.outputBuffer.length > MAX_BUFFER_SIZE) {
|
|
21247
|
-
session.outputBuffer = session.outputBuffer.slice(-MAX_BUFFER_SIZE);
|
|
21248
|
-
}
|
|
21249
|
-
if (session.ws && session.ws.readyState === 1) {
|
|
21250
|
-
send(session.ws, { type: "terminal:data", data });
|
|
21251
|
-
}
|
|
21252
|
-
});
|
|
21253
|
-
ptyProcess.onExit(({ exitCode }) => {
|
|
21254
|
-
session.exited = true;
|
|
21255
|
-
session.exitCode = exitCode;
|
|
21256
|
-
const uptime = Date.now() - startTime;
|
|
21257
|
-
const crashed = exitCode !== 0 && uptime < 5000;
|
|
21258
|
-
let reason;
|
|
21259
|
-
if (crashed) {
|
|
21260
|
-
const clean = session.outputBuffer.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").trim();
|
|
21261
|
-
const lines = clean.split(`
|
|
21262
|
-
`).filter((l) => l.trim()).slice(-5);
|
|
21263
|
-
reason = lines.join(`
|
|
21264
|
-
`) || `Process exited with code ${exitCode}`;
|
|
21265
|
-
console.error(`[relay] Session ${id} crashed after ${uptime}ms (code ${exitCode}): ${reason}`);
|
|
21266
|
-
}
|
|
21267
|
-
if (session.ws) {
|
|
21268
|
-
send(session.ws, { type: "session:exit", exitCode, ...reason ? { reason } : {} });
|
|
21269
|
-
}
|
|
21270
|
-
scheduleReap(session, 1e4);
|
|
21271
|
-
});
|
|
21272
|
-
sessions3.set(id, session);
|
|
21273
|
-
console.log(`[relay] Session ${id} created (${cols}x${rows})`);
|
|
21274
|
-
return session;
|
|
21275
|
-
}
|
|
21276
|
-
function attachSession(session, ws, cols, rows) {
|
|
21277
|
-
if (session.reapTimer) {
|
|
21278
|
-
clearTimeout(session.reapTimer);
|
|
21279
|
-
session.reapTimer = null;
|
|
21280
|
-
}
|
|
21281
|
-
session.ws = ws;
|
|
21282
|
-
if (cols && rows) {
|
|
21283
|
-
const c = Math.max(cols, 20);
|
|
21284
|
-
const r = Math.max(rows, 4);
|
|
21285
|
-
if (c !== session.cols || r !== session.rows) {
|
|
21286
|
-
session.pty.resize(c, r);
|
|
21287
|
-
session.cols = c;
|
|
21288
|
-
session.rows = r;
|
|
21289
|
-
}
|
|
21290
|
-
}
|
|
21291
|
-
if (session.exited) {
|
|
21292
|
-
send(ws, { type: "session:exit", exitCode: session.exitCode });
|
|
21293
|
-
} else if (session.outputBuffer.length > 0) {
|
|
21294
|
-
send(ws, { type: "terminal:data", data: session.outputBuffer });
|
|
21295
|
-
}
|
|
21296
|
-
console.log(`[relay] Session ${session.id} reconnected`);
|
|
21297
|
-
}
|
|
21298
|
-
function detachSession(session) {
|
|
21299
|
-
session.ws = null;
|
|
21300
|
-
if (session.exited) {
|
|
21301
|
-
scheduleReap(session, 5000);
|
|
21302
|
-
} else {
|
|
21303
|
-
scheduleReap(session, session.orphanTTL);
|
|
21304
|
-
console.log(`[relay] Session ${session.id} detached (orphaned for ${session.orphanTTL / 1000}s)`);
|
|
21305
|
-
}
|
|
21306
|
-
}
|
|
21307
|
-
function scheduleReap(session, delay) {
|
|
21308
|
-
if (session.reapTimer)
|
|
21309
|
-
clearTimeout(session.reapTimer);
|
|
21310
|
-
session.reapTimer = setTimeout(() => {
|
|
21311
|
-
if (!session.ws) {
|
|
21312
|
-
destroy(session.id);
|
|
21313
|
-
}
|
|
21314
|
-
}, delay);
|
|
21315
|
-
}
|
|
21316
|
-
function destroy(sessionId) {
|
|
21317
|
-
const session = sessions3.get(sessionId);
|
|
21318
|
-
if (!session)
|
|
21319
|
-
return;
|
|
21320
|
-
if (session.reapTimer)
|
|
21321
|
-
clearTimeout(session.reapTimer);
|
|
21322
|
-
try {
|
|
21323
|
-
session.pty.kill();
|
|
21324
|
-
} catch {}
|
|
21325
|
-
sessions3.delete(sessionId);
|
|
21326
|
-
if (session.backend === "tmux") {
|
|
21327
|
-
console.log(`[relay] Session ${sessionId} bridge destroyed (tmux session '${session.tmuxSession}' still alive)`);
|
|
21328
|
-
} else {
|
|
21329
|
-
console.log(`[relay] Session ${sessionId} destroyed`);
|
|
21330
|
-
}
|
|
21331
|
-
}
|
|
21332
|
-
|
|
21333
|
-
// packages/web/server/relay.ts
|
|
21334
|
-
import { mkdir as mkdir7 } from "fs/promises";
|
|
21335
|
-
import { join as join20 } from "path";
|
|
21336
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
21337
|
-
var UPLOAD_DIR = "/tmp/scout-uploads";
|
|
21338
|
-
async function handleRelayUpload(req) {
|
|
21339
|
-
const { name, data } = await req.json();
|
|
21340
|
-
if (!name || !data) {
|
|
21341
|
-
return Response.json({ error: "Missing name or data" }, { status: 400 });
|
|
21342
|
-
}
|
|
21343
|
-
await mkdir7(UPLOAD_DIR, { recursive: true });
|
|
21344
|
-
const filename = `${randomUUID3()}-${name}`;
|
|
21345
|
-
const filepath = join20(UPLOAD_DIR, filename);
|
|
21346
|
-
await Bun.write(filepath, Buffer.from(data, "base64"));
|
|
21347
|
-
return Response.json({ path: filepath });
|
|
21348
|
-
}
|
|
21349
|
-
var _pendingCommand = null;
|
|
21350
|
-
function queueTerminalCommand(cmd) {
|
|
21351
|
-
for (const [, session] of sessions3) {
|
|
21352
|
-
if (!session.exited) {
|
|
21353
|
-
session.pty.write(cmd + `
|
|
21354
|
-
`);
|
|
21355
|
-
return;
|
|
21356
|
-
}
|
|
21357
|
-
}
|
|
21358
|
-
_pendingCommand = cmd;
|
|
21359
|
-
}
|
|
21360
|
-
function drainPendingCommand() {
|
|
21361
|
-
const cmd = _pendingCommand;
|
|
21362
|
-
_pendingCommand = null;
|
|
21363
|
-
return cmd;
|
|
21364
|
-
}
|
|
21365
|
-
function asRelay(ws) {
|
|
21366
|
-
return ws;
|
|
21367
|
-
}
|
|
21368
|
-
function parseMessage(raw2) {
|
|
21369
|
-
try {
|
|
21370
|
-
const msg = JSON.parse(raw2);
|
|
21371
|
-
if (typeof msg.type === "string")
|
|
21372
|
-
return msg;
|
|
21373
|
-
} catch {}
|
|
21374
|
-
return null;
|
|
21375
|
-
}
|
|
21376
|
-
var relayWebSocket = {
|
|
21377
|
-
open(_ws) {},
|
|
21378
|
-
message(ws, raw2) {
|
|
21379
|
-
const msg = parseMessage(typeof raw2 === "string" ? raw2 : raw2.toString());
|
|
21380
|
-
if (!msg)
|
|
21381
|
-
return;
|
|
21382
|
-
const rws = asRelay(ws);
|
|
21383
|
-
switch (msg.type) {
|
|
21384
|
-
case "session:init": {
|
|
21385
|
-
if (ws.data.sessionId) {
|
|
21386
|
-
const prev = sessions3.get(ws.data.sessionId);
|
|
21387
|
-
if (prev)
|
|
21388
|
-
detachSession(prev);
|
|
21389
|
-
}
|
|
21390
|
-
const session = createSession(rws, msg);
|
|
21391
|
-
if (!session)
|
|
21392
|
-
break;
|
|
21393
|
-
ws.data.sessionId = session.id;
|
|
21394
|
-
send(rws, { type: "session:ready", sessionId: session.id });
|
|
21395
|
-
const pending = drainPendingCommand();
|
|
21396
|
-
if (pending)
|
|
21397
|
-
setTimeout(() => session.pty.write(pending + `
|
|
21398
|
-
`), 400);
|
|
21399
|
-
break;
|
|
21400
|
-
}
|
|
21401
|
-
case "session:reconnect": {
|
|
21402
|
-
const existing = sessions3.get(msg.sessionId);
|
|
21403
|
-
if (existing && !existing.exited) {
|
|
21404
|
-
if (ws.data.sessionId && ws.data.sessionId !== msg.sessionId) {
|
|
21405
|
-
const prev = sessions3.get(ws.data.sessionId);
|
|
21406
|
-
if (prev)
|
|
21407
|
-
detachSession(prev);
|
|
21408
|
-
}
|
|
21409
|
-
if (existing.ws && existing.ws !== rws) {
|
|
21410
|
-
send(existing.ws, { type: "session:detached" });
|
|
21411
|
-
}
|
|
21412
|
-
ws.data.sessionId = existing.id;
|
|
21413
|
-
attachSession(existing, rws, msg.cols, msg.rows);
|
|
21414
|
-
send(rws, {
|
|
21415
|
-
type: "session:ready",
|
|
21416
|
-
sessionId: existing.id,
|
|
21417
|
-
reconnected: true
|
|
21418
|
-
});
|
|
21419
|
-
const pending = drainPendingCommand();
|
|
21420
|
-
if (pending)
|
|
21421
|
-
setTimeout(() => existing.pty.write(pending + `
|
|
21422
|
-
`), 400);
|
|
21423
|
-
} else {
|
|
21424
|
-
send(rws, { type: "session:expired", sessionId: msg.sessionId });
|
|
21425
|
-
}
|
|
21426
|
-
break;
|
|
21427
|
-
}
|
|
21428
|
-
case "terminal:input": {
|
|
21429
|
-
if (!ws.data.sessionId)
|
|
21430
|
-
return;
|
|
21431
|
-
const session = sessions3.get(ws.data.sessionId);
|
|
21432
|
-
if (session && !session.exited) {
|
|
21433
|
-
session.pty.write(msg.data);
|
|
21434
|
-
}
|
|
21435
|
-
break;
|
|
21436
|
-
}
|
|
21437
|
-
case "terminal:resize": {
|
|
21438
|
-
if (!ws.data.sessionId)
|
|
21439
|
-
return;
|
|
21440
|
-
const session = sessions3.get(ws.data.sessionId);
|
|
21441
|
-
if (session && !session.exited) {
|
|
21442
|
-
const cols = Math.max(msg.cols || 80, 20);
|
|
21443
|
-
const rows = Math.max(msg.rows || 24, 4);
|
|
21444
|
-
session.pty.resize(cols, rows);
|
|
21445
|
-
session.cols = cols;
|
|
21446
|
-
session.rows = rows;
|
|
21447
|
-
}
|
|
21448
|
-
break;
|
|
21449
|
-
}
|
|
21450
|
-
}
|
|
21451
|
-
},
|
|
21452
|
-
close(ws) {
|
|
21453
|
-
if (ws.data.sessionId) {
|
|
21454
|
-
const session = sessions3.get(ws.data.sessionId);
|
|
21455
|
-
if (session)
|
|
21456
|
-
detachSession(session);
|
|
21457
|
-
ws.data.sessionId = null;
|
|
21458
|
-
}
|
|
21459
|
-
}
|
|
21460
|
-
};
|
|
21461
|
-
function destroyAllRelaySessions() {
|
|
21462
|
-
for (const [id] of sessions3)
|
|
21463
|
-
destroy(id);
|
|
21464
|
-
}
|
|
21465
|
-
|
|
21466
|
-
// packages/web/server/create-openscout-web-server.ts
|
|
21467
19049
|
function parseOptionalPositiveInt(value, fallback) {
|
|
21468
19050
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
21469
19051
|
return fallback;
|
|
@@ -21518,7 +19100,7 @@ function resolveStaticRoot(staticRoot) {
|
|
|
21518
19100
|
return configured;
|
|
21519
19101
|
}
|
|
21520
19102
|
const bundled = resolveBundledStaticClientRoot(import.meta.url);
|
|
21521
|
-
if (
|
|
19103
|
+
if (existsSync13(resolve9(bundled, "index.html"))) {
|
|
21522
19104
|
return bundled;
|
|
21523
19105
|
}
|
|
21524
19106
|
return resolveSourceStaticClientRoot(import.meta.url);
|
|
@@ -21791,7 +19373,15 @@ async function createOpenScoutWebServer(options) {
|
|
|
21791
19373
|
const body = await c.req.json();
|
|
21792
19374
|
if (!body.command)
|
|
21793
19375
|
return c.json({ error: "missing command" }, 400);
|
|
21794
|
-
|
|
19376
|
+
if (!options.runTerminalCommand) {
|
|
19377
|
+
return c.json({ error: "terminal relay is unavailable" }, 503);
|
|
19378
|
+
}
|
|
19379
|
+
try {
|
|
19380
|
+
await options.runTerminalCommand(body.command);
|
|
19381
|
+
} catch (error) {
|
|
19382
|
+
const message = error instanceof Error ? error.message : "failed to queue command";
|
|
19383
|
+
return c.json({ error: message }, 503);
|
|
19384
|
+
}
|
|
21795
19385
|
return c.json({ ok: true });
|
|
21796
19386
|
});
|
|
21797
19387
|
app.post("/api/agents/:agentId/interrupt", async (c) => {
|
|
@@ -21887,6 +19477,254 @@ async function createOpenScoutWebServer(options) {
|
|
|
21887
19477
|
return { app, warmupCaches };
|
|
21888
19478
|
}
|
|
21889
19479
|
|
|
19480
|
+
// packages/web/server/relay.ts
|
|
19481
|
+
import { mkdir as mkdir7 } from "fs/promises";
|
|
19482
|
+
import { join as join19 } from "path";
|
|
19483
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
19484
|
+
var UPLOAD_DIR = "/tmp/scout-uploads";
|
|
19485
|
+
async function handleRelayUpload(req) {
|
|
19486
|
+
const { name, data } = await req.json();
|
|
19487
|
+
if (!name || !data) {
|
|
19488
|
+
return Response.json({ error: "Missing name or data" }, { status: 400 });
|
|
19489
|
+
}
|
|
19490
|
+
await mkdir7(UPLOAD_DIR, { recursive: true });
|
|
19491
|
+
const filename = `${randomUUID3()}-${name}`;
|
|
19492
|
+
const filepath = join19(UPLOAD_DIR, filename);
|
|
19493
|
+
await Bun.write(filepath, Buffer.from(data, "base64"));
|
|
19494
|
+
return Response.json({ path: filepath });
|
|
19495
|
+
}
|
|
19496
|
+
function flushPending(ws) {
|
|
19497
|
+
const upstream = ws.data.upstream;
|
|
19498
|
+
if (!upstream || upstream.readyState !== WebSocket.OPEN) {
|
|
19499
|
+
return;
|
|
19500
|
+
}
|
|
19501
|
+
for (const payload of ws.data.pending) {
|
|
19502
|
+
upstream.send(payload);
|
|
19503
|
+
}
|
|
19504
|
+
ws.data.pending.length = 0;
|
|
19505
|
+
}
|
|
19506
|
+
function forwardToClient(ws, data) {
|
|
19507
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
19508
|
+
return;
|
|
19509
|
+
}
|
|
19510
|
+
if (typeof data === "string") {
|
|
19511
|
+
ws.send(data);
|
|
19512
|
+
return;
|
|
19513
|
+
}
|
|
19514
|
+
if (data instanceof Blob) {
|
|
19515
|
+
data.arrayBuffer().then((buffer) => {
|
|
19516
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
19517
|
+
ws.send(buffer);
|
|
19518
|
+
}
|
|
19519
|
+
}).catch(() => {});
|
|
19520
|
+
return;
|
|
19521
|
+
}
|
|
19522
|
+
ws.send(data);
|
|
19523
|
+
}
|
|
19524
|
+
function createRelayWebSocketProxy(targetUrl) {
|
|
19525
|
+
return {
|
|
19526
|
+
open(ws) {
|
|
19527
|
+
ws.data.pending = [];
|
|
19528
|
+
const upstream = new WebSocket(targetUrl);
|
|
19529
|
+
upstream.binaryType = "arraybuffer";
|
|
19530
|
+
ws.data.upstream = upstream;
|
|
19531
|
+
upstream.onopen = () => {
|
|
19532
|
+
flushPending(ws);
|
|
19533
|
+
};
|
|
19534
|
+
upstream.onmessage = (event) => {
|
|
19535
|
+
forwardToClient(ws, event.data);
|
|
19536
|
+
};
|
|
19537
|
+
upstream.onerror = () => {
|
|
19538
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
19539
|
+
ws.close();
|
|
19540
|
+
}
|
|
19541
|
+
};
|
|
19542
|
+
upstream.onclose = () => {
|
|
19543
|
+
ws.data.upstream = null;
|
|
19544
|
+
ws.data.pending.length = 0;
|
|
19545
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
19546
|
+
ws.close();
|
|
19547
|
+
}
|
|
19548
|
+
};
|
|
19549
|
+
},
|
|
19550
|
+
message(ws, raw2) {
|
|
19551
|
+
const upstream = ws.data.upstream;
|
|
19552
|
+
const payload = raw2;
|
|
19553
|
+
if (!upstream || upstream.readyState === WebSocket.CONNECTING) {
|
|
19554
|
+
ws.data.pending.push(payload);
|
|
19555
|
+
return;
|
|
19556
|
+
}
|
|
19557
|
+
if (upstream.readyState === WebSocket.OPEN) {
|
|
19558
|
+
upstream.send(payload);
|
|
19559
|
+
}
|
|
19560
|
+
},
|
|
19561
|
+
close(ws) {
|
|
19562
|
+
const upstream = ws.data.upstream;
|
|
19563
|
+
ws.data.upstream = null;
|
|
19564
|
+
ws.data.pending.length = 0;
|
|
19565
|
+
if (!upstream) {
|
|
19566
|
+
return;
|
|
19567
|
+
}
|
|
19568
|
+
if (upstream.readyState === WebSocket.OPEN || upstream.readyState === WebSocket.CONNECTING) {
|
|
19569
|
+
upstream.close();
|
|
19570
|
+
}
|
|
19571
|
+
}
|
|
19572
|
+
};
|
|
19573
|
+
}
|
|
19574
|
+
|
|
19575
|
+
// packages/web/server/managed-terminal-relay.ts
|
|
19576
|
+
import { existsSync as existsSync14 } from "fs";
|
|
19577
|
+
import { spawn as spawn5, spawnSync as spawnSync2 } from "child_process";
|
|
19578
|
+
import { dirname as dirname13, resolve as resolve10 } from "path";
|
|
19579
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
19580
|
+
function relayPortForWebPort(webPort) {
|
|
19581
|
+
const configured = process.env.OPENSCOUT_WEB_TERMINAL_RELAY_PORT?.trim();
|
|
19582
|
+
if (configured) {
|
|
19583
|
+
const parsed = Number.parseInt(configured, 10);
|
|
19584
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
19585
|
+
return parsed;
|
|
19586
|
+
}
|
|
19587
|
+
}
|
|
19588
|
+
return webPort + 1;
|
|
19589
|
+
}
|
|
19590
|
+
function relayHostForWebHost(hostname2) {
|
|
19591
|
+
return process.env.OPENSCOUT_WEB_TERMINAL_RELAY_HOST?.trim() || hostname2;
|
|
19592
|
+
}
|
|
19593
|
+
function relayLoopbackHost(hostname2) {
|
|
19594
|
+
if (hostname2 === "0.0.0.0" || hostname2 === "::") {
|
|
19595
|
+
return "127.0.0.1";
|
|
19596
|
+
}
|
|
19597
|
+
return hostname2;
|
|
19598
|
+
}
|
|
19599
|
+
async function isHealthy(targetHttpUrl) {
|
|
19600
|
+
try {
|
|
19601
|
+
const response = await fetch(`${targetHttpUrl}/health`, {
|
|
19602
|
+
signal: AbortSignal.timeout(1000)
|
|
19603
|
+
});
|
|
19604
|
+
return response.ok;
|
|
19605
|
+
} catch {
|
|
19606
|
+
return false;
|
|
19607
|
+
}
|
|
19608
|
+
}
|
|
19609
|
+
async function waitForHealthy(targetHttpUrl, timeoutMs) {
|
|
19610
|
+
const deadline = Date.now() + timeoutMs;
|
|
19611
|
+
while (Date.now() < deadline) {
|
|
19612
|
+
if (await isHealthy(targetHttpUrl)) {
|
|
19613
|
+
return true;
|
|
19614
|
+
}
|
|
19615
|
+
await new Promise((resolveTimer) => setTimeout(resolveTimer, 100));
|
|
19616
|
+
}
|
|
19617
|
+
return false;
|
|
19618
|
+
}
|
|
19619
|
+
function resolveRuntimeEntry() {
|
|
19620
|
+
const selfDir = dirname13(fileURLToPath8(import.meta.url));
|
|
19621
|
+
const sourceEntry = resolve10(selfDir, "terminal-relay-node.ts");
|
|
19622
|
+
const bundledEntry = resolve10(selfDir, "openscout-terminal-relay.mjs");
|
|
19623
|
+
const sourceBundle = resolve10(selfDir, "../dist/openscout-terminal-relay.mjs");
|
|
19624
|
+
if (existsSync14(sourceEntry)) {
|
|
19625
|
+
const build = spawnSync2("bun", [
|
|
19626
|
+
"build",
|
|
19627
|
+
sourceEntry,
|
|
19628
|
+
"--target=node",
|
|
19629
|
+
"--format=esm",
|
|
19630
|
+
"--outfile",
|
|
19631
|
+
sourceBundle,
|
|
19632
|
+
"--external",
|
|
19633
|
+
"node-pty"
|
|
19634
|
+
], {
|
|
19635
|
+
cwd: resolve10(selfDir, ".."),
|
|
19636
|
+
stdio: "inherit"
|
|
19637
|
+
});
|
|
19638
|
+
if ((build.status ?? 1) !== 0) {
|
|
19639
|
+
throw new Error("Failed to build terminal relay bundle");
|
|
19640
|
+
}
|
|
19641
|
+
return sourceBundle;
|
|
19642
|
+
}
|
|
19643
|
+
if (existsSync14(bundledEntry)) {
|
|
19644
|
+
return bundledEntry;
|
|
19645
|
+
}
|
|
19646
|
+
throw new Error("Could not locate the terminal relay runtime entry");
|
|
19647
|
+
}
|
|
19648
|
+
async function startManagedTerminalRelay(args) {
|
|
19649
|
+
const relayPort = relayPortForWebPort(args.webPort);
|
|
19650
|
+
const bindHost = relayHostForWebHost(args.hostname);
|
|
19651
|
+
const loopbackHost = relayLoopbackHost(bindHost);
|
|
19652
|
+
const targetHttpUrl = `http://${loopbackHost}:${relayPort}`;
|
|
19653
|
+
const targetWebSocketUrl = `ws://${loopbackHost}:${relayPort}`;
|
|
19654
|
+
if (await isHealthy(targetHttpUrl)) {
|
|
19655
|
+
return {
|
|
19656
|
+
healthcheck: () => isHealthy(targetHttpUrl),
|
|
19657
|
+
queueCommand: async (command) => {
|
|
19658
|
+
const response = await fetch(`${targetHttpUrl}/api/terminal/run`, {
|
|
19659
|
+
method: "POST",
|
|
19660
|
+
headers: { "Content-Type": "application/json" },
|
|
19661
|
+
body: JSON.stringify({ command })
|
|
19662
|
+
});
|
|
19663
|
+
if (!response.ok) {
|
|
19664
|
+
throw new Error("Terminal relay rejected queued command");
|
|
19665
|
+
}
|
|
19666
|
+
},
|
|
19667
|
+
shutdown() {},
|
|
19668
|
+
targetHttpUrl,
|
|
19669
|
+
targetWebSocketUrl
|
|
19670
|
+
};
|
|
19671
|
+
}
|
|
19672
|
+
const runtimeEntry = resolveRuntimeEntry();
|
|
19673
|
+
const child = spawn5("node", [runtimeEntry], {
|
|
19674
|
+
cwd: resolve10(dirname13(runtimeEntry), ".."),
|
|
19675
|
+
env: {
|
|
19676
|
+
...process.env,
|
|
19677
|
+
OPENSCOUT_WEB_TERMINAL_RELAY_HOST: bindHost,
|
|
19678
|
+
OPENSCOUT_WEB_TERMINAL_RELAY_PORT: String(relayPort)
|
|
19679
|
+
},
|
|
19680
|
+
stdio: "inherit"
|
|
19681
|
+
});
|
|
19682
|
+
let spawnError = null;
|
|
19683
|
+
child.once("error", (error) => {
|
|
19684
|
+
spawnError = error;
|
|
19685
|
+
});
|
|
19686
|
+
child.on("exit", (code, signal) => {
|
|
19687
|
+
if (signal === "SIGTERM" || signal === "SIGINT") {
|
|
19688
|
+
return;
|
|
19689
|
+
}
|
|
19690
|
+
console.error(`[relay] Managed terminal relay exited unexpectedly (code: ${code ?? "null"}, signal: ${signal ?? "none"})`);
|
|
19691
|
+
});
|
|
19692
|
+
const ready = await waitForHealthy(targetHttpUrl, 5000);
|
|
19693
|
+
if (!ready) {
|
|
19694
|
+
if (!child.killed) {
|
|
19695
|
+
child.kill("SIGTERM");
|
|
19696
|
+
}
|
|
19697
|
+
if (spawnError) {
|
|
19698
|
+
throw spawnError;
|
|
19699
|
+
}
|
|
19700
|
+
throw new Error(`Terminal relay did not become healthy at ${targetHttpUrl}`);
|
|
19701
|
+
}
|
|
19702
|
+
return {
|
|
19703
|
+
healthcheck: () => isHealthy(targetHttpUrl),
|
|
19704
|
+
queueCommand: async (command) => {
|
|
19705
|
+
const response = await fetch(`${targetHttpUrl}/api/terminal/run`, {
|
|
19706
|
+
method: "POST",
|
|
19707
|
+
headers: { "Content-Type": "application/json" },
|
|
19708
|
+
body: JSON.stringify({ command })
|
|
19709
|
+
});
|
|
19710
|
+
if (!response.ok) {
|
|
19711
|
+
throw new Error("Terminal relay rejected queued command");
|
|
19712
|
+
}
|
|
19713
|
+
},
|
|
19714
|
+
shutdown() {
|
|
19715
|
+
terminateChild(child);
|
|
19716
|
+
},
|
|
19717
|
+
targetHttpUrl,
|
|
19718
|
+
targetWebSocketUrl
|
|
19719
|
+
};
|
|
19720
|
+
}
|
|
19721
|
+
function terminateChild(child) {
|
|
19722
|
+
if (child.killed) {
|
|
19723
|
+
return;
|
|
19724
|
+
}
|
|
19725
|
+
child.kill("SIGTERM");
|
|
19726
|
+
}
|
|
19727
|
+
|
|
21890
19728
|
// packages/web/server/index.ts
|
|
21891
19729
|
var port = Number(process.env.OPENSCOUT_WEB_PORT ?? process.env.SCOUT_WEB_PORT ?? "3200");
|
|
21892
19730
|
var hostname2 = process.env.OPENSCOUT_WEB_HOST?.trim() || process.env.SCOUT_WEB_HOST?.trim() || "127.0.0.1";
|
|
@@ -21896,13 +19734,13 @@ function resolveStaticRoot2() {
|
|
|
21896
19734
|
if (process.env.OPENSCOUT_WEB_STATIC_ROOT?.trim()) {
|
|
21897
19735
|
return process.env.OPENSCOUT_WEB_STATIC_ROOT.trim();
|
|
21898
19736
|
}
|
|
21899
|
-
const selfDir =
|
|
21900
|
-
const siblingClientRoot =
|
|
21901
|
-
if (existsSync15(
|
|
19737
|
+
const selfDir = dirname14(fileURLToPath9(import.meta.url));
|
|
19738
|
+
const siblingClientRoot = join20(selfDir, "client");
|
|
19739
|
+
if (existsSync15(join20(siblingClientRoot, "index.html"))) {
|
|
21902
19740
|
return siblingClientRoot;
|
|
21903
19741
|
}
|
|
21904
|
-
const sourceDistClientRoot =
|
|
21905
|
-
if (existsSync15(
|
|
19742
|
+
const sourceDistClientRoot = resolve11(selfDir, "../dist/client");
|
|
19743
|
+
if (existsSync15(join20(sourceDistClientRoot, "index.html"))) {
|
|
21906
19744
|
return sourceDistClientRoot;
|
|
21907
19745
|
}
|
|
21908
19746
|
return;
|
|
@@ -21911,26 +19749,47 @@ var staticRoot = resolveStaticRoot2();
|
|
|
21911
19749
|
var viteDevUrl = process.env.OPENSCOUT_WEB_VITE_URL?.trim() || undefined;
|
|
21912
19750
|
var useViteProxy = Boolean(viteDevUrl) || !staticRoot;
|
|
21913
19751
|
var idleTimeoutSeconds = Number.parseInt(process.env.OPENSCOUT_WEB_IDLE_TIMEOUT_SECONDS?.trim() || (useViteProxy ? "180" : "30"), 10);
|
|
19752
|
+
var terminalRelay = null;
|
|
19753
|
+
try {
|
|
19754
|
+
terminalRelay = await startManagedTerminalRelay({
|
|
19755
|
+
hostname: hostname2,
|
|
19756
|
+
webPort: port
|
|
19757
|
+
});
|
|
19758
|
+
} catch (error) {
|
|
19759
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19760
|
+
console.warn(`[scout] Terminal relay unavailable: ${message}`);
|
|
19761
|
+
}
|
|
21914
19762
|
var { app, warmupCaches } = await createOpenScoutWebServer({
|
|
21915
19763
|
currentDirectory,
|
|
21916
19764
|
shellStateCacheTtlMs,
|
|
21917
19765
|
assetMode: useViteProxy ? "vite-proxy" : "static",
|
|
21918
19766
|
viteDevUrl,
|
|
21919
|
-
staticRoot
|
|
19767
|
+
staticRoot,
|
|
19768
|
+
runTerminalCommand: terminalRelay?.queueCommand
|
|
21920
19769
|
});
|
|
21921
19770
|
var honoFetch = app.fetch;
|
|
19771
|
+
var relayWebSocket = terminalRelay ? createRelayWebSocketProxy(terminalRelay.targetWebSocketUrl) : {
|
|
19772
|
+
open(ws) {
|
|
19773
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
19774
|
+
ws.close(1013, "Terminal relay unavailable");
|
|
19775
|
+
}
|
|
19776
|
+
},
|
|
19777
|
+
message() {},
|
|
19778
|
+
close() {}
|
|
19779
|
+
};
|
|
21922
19780
|
var server = Bun.serve({
|
|
21923
19781
|
port,
|
|
21924
19782
|
hostname: hostname2,
|
|
21925
19783
|
idleTimeout: idleTimeoutSeconds,
|
|
21926
|
-
fetch(req, server2) {
|
|
19784
|
+
async fetch(req, server2) {
|
|
21927
19785
|
const url = new URL(req.url);
|
|
21928
19786
|
if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
|
|
21929
|
-
const ok = server2.upgrade(req, { data: {
|
|
19787
|
+
const ok = server2.upgrade(req, { data: { upstream: null, pending: [] } });
|
|
21930
19788
|
return ok ? undefined : new Response("WebSocket upgrade failed", { status: 500 });
|
|
21931
19789
|
}
|
|
21932
19790
|
if (req.method === "GET" && url.pathname === "/health") {
|
|
21933
|
-
|
|
19791
|
+
const relayOk = terminalRelay ? await terminalRelay.healthcheck() : false;
|
|
19792
|
+
return Response.json({ ok: relayOk, relay: relayOk ? "up" : "down" }, { status: relayOk ? 200 : 503 });
|
|
21934
19793
|
}
|
|
21935
19794
|
if (req.method === "POST" && (url.pathname === "/api/upload" || url.pathname === "/api/relay/upload")) {
|
|
21936
19795
|
return handleRelayUpload(req);
|
|
@@ -21941,8 +19800,8 @@ var server = Bun.serve({
|
|
|
21941
19800
|
});
|
|
21942
19801
|
var shutdown = () => {
|
|
21943
19802
|
console.log(`
|
|
21944
|
-
[scout] Shutting down relay
|
|
21945
|
-
|
|
19803
|
+
[scout] Shutting down terminal relay...`);
|
|
19804
|
+
terminalRelay?.shutdown();
|
|
21946
19805
|
server.stop();
|
|
21947
19806
|
process.exit(0);
|
|
21948
19807
|
};
|