@schoolai/shipyard 3.13.0 → 3.14.0-rc.20260616.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-7MVFHLYV.js → chunk-3KE2VDKA.js} +2 -2
- package/dist/{chunk-MR6HRO7R.js → chunk-WGS7ZW6N.js} +3 -3
- package/dist/electron-utility.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{serve-RW42I4NC.js → serve-25I4ML7R.js} +1578 -1210
- package/dist/{serve-RW42I4NC.js.map → serve-25I4ML7R.js.map} +1 -1
- package/dist/{start-7GIHIERV.js → start-O2DXLW23.js} +4 -4
- package/package.json +1 -1
- /package/dist/{chunk-7MVFHLYV.js.map → chunk-3KE2VDKA.js.map} +0 -0
- /package/dist/{chunk-MR6HRO7R.js.map → chunk-WGS7ZW6N.js.map} +0 -0
- /package/dist/{start-7GIHIERV.js.map → start-O2DXLW23.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
guardedSubscribe,
|
|
13
13
|
makeInitialAttemptState,
|
|
14
14
|
shutdownFileWatcherGuard
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-3KE2VDKA.js";
|
|
16
16
|
import {
|
|
17
17
|
TRAILER_SCHEMA_VERSION,
|
|
18
18
|
appendTrailerToMessage,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
} from "./chunk-TFWDJCUJ.js";
|
|
46
46
|
import {
|
|
47
47
|
getDaemonVersion
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-WGS7ZW6N.js";
|
|
49
49
|
import {
|
|
50
50
|
collectPriorCrashDetail,
|
|
51
51
|
getRecentWasmPanicMessages
|
|
@@ -482,7 +482,7 @@ import "./chunk-2H7UOFLK.js";
|
|
|
482
482
|
// src/services/serve.ts
|
|
483
483
|
import { mkdir as mkdir44, realpath as realpath3 } from "fs/promises";
|
|
484
484
|
import { homedir as homedir15 } from "os";
|
|
485
|
-
import { join as
|
|
485
|
+
import { join as join87 } from "path";
|
|
486
486
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
487
487
|
|
|
488
488
|
// ../../node_modules/.pnpm/@levischuck+tiny-cbor@0.3.2/node_modules/@levischuck/tiny-cbor/esm/cbor/cbor_internal.js
|
|
@@ -5764,7 +5764,7 @@ function nanoid(size = 21) {
|
|
|
5764
5764
|
}
|
|
5765
5765
|
|
|
5766
5766
|
// src/services/bootstrap/signaling.ts
|
|
5767
|
-
var DAEMON_NPM_VERSION = true ? "3.
|
|
5767
|
+
var DAEMON_NPM_VERSION = true ? "3.14.0" : "unknown";
|
|
5768
5768
|
function createDaemonSignaling(config) {
|
|
5769
5769
|
const agentId = config.agentId ?? nanoid();
|
|
5770
5770
|
function send(msg) {
|
|
@@ -6526,6 +6526,583 @@ async function createCapabilityWatcher(deps) {
|
|
|
6526
6526
|
};
|
|
6527
6527
|
}
|
|
6528
6528
|
|
|
6529
|
+
// src/services/collab/crdt-permissions.ts
|
|
6530
|
+
function isPersonalPeer(entry) {
|
|
6531
|
+
return entry?.source === "personal";
|
|
6532
|
+
}
|
|
6533
|
+
function findEntry(registry, peer) {
|
|
6534
|
+
const direct = registry.getEntry(peer.peerId);
|
|
6535
|
+
if (direct) return direct;
|
|
6536
|
+
if (peer.peerType === "user") {
|
|
6537
|
+
return registry.findBySource("personal");
|
|
6538
|
+
}
|
|
6539
|
+
return void 0;
|
|
6540
|
+
}
|
|
6541
|
+
var COLLAB_VISIBLE_PREFIXES = {
|
|
6542
|
+
owner: /* @__PURE__ */ new Set(["plan", "canvas"]),
|
|
6543
|
+
"collaborator-full": /* @__PURE__ */ new Set(["plan", "canvas"]),
|
|
6544
|
+
"collaborator-review": /* @__PURE__ */ new Set(["plan", "canvas"]),
|
|
6545
|
+
viewer: /* @__PURE__ */ new Set([])
|
|
6546
|
+
};
|
|
6547
|
+
var COLLAB_MUTABLE_PREFIXES = {
|
|
6548
|
+
owner: /* @__PURE__ */ new Set(["plan", "canvas"]),
|
|
6549
|
+
"collaborator-full": /* @__PURE__ */ new Set(["plan", "canvas"]),
|
|
6550
|
+
"collaborator-review": /* @__PURE__ */ new Set(["plan"]),
|
|
6551
|
+
viewer: /* @__PURE__ */ new Set([])
|
|
6552
|
+
};
|
|
6553
|
+
function buildCollabPermissions(registry) {
|
|
6554
|
+
function isAllowed(_doc, peer) {
|
|
6555
|
+
if (peer.channelKind === "storage") return true;
|
|
6556
|
+
if (peer.peerType === "service") return true;
|
|
6557
|
+
const entry = findEntry(registry, peer);
|
|
6558
|
+
if (!entry) return false;
|
|
6559
|
+
return isPersonalPeer(entry);
|
|
6560
|
+
}
|
|
6561
|
+
return {
|
|
6562
|
+
visibility: isAllowed,
|
|
6563
|
+
mutability: isAllowed,
|
|
6564
|
+
creation(docId, peer) {
|
|
6565
|
+
const colonIdx = docId.indexOf(":");
|
|
6566
|
+
if (colonIdx > 0 && OBSOLETE_PREFIXES.has(docId.slice(0, colonIdx))) return false;
|
|
6567
|
+
if (peer.channelKind === "storage") return true;
|
|
6568
|
+
if (peer.peerType === "service") return true;
|
|
6569
|
+
const entry = findEntry(registry, peer);
|
|
6570
|
+
if (!entry) return false;
|
|
6571
|
+
return isPersonalPeer(entry);
|
|
6572
|
+
},
|
|
6573
|
+
deletion(_doc, _peer) {
|
|
6574
|
+
return false;
|
|
6575
|
+
}
|
|
6576
|
+
};
|
|
6577
|
+
}
|
|
6578
|
+
function isCollabPrefixVisible(role, prefix) {
|
|
6579
|
+
return COLLAB_VISIBLE_PREFIXES[role].has(prefix);
|
|
6580
|
+
}
|
|
6581
|
+
function isCollabPrefixMutable(role, prefix) {
|
|
6582
|
+
return COLLAB_MUTABLE_PREFIXES[role].has(prefix);
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6585
|
+
// src/services/collab/collab-repo-manager.ts
|
|
6586
|
+
var BRIDGE_EPHEMERAL_HOPS = 1;
|
|
6587
|
+
var DAEMON_IDENTITY = { name: "shipyard-daemon", type: "service" };
|
|
6588
|
+
function restoreEphemeralHops(message) {
|
|
6589
|
+
switch (message.type) {
|
|
6590
|
+
case "channel/ephemeral":
|
|
6591
|
+
if (message.hopsRemaining >= BRIDGE_EPHEMERAL_HOPS) return message;
|
|
6592
|
+
return { ...message, hopsRemaining: BRIDGE_EPHEMERAL_HOPS };
|
|
6593
|
+
case "channel/batch": {
|
|
6594
|
+
let changed = false;
|
|
6595
|
+
const nextMessages = message.messages.map((inner) => {
|
|
6596
|
+
if (inner.type !== "channel/ephemeral") return inner;
|
|
6597
|
+
if (inner.hopsRemaining >= BRIDGE_EPHEMERAL_HOPS) return inner;
|
|
6598
|
+
changed = true;
|
|
6599
|
+
return { ...inner, hopsRemaining: BRIDGE_EPHEMERAL_HOPS };
|
|
6600
|
+
});
|
|
6601
|
+
if (!changed) return message;
|
|
6602
|
+
return { ...message, messages: nextMessages };
|
|
6603
|
+
}
|
|
6604
|
+
case "channel/establish-request":
|
|
6605
|
+
case "channel/establish-response":
|
|
6606
|
+
case "channel/sync-request":
|
|
6607
|
+
case "channel/sync-response":
|
|
6608
|
+
case "channel/update":
|
|
6609
|
+
case "channel/directory-request":
|
|
6610
|
+
case "channel/directory-response":
|
|
6611
|
+
case "channel/new-doc":
|
|
6612
|
+
case "channel/delete-request":
|
|
6613
|
+
case "channel/delete-response":
|
|
6614
|
+
return message;
|
|
6615
|
+
default: {
|
|
6616
|
+
const _exhaustive = message;
|
|
6617
|
+
void _exhaustive;
|
|
6618
|
+
return message;
|
|
6619
|
+
}
|
|
6620
|
+
}
|
|
6621
|
+
}
|
|
6622
|
+
var HopRestoringBridgeAdapter = class extends BridgeAdapter {
|
|
6623
|
+
generate(context) {
|
|
6624
|
+
const channel = super.generate(context);
|
|
6625
|
+
const originalSend = channel.send;
|
|
6626
|
+
return {
|
|
6627
|
+
...channel,
|
|
6628
|
+
send: (msg) => originalSend(restoreEphemeralHops(msg))
|
|
6629
|
+
};
|
|
6630
|
+
}
|
|
6631
|
+
};
|
|
6632
|
+
function planCollabRoomResources(taskId, roomId, epochs) {
|
|
6633
|
+
return {
|
|
6634
|
+
docIds: [buildPlanDocId(taskId, epochs.plan), buildCanvasDocId(taskId, epochs.canvas)],
|
|
6635
|
+
personalBridgeType: `bridge-personal-${roomId}`,
|
|
6636
|
+
collabBridgeType: `bridge-collab-${roomId}`
|
|
6637
|
+
};
|
|
6638
|
+
}
|
|
6639
|
+
function buildCollabRepoPermissions(taskId, peerRoleRegistry) {
|
|
6640
|
+
return {
|
|
6641
|
+
visibility(doc, peer) {
|
|
6642
|
+
if (peer.channelKind === "storage") return true;
|
|
6643
|
+
if (peer.peerType === "service") return true;
|
|
6644
|
+
const parsed = parseDocumentId(doc.id);
|
|
6645
|
+
if (!parsed) return false;
|
|
6646
|
+
if (parsed.key !== taskId) return false;
|
|
6647
|
+
const entry = peerRoleRegistry.getEntry(peer.peerId);
|
|
6648
|
+
if (!entry) return false;
|
|
6649
|
+
return isCollabPrefixVisible(entry.role, parsed.prefix);
|
|
6650
|
+
},
|
|
6651
|
+
mutability(doc, peer) {
|
|
6652
|
+
if (peer.channelKind === "storage") return true;
|
|
6653
|
+
if (peer.peerType === "service") return true;
|
|
6654
|
+
const parsed = parseDocumentId(doc.id);
|
|
6655
|
+
if (!parsed) return false;
|
|
6656
|
+
if (parsed.key !== taskId) return false;
|
|
6657
|
+
const entry = peerRoleRegistry.getEntry(peer.peerId);
|
|
6658
|
+
if (!entry) return false;
|
|
6659
|
+
return isCollabPrefixMutable(entry.role, parsed.prefix);
|
|
6660
|
+
},
|
|
6661
|
+
creation(_docId, _peer) {
|
|
6662
|
+
return false;
|
|
6663
|
+
},
|
|
6664
|
+
deletion(_doc, _peer) {
|
|
6665
|
+
return false;
|
|
6666
|
+
}
|
|
6667
|
+
};
|
|
6668
|
+
}
|
|
6669
|
+
function createCollabRepo(personalRepo, taskId, roomId, epochs, peerRoleRegistry) {
|
|
6670
|
+
const resources = planCollabRoomResources(taskId, roomId, epochs);
|
|
6671
|
+
const bridge = new Bridge();
|
|
6672
|
+
const personalBridgeAdapter = new HopRestoringBridgeAdapter({
|
|
6673
|
+
adapterType: resources.personalBridgeType,
|
|
6674
|
+
bridge
|
|
6675
|
+
});
|
|
6676
|
+
const collabBridgeAdapter = new HopRestoringBridgeAdapter({
|
|
6677
|
+
adapterType: resources.collabBridgeType,
|
|
6678
|
+
bridge
|
|
6679
|
+
});
|
|
6680
|
+
const collabWebrtcAdapter = new WebRtcDataChannelAdapter();
|
|
6681
|
+
const collabRepo = new Repo({
|
|
6682
|
+
identity: { ...DAEMON_IDENTITY, name: `collab-room-${roomId}` },
|
|
6683
|
+
adapters: [collabBridgeAdapter, collabWebrtcAdapter],
|
|
6684
|
+
permissions: buildCollabRepoPermissions(taskId, peerRoleRegistry)
|
|
6685
|
+
});
|
|
6686
|
+
personalRepo.addAdapter(personalBridgeAdapter);
|
|
6687
|
+
return {
|
|
6688
|
+
repo: collabRepo,
|
|
6689
|
+
webrtcAdapter: collabWebrtcAdapter,
|
|
6690
|
+
async destroy() {
|
|
6691
|
+
personalRepo.removeAdapter(personalBridgeAdapter.adapterId);
|
|
6692
|
+
await collabRepo.shutdown();
|
|
6693
|
+
}
|
|
6694
|
+
};
|
|
6695
|
+
}
|
|
6696
|
+
|
|
6697
|
+
// src/services/collab/collab-room-manager.ts
|
|
6698
|
+
function assertNever2(x2) {
|
|
6699
|
+
throw new Error(`Unhandled message type: ${JSON.stringify(x2)}`);
|
|
6700
|
+
}
|
|
6701
|
+
function namespacePeerId(roomId, remoteUserId) {
|
|
6702
|
+
return `collab:${roomId}:${remoteUserId}`;
|
|
6703
|
+
}
|
|
6704
|
+
function stripCollabRoomPrefix(roomId, namespacedId) {
|
|
6705
|
+
const roomPrefix = `collab:${roomId}:`;
|
|
6706
|
+
if (!namespacedId.startsWith(roomPrefix)) {
|
|
6707
|
+
throw new Error(
|
|
6708
|
+
`Expected collab-namespaced peer ID with prefix "${roomPrefix}", got "${namespacedId}"`
|
|
6709
|
+
);
|
|
6710
|
+
}
|
|
6711
|
+
const browserMarker = ":browser:";
|
|
6712
|
+
const browserIdx = namespacedId.lastIndexOf(browserMarker);
|
|
6713
|
+
if (browserIdx === -1) {
|
|
6714
|
+
throw new Error(`Expected collab peer ID to contain "${browserMarker}", got "${namespacedId}"`);
|
|
6715
|
+
}
|
|
6716
|
+
return namespacedId.slice(browserIdx + browserMarker.length);
|
|
6717
|
+
}
|
|
6718
|
+
function bumpAndNotify(room, deps) {
|
|
6719
|
+
room.generation += 1;
|
|
6720
|
+
deps.onRoomStateChanged(room.taskId);
|
|
6721
|
+
}
|
|
6722
|
+
function handleParticipantsList(room, participants, deps) {
|
|
6723
|
+
if (!room.myUserId) return;
|
|
6724
|
+
room.participants = participants.map((p2) => ({
|
|
6725
|
+
userId: p2.userId,
|
|
6726
|
+
username: p2.username,
|
|
6727
|
+
avatarUrl: p2.avatarUrl,
|
|
6728
|
+
role: p2.role,
|
|
6729
|
+
connectionId: p2.connectionId,
|
|
6730
|
+
isHub: p2.isHub
|
|
6731
|
+
}));
|
|
6732
|
+
room.connectionIdByUser = new Map(participants.map((p2) => [p2.userId, p2.connectionId]));
|
|
6733
|
+
bumpAndNotify(room, deps);
|
|
6734
|
+
for (const participant of participants) {
|
|
6735
|
+
if (participant.userId === room.myUserId) continue;
|
|
6736
|
+
if (room.knownPeers.has(participant.userId)) continue;
|
|
6737
|
+
registerCollabParticipant(
|
|
6738
|
+
room,
|
|
6739
|
+
participant.userId,
|
|
6740
|
+
participant.role,
|
|
6741
|
+
participant.username,
|
|
6742
|
+
deps
|
|
6743
|
+
);
|
|
6744
|
+
offerOrEnqueueParticipant(room, participant.userId, deps);
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
function handleParticipantJoined(room, participant, deps) {
|
|
6748
|
+
if (!room.myUserId) return;
|
|
6749
|
+
if (participant.userId === room.myUserId) return;
|
|
6750
|
+
room.connectionIdByUser.set(participant.userId, participant.connectionId);
|
|
6751
|
+
if (room.knownPeers.has(participant.userId)) return;
|
|
6752
|
+
room.participants = [
|
|
6753
|
+
...room.participants,
|
|
6754
|
+
{
|
|
6755
|
+
userId: participant.userId,
|
|
6756
|
+
username: participant.username,
|
|
6757
|
+
avatarUrl: participant.avatarUrl,
|
|
6758
|
+
role: participant.role,
|
|
6759
|
+
connectionId: participant.connectionId,
|
|
6760
|
+
isHub: participant.isHub
|
|
6761
|
+
}
|
|
6762
|
+
];
|
|
6763
|
+
bumpAndNotify(room, deps);
|
|
6764
|
+
registerCollabParticipant(room, participant.userId, participant.role, participant.username, deps);
|
|
6765
|
+
offerOrEnqueueParticipant(room, participant.userId, deps);
|
|
6766
|
+
}
|
|
6767
|
+
function handleParticipantLeft(room, msg, deps) {
|
|
6768
|
+
const { userId, connectionId } = msg;
|
|
6769
|
+
const tracked = room.connectionIdByUser.get(userId);
|
|
6770
|
+
if (tracked !== void 0 && tracked !== connectionId) {
|
|
6771
|
+
deps.log({
|
|
6772
|
+
event: "participant_left_stale_dropped",
|
|
6773
|
+
roomId: room.roomId,
|
|
6774
|
+
userId,
|
|
6775
|
+
incomingConnectionId: connectionId,
|
|
6776
|
+
trackedConnectionId: tracked
|
|
6777
|
+
});
|
|
6778
|
+
return;
|
|
6779
|
+
}
|
|
6780
|
+
room.knownPeers.delete(userId);
|
|
6781
|
+
room.pendingOfferUserIds.delete(userId);
|
|
6782
|
+
room.participants = room.participants.filter((p2) => p2.userId !== userId);
|
|
6783
|
+
room.connectionIdByUser.delete(userId);
|
|
6784
|
+
bumpAndNotify(room, deps);
|
|
6785
|
+
const peerId = namespacePeerId(room.roomId, userId);
|
|
6786
|
+
deps.peerRoleRegistry.unregisterPeer(peerId);
|
|
6787
|
+
room.peerManager.closePeer(peerId);
|
|
6788
|
+
deps.log({ event: "collab_participant_left", roomId: room.roomId, userId });
|
|
6789
|
+
}
|
|
6790
|
+
function handleWebrtcSignaling(room, type, targetUserId, generationId, payload, deps) {
|
|
6791
|
+
const peerId = namespacePeerId(room.roomId, targetUserId);
|
|
6792
|
+
const actions = {
|
|
6793
|
+
// eslint-disable-next-line no-restricted-syntax -- SDP is opaque z.unknown() from signaling
|
|
6794
|
+
offer: () => room.peerManager.handleOffer(peerId, payload, generationId),
|
|
6795
|
+
// eslint-disable-next-line no-restricted-syntax -- SDP is opaque z.unknown() from signaling
|
|
6796
|
+
answer: () => room.peerManager.handleAnswer(peerId, payload, generationId),
|
|
6797
|
+
// eslint-disable-next-line no-restricted-syntax -- ICE candidate is opaque z.unknown() from signaling
|
|
6798
|
+
ice: () => room.peerManager.handleIce(peerId, payload, generationId)
|
|
6799
|
+
};
|
|
6800
|
+
actions[type]().catch((err3) => {
|
|
6801
|
+
deps.log({
|
|
6802
|
+
event: `collab_${type}_failed`,
|
|
6803
|
+
roomId: room.roomId,
|
|
6804
|
+
generationId,
|
|
6805
|
+
error: String(err3)
|
|
6806
|
+
});
|
|
6807
|
+
});
|
|
6808
|
+
}
|
|
6809
|
+
function handleCollabRoomMessage(room, msg, deps) {
|
|
6810
|
+
switch (msg.type) {
|
|
6811
|
+
case "authenticated":
|
|
6812
|
+
room.myUserId = msg.userId;
|
|
6813
|
+
deps.log({ event: "collab_authenticated", roomId: room.roomId, userId: msg.userId });
|
|
6814
|
+
break;
|
|
6815
|
+
case "participants-list":
|
|
6816
|
+
handleParticipantsList(room, msg.participants, deps);
|
|
6817
|
+
break;
|
|
6818
|
+
case "participant-joined":
|
|
6819
|
+
handleParticipantJoined(room, msg.participant, deps);
|
|
6820
|
+
break;
|
|
6821
|
+
case "participant-left":
|
|
6822
|
+
handleParticipantLeft(room, { userId: msg.userId, connectionId: msg.connectionId }, deps);
|
|
6823
|
+
break;
|
|
6824
|
+
case "webrtc-offer":
|
|
6825
|
+
handleWebrtcSignaling(room, "offer", msg.targetUserId, msg.generationId, msg.offer, deps);
|
|
6826
|
+
break;
|
|
6827
|
+
case "webrtc-answer":
|
|
6828
|
+
handleWebrtcSignaling(room, "answer", msg.targetUserId, msg.generationId, msg.answer, deps);
|
|
6829
|
+
break;
|
|
6830
|
+
case "webrtc-ice":
|
|
6831
|
+
handleWebrtcSignaling(room, "ice", msg.targetUserId, msg.generationId, msg.candidate, deps);
|
|
6832
|
+
break;
|
|
6833
|
+
case "ice-servers":
|
|
6834
|
+
applyIceServers(room, msg.iceServers, deps);
|
|
6835
|
+
break;
|
|
6836
|
+
case "error":
|
|
6837
|
+
deps.log({ event: "collab_room_error", roomId: room.roomId, message: msg.message });
|
|
6838
|
+
break;
|
|
6839
|
+
default:
|
|
6840
|
+
assertNever2(msg);
|
|
6841
|
+
}
|
|
6842
|
+
}
|
|
6843
|
+
function registerCollabParticipant(room, userId, role, displayName, deps) {
|
|
6844
|
+
const peerId = namespacePeerId(room.roomId, userId);
|
|
6845
|
+
deps.peerRoleRegistry.registerCollabPeer(peerId, role, userId, displayName, room.taskId);
|
|
6846
|
+
}
|
|
6847
|
+
function initiateOfferToParticipant(room, remoteUserId, deps) {
|
|
6848
|
+
if (!room.myUserId) return;
|
|
6849
|
+
room.knownPeers.add(remoteUserId);
|
|
6850
|
+
deps.log({ event: "collab_initiating_offer", roomId: room.roomId, target: remoteUserId });
|
|
6851
|
+
const peerId = namespacePeerId(room.roomId, remoteUserId);
|
|
6852
|
+
room.peerManager.initiateOffer(peerId).catch((err3) => {
|
|
6853
|
+
deps.log({ event: "collab_offer_initiate_failed", roomId: room.roomId, error: String(err3) });
|
|
6854
|
+
});
|
|
6855
|
+
}
|
|
6856
|
+
function offerOrEnqueueParticipant(room, remoteUserId, deps) {
|
|
6857
|
+
if (room.iceServersReady) {
|
|
6858
|
+
initiateOfferToParticipant(room, remoteUserId, deps);
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
room.pendingOfferUserIds.add(remoteUserId);
|
|
6862
|
+
deps.log({ event: "collab_offer_deferred_no_ice", roomId: room.roomId, target: remoteUserId });
|
|
6863
|
+
}
|
|
6864
|
+
function drainPendingOffers(room, deps) {
|
|
6865
|
+
if (room.pendingOfferUserIds.size === 0) return;
|
|
6866
|
+
const pending = [...room.pendingOfferUserIds];
|
|
6867
|
+
room.pendingOfferUserIds.clear();
|
|
6868
|
+
for (const userId of pending) {
|
|
6869
|
+
if (userId === room.myUserId) continue;
|
|
6870
|
+
if (room.knownPeers.has(userId)) continue;
|
|
6871
|
+
initiateOfferToParticipant(room, userId, deps);
|
|
6872
|
+
}
|
|
6873
|
+
}
|
|
6874
|
+
function applyIceServers(room, iceServers, deps) {
|
|
6875
|
+
room.iceServers = iceServers;
|
|
6876
|
+
room.iceServersReady = true;
|
|
6877
|
+
room.peerManager.updateIceServers(iceServers);
|
|
6878
|
+
deps.log({ event: "collab_ice_servers", roomId: room.roomId, count: iceServers.length });
|
|
6879
|
+
drainPendingOffers(room, deps);
|
|
6880
|
+
}
|
|
6881
|
+
function resetCollabRoomForReconnect(room, deps) {
|
|
6882
|
+
room.peerManager.destroy();
|
|
6883
|
+
const adapter = room.collabRepoHandle?.webrtcAdapter;
|
|
6884
|
+
if (adapter) {
|
|
6885
|
+
room.peerManager = deps.createPeerManagerForRoom(
|
|
6886
|
+
room.roomId,
|
|
6887
|
+
room.connection,
|
|
6888
|
+
room.taskId,
|
|
6889
|
+
adapter
|
|
6890
|
+
);
|
|
6891
|
+
if (room.iceServers) {
|
|
6892
|
+
room.peerManager.updateIceServers(room.iceServers);
|
|
6893
|
+
}
|
|
6894
|
+
} else {
|
|
6895
|
+
deps.log({ event: "collab_reconnect_no_adapter", roomId: room.roomId });
|
|
6896
|
+
}
|
|
6897
|
+
room.myUserId = null;
|
|
6898
|
+
room.knownPeers.clear();
|
|
6899
|
+
room.pendingOfferUserIds.clear();
|
|
6900
|
+
room.iceServersReady = adapter !== void 0 && room.iceServers !== null;
|
|
6901
|
+
}
|
|
6902
|
+
function shouldBroadcastRoomState(state) {
|
|
6903
|
+
if (state === null) return true;
|
|
6904
|
+
return state.ownerId !== "";
|
|
6905
|
+
}
|
|
6906
|
+
function createCollabRoomManager(deps) {
|
|
6907
|
+
const rooms = /* @__PURE__ */ new Map();
|
|
6908
|
+
async function leave(roomId, opts) {
|
|
6909
|
+
const room = rooms.get(roomId);
|
|
6910
|
+
if (!room) return;
|
|
6911
|
+
deps.log({ event: "collab_leaving", roomId });
|
|
6912
|
+
clearTimeout(room.expiryTimer);
|
|
6913
|
+
room.unsubMessage();
|
|
6914
|
+
room.unsubState();
|
|
6915
|
+
for (const userId of room.knownPeers) {
|
|
6916
|
+
deps.peerRoleRegistry.unregisterPeer(namespacePeerId(roomId, userId));
|
|
6917
|
+
}
|
|
6918
|
+
const destroyPromise = room.collabRepoHandle?.destroy();
|
|
6919
|
+
room.peerManager.destroy();
|
|
6920
|
+
room.connection.disconnect();
|
|
6921
|
+
const leftTaskId = room.taskId;
|
|
6922
|
+
rooms.delete(roomId);
|
|
6923
|
+
if (opts?.removePersisted !== false) {
|
|
6924
|
+
deps.collabHostingStore.remove(roomId).catch((err3) => {
|
|
6925
|
+
deps.log({
|
|
6926
|
+
event: "collab_hosting_remove_failed",
|
|
6927
|
+
roomId,
|
|
6928
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
6929
|
+
});
|
|
6930
|
+
});
|
|
6931
|
+
}
|
|
6932
|
+
deps.onRoomStateChanged(leftTaskId);
|
|
6933
|
+
if (destroyPromise) {
|
|
6934
|
+
await destroyPromise.catch((err3) => {
|
|
6935
|
+
deps.log({
|
|
6936
|
+
event: "collab_repo_destroy_failed",
|
|
6937
|
+
roomId,
|
|
6938
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
6939
|
+
});
|
|
6940
|
+
});
|
|
6941
|
+
}
|
|
6942
|
+
}
|
|
6943
|
+
return {
|
|
6944
|
+
joinRoom(config) {
|
|
6945
|
+
const { roomId, taskId, token, expiresAt, signalingBaseUrl, userToken, machineId } = config;
|
|
6946
|
+
const existing = rooms.get(roomId);
|
|
6947
|
+
if (existing) {
|
|
6948
|
+
const existingState = existing.connection.getState();
|
|
6949
|
+
if (existing.taskId === taskId && (existingState === "connected" || existingState === "connecting")) {
|
|
6950
|
+
deps.log({ event: "collab_join_dedup", roomId, state: existingState });
|
|
6951
|
+
return {
|
|
6952
|
+
roomId,
|
|
6953
|
+
taskId,
|
|
6954
|
+
async destroy() {
|
|
6955
|
+
await leave(roomId);
|
|
6956
|
+
}
|
|
6957
|
+
};
|
|
6958
|
+
}
|
|
6959
|
+
deps.log({ event: "collab_replacing", roomId });
|
|
6960
|
+
leave(roomId, { removePersisted: false }).catch((err3) => {
|
|
6961
|
+
deps.log({
|
|
6962
|
+
event: "collab_replace_leave_failed",
|
|
6963
|
+
roomId,
|
|
6964
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
6965
|
+
});
|
|
6966
|
+
});
|
|
6967
|
+
}
|
|
6968
|
+
const wsUrl = new URL(signalingBaseUrl);
|
|
6969
|
+
wsUrl.pathname = ROUTES.WS_COLLAB.replace(":roomId", roomId);
|
|
6970
|
+
wsUrl.searchParams.set("token", token);
|
|
6971
|
+
wsUrl.searchParams.set("userToken", userToken);
|
|
6972
|
+
wsUrl.searchParams.set("clientType", "agent");
|
|
6973
|
+
wsUrl.searchParams.set("machineId", machineId);
|
|
6974
|
+
const connection = new CollabRoomConnection({
|
|
6975
|
+
url: wsUrl.toString(),
|
|
6976
|
+
maxRetries: -1,
|
|
6977
|
+
initialDelayMs: 1e3,
|
|
6978
|
+
maxDelayMs: 3e4,
|
|
6979
|
+
backoffMultiplier: 2
|
|
6980
|
+
});
|
|
6981
|
+
const collabRepoHandle = createCollabRepo(
|
|
6982
|
+
deps.personalRepo,
|
|
6983
|
+
taskId,
|
|
6984
|
+
roomId,
|
|
6985
|
+
{ plan: deps.planEpoch, canvas: deps.canvasEpoch },
|
|
6986
|
+
deps.peerRoleRegistry
|
|
6987
|
+
);
|
|
6988
|
+
const peerManager = deps.createPeerManagerForRoom(
|
|
6989
|
+
roomId,
|
|
6990
|
+
connection,
|
|
6991
|
+
taskId,
|
|
6992
|
+
collabRepoHandle.webrtcAdapter
|
|
6993
|
+
);
|
|
6994
|
+
const handle = {
|
|
6995
|
+
roomId,
|
|
6996
|
+
taskId,
|
|
6997
|
+
async destroy() {
|
|
6998
|
+
await leave(roomId);
|
|
6999
|
+
}
|
|
7000
|
+
};
|
|
7001
|
+
const delayMs = expiresAt - Date.now();
|
|
7002
|
+
if (delayMs <= 0 || !Number.isFinite(delayMs)) {
|
|
7003
|
+
deps.log({ event: "collab_expired", roomId, expiresAt });
|
|
7004
|
+
peerManager.destroy();
|
|
7005
|
+
collabRepoHandle.destroy().catch((err3) => {
|
|
7006
|
+
deps.log({
|
|
7007
|
+
event: "collab_repo_destroy_failed",
|
|
7008
|
+
roomId,
|
|
7009
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
7010
|
+
});
|
|
7011
|
+
});
|
|
7012
|
+
return handle;
|
|
7013
|
+
}
|
|
7014
|
+
const safeDelayMs = Math.min(delayMs, 2147483647);
|
|
7015
|
+
const room = {
|
|
7016
|
+
roomId,
|
|
7017
|
+
taskId,
|
|
7018
|
+
connection,
|
|
7019
|
+
peerManager,
|
|
7020
|
+
collabRepoHandle,
|
|
7021
|
+
myUserId: null,
|
|
7022
|
+
knownPeers: /* @__PURE__ */ new Set(),
|
|
7023
|
+
participants: [],
|
|
7024
|
+
connectionIdByUser: /* @__PURE__ */ new Map(),
|
|
7025
|
+
iceServers: null,
|
|
7026
|
+
iceServersReady: false,
|
|
7027
|
+
pendingOfferUserIds: /* @__PURE__ */ new Set(),
|
|
7028
|
+
expiresAt,
|
|
7029
|
+
generation: 0,
|
|
7030
|
+
expiryTimer: setTimeout(() => {
|
|
7031
|
+
deps.log({ event: "collab_token_expired", roomId });
|
|
7032
|
+
leave(roomId).catch((err3) => {
|
|
7033
|
+
deps.log({
|
|
7034
|
+
event: "collab_expiry_leave_failed",
|
|
7035
|
+
roomId,
|
|
7036
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
7037
|
+
});
|
|
7038
|
+
});
|
|
7039
|
+
}, safeDelayMs),
|
|
7040
|
+
unsubMessage: () => {
|
|
7041
|
+
},
|
|
7042
|
+
unsubState: () => {
|
|
7043
|
+
}
|
|
7044
|
+
};
|
|
7045
|
+
rooms.set(roomId, room);
|
|
7046
|
+
deps.collabHostingStore.persist({ roomId, taskId, token, expiresAt }).catch((err3) => {
|
|
7047
|
+
deps.log({
|
|
7048
|
+
event: "collab_hosting_persist_failed",
|
|
7049
|
+
roomId,
|
|
7050
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
7051
|
+
});
|
|
7052
|
+
});
|
|
7053
|
+
room.unsubMessage = connection.onMessage((msg) => {
|
|
7054
|
+
handleCollabRoomMessage(room, msg, deps);
|
|
7055
|
+
});
|
|
7056
|
+
room.unsubState = connection.onStateChange((state) => {
|
|
7057
|
+
deps.log({ event: "collab_state_change", roomId, state });
|
|
7058
|
+
if (state === "disconnected" || state === "error") {
|
|
7059
|
+
resetCollabRoomForReconnect(room, deps);
|
|
7060
|
+
}
|
|
7061
|
+
});
|
|
7062
|
+
connection.connect();
|
|
7063
|
+
deps.log({ event: "collab_joining", roomId, taskId });
|
|
7064
|
+
bumpAndNotify(room, deps);
|
|
7065
|
+
return handle;
|
|
7066
|
+
},
|
|
7067
|
+
async leaveRoom(roomId) {
|
|
7068
|
+
await leave(roomId);
|
|
7069
|
+
},
|
|
7070
|
+
getParticipantsForTask(taskId) {
|
|
7071
|
+
for (const room of rooms.values()) {
|
|
7072
|
+
if (room.taskId === taskId) {
|
|
7073
|
+
return room.participants.filter((p2) => p2.userId !== room.myUserId).map((p2) => ({ name: p2.username, role: p2.role }));
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
return [];
|
|
7077
|
+
},
|
|
7078
|
+
listActiveTaskIds() {
|
|
7079
|
+
const ids = [];
|
|
7080
|
+
for (const room of rooms.values()) ids.push(room.taskId);
|
|
7081
|
+
return ids;
|
|
7082
|
+
},
|
|
7083
|
+
getStateForTask(taskId) {
|
|
7084
|
+
for (const room of rooms.values()) {
|
|
7085
|
+
if (room.taskId !== taskId) continue;
|
|
7086
|
+
const owner = room.participants.find((p2) => p2.role === "owner");
|
|
7087
|
+
return {
|
|
7088
|
+
roomId: room.roomId,
|
|
7089
|
+
taskId: room.taskId,
|
|
7090
|
+
ownerId: owner?.userId ?? "",
|
|
7091
|
+
participants: room.participants,
|
|
7092
|
+
expiresAt: room.expiresAt,
|
|
7093
|
+
generation: room.generation
|
|
7094
|
+
};
|
|
7095
|
+
}
|
|
7096
|
+
return null;
|
|
7097
|
+
},
|
|
7098
|
+
async destroy() {
|
|
7099
|
+
await Promise.all(
|
|
7100
|
+
[...rooms.keys()].map((roomId) => leave(roomId, { removePersisted: false }))
|
|
7101
|
+
);
|
|
7102
|
+
}
|
|
7103
|
+
};
|
|
7104
|
+
}
|
|
7105
|
+
|
|
6529
7106
|
// src/services/mcp/state-snapshot.ts
|
|
6530
7107
|
function computeTransportEnabled(server, live) {
|
|
6531
7108
|
if (live) return live.status !== "disabled";
|
|
@@ -6855,6 +7432,7 @@ function sendInitialCollabSnapshots(handler, deps) {
|
|
|
6855
7432
|
const mgr = deps.collabRoomManager;
|
|
6856
7433
|
for (const taskId of mgr.listActiveTaskIds()) {
|
|
6857
7434
|
const state = mgr.getStateForTask(taskId);
|
|
7435
|
+
if (!shouldBroadcastRoomState(state)) continue;
|
|
6858
7436
|
handler.sendControl({
|
|
6859
7437
|
type: "collab_room_state",
|
|
6860
7438
|
taskId,
|
|
@@ -12938,10 +13516,94 @@ function buildBrowserMetricsTelemetry(msg) {
|
|
|
12938
13516
|
eventLoopDriftP99Ms: m2.eventLoopDriftMs?.p99 ?? null,
|
|
12939
13517
|
longTaskTotalMs: m2.longTasks?.totalMs ?? null,
|
|
12940
13518
|
longTaskMaxMs: m2.longTasks?.maxMs ?? null,
|
|
13519
|
+
rtcSelectedCandidatePair: toTelemetryCandidatePair(m2.rtcSelectedCandidatePair ?? null),
|
|
13520
|
+
networkEffectiveType: toTelemetryNetworkEffectiveType(m2.network?.effectiveType ?? null),
|
|
12941
13521
|
controlChannelTotalBytes: m2.controlChannel?.totalBytes ?? null,
|
|
12942
13522
|
runtimeErrorCount: m2.runtimeErrors?.errorCount ?? null
|
|
12943
13523
|
};
|
|
12944
13524
|
}
|
|
13525
|
+
function toTelemetryCandidatePair(pair) {
|
|
13526
|
+
if (!pair) return null;
|
|
13527
|
+
return {
|
|
13528
|
+
localCandidateType: toTelemetryCandidateType(pair.localCandidateType),
|
|
13529
|
+
remoteCandidateType: toTelemetryCandidateType(pair.remoteCandidateType),
|
|
13530
|
+
localProtocol: toTelemetryProtocol(pair.localProtocol),
|
|
13531
|
+
remoteProtocol: toTelemetryProtocol(pair.remoteProtocol),
|
|
13532
|
+
protocol: formatCandidatePairProtocol(
|
|
13533
|
+
toTelemetryProtocol(pair.localProtocol),
|
|
13534
|
+
toTelemetryProtocol(pair.remoteProtocol)
|
|
13535
|
+
),
|
|
13536
|
+
relayProtocol: toTelemetryRelayProtocol(pair.relayProtocol),
|
|
13537
|
+
networkType: toTelemetryCandidateNetworkType(pair.networkType)
|
|
13538
|
+
};
|
|
13539
|
+
}
|
|
13540
|
+
function formatCandidatePairProtocol(localProtocol, remoteProtocol) {
|
|
13541
|
+
if (localProtocol && remoteProtocol) {
|
|
13542
|
+
return localProtocol === remoteProtocol ? localProtocol : `${localProtocol}/${remoteProtocol}`;
|
|
13543
|
+
}
|
|
13544
|
+
return localProtocol ?? remoteProtocol;
|
|
13545
|
+
}
|
|
13546
|
+
function toTelemetryCandidateType(value) {
|
|
13547
|
+
if (!value) return null;
|
|
13548
|
+
switch (value) {
|
|
13549
|
+
case "host":
|
|
13550
|
+
case "prflx":
|
|
13551
|
+
case "relay":
|
|
13552
|
+
case "srflx":
|
|
13553
|
+
return value;
|
|
13554
|
+
default:
|
|
13555
|
+
return "unknown";
|
|
13556
|
+
}
|
|
13557
|
+
}
|
|
13558
|
+
function toTelemetryProtocol(value) {
|
|
13559
|
+
if (!value) return null;
|
|
13560
|
+
switch (value) {
|
|
13561
|
+
case "tcp":
|
|
13562
|
+
case "udp":
|
|
13563
|
+
return value;
|
|
13564
|
+
default:
|
|
13565
|
+
return "unknown";
|
|
13566
|
+
}
|
|
13567
|
+
}
|
|
13568
|
+
function toTelemetryRelayProtocol(value) {
|
|
13569
|
+
if (!value) return null;
|
|
13570
|
+
switch (value) {
|
|
13571
|
+
case "tcp":
|
|
13572
|
+
case "tls":
|
|
13573
|
+
case "udp":
|
|
13574
|
+
return value;
|
|
13575
|
+
default:
|
|
13576
|
+
return "unknown";
|
|
13577
|
+
}
|
|
13578
|
+
}
|
|
13579
|
+
function toTelemetryNetworkEffectiveType(value) {
|
|
13580
|
+
if (!value) return null;
|
|
13581
|
+
switch (value) {
|
|
13582
|
+
case "2g":
|
|
13583
|
+
case "3g":
|
|
13584
|
+
case "4g":
|
|
13585
|
+
case "slow-2g":
|
|
13586
|
+
return value;
|
|
13587
|
+
default:
|
|
13588
|
+
return "unknown";
|
|
13589
|
+
}
|
|
13590
|
+
}
|
|
13591
|
+
function toTelemetryCandidateNetworkType(value) {
|
|
13592
|
+
if (!value) return null;
|
|
13593
|
+
switch (value) {
|
|
13594
|
+
case "bluetooth":
|
|
13595
|
+
case "cellular":
|
|
13596
|
+
case "ethernet":
|
|
13597
|
+
case "vpn":
|
|
13598
|
+
case "wifi":
|
|
13599
|
+
case "wimax":
|
|
13600
|
+
case "wlan":
|
|
13601
|
+
case "unknown":
|
|
13602
|
+
return value;
|
|
13603
|
+
default:
|
|
13604
|
+
return "unknown";
|
|
13605
|
+
}
|
|
13606
|
+
}
|
|
12945
13607
|
function buildBrowserMetricsBaseLogEntry(msg, controlPeerId, daemonTs) {
|
|
12946
13608
|
const m2 = msg.metrics;
|
|
12947
13609
|
return {
|
|
@@ -18505,6 +19167,15 @@ function sendAttachSnapshot(handler, daemon, deps, logAdapter) {
|
|
|
18505
19167
|
|
|
18506
19168
|
// src/shared/capabilities/anthropic-login.ts
|
|
18507
19169
|
import { spawn as spawn7 } from "child_process";
|
|
19170
|
+
var VERSION_INCOMPAT_MESSAGE = "Your installed Claude CLI is too old for this sign-in method. Update it (run `claude update`, or reinstall the Claude CLI) and try again.";
|
|
19171
|
+
var VERSION_INCOMPAT_PATTERN = /error: unknown (option|command)/i;
|
|
19172
|
+
function classifyLoginExit(exitCode, output) {
|
|
19173
|
+
if (exitCode === 0) return { kind: "success" };
|
|
19174
|
+
if (VERSION_INCOMPAT_PATTERN.test(output)) {
|
|
19175
|
+
return { kind: "version-incompatible", error: VERSION_INCOMPAT_MESSAGE };
|
|
19176
|
+
}
|
|
19177
|
+
return { kind: "failed", error: `Login failed (exit code ${exitCode})` };
|
|
19178
|
+
}
|
|
18508
19179
|
function buildLoginArgs(method) {
|
|
18509
19180
|
switch (method) {
|
|
18510
19181
|
case "claude-ai":
|
|
@@ -18517,6 +19188,63 @@ function buildLoginArgs(method) {
|
|
|
18517
19188
|
return ["auth", "login"];
|
|
18518
19189
|
}
|
|
18519
19190
|
}
|
|
19191
|
+
function resolveLoginBinary(log) {
|
|
19192
|
+
const resolved = resolveClaudeBinaryPath(log);
|
|
19193
|
+
return resolved ? { binary: resolved, usedPathFallback: false } : { binary: "claude", usedPathFallback: true };
|
|
19194
|
+
}
|
|
19195
|
+
function handleLoginSuccess(ctx) {
|
|
19196
|
+
const { requestId, method, sendStatus, onAuthRefreshed, log } = ctx;
|
|
19197
|
+
log({ event: "anthropic_login_completed", requestId });
|
|
19198
|
+
detectAnthropicAuth(method).then((result) => {
|
|
19199
|
+
if (result.kind === "preserved") {
|
|
19200
|
+
log({ event: "anthropic_auth_refresh_preserved", requestId, reason: result.reason });
|
|
19201
|
+
return;
|
|
19202
|
+
}
|
|
19203
|
+
onAuthRefreshed(result.auth);
|
|
19204
|
+
log({ event: "anthropic_auth_refreshed", requestId, authStatus: result.auth });
|
|
19205
|
+
}).catch((err3) => {
|
|
19206
|
+
log({
|
|
19207
|
+
event: "anthropic_auth_refresh_failed",
|
|
19208
|
+
requestId,
|
|
19209
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
19210
|
+
});
|
|
19211
|
+
}).finally(() => {
|
|
19212
|
+
sendStatus({
|
|
19213
|
+
type: "anthropic_login_status",
|
|
19214
|
+
requestId,
|
|
19215
|
+
status: "done",
|
|
19216
|
+
loginUrl: null,
|
|
19217
|
+
error: null
|
|
19218
|
+
});
|
|
19219
|
+
});
|
|
19220
|
+
}
|
|
19221
|
+
function handleLoginExit(exitCode, output, ctx) {
|
|
19222
|
+
const classification = classifyLoginExit(exitCode, output);
|
|
19223
|
+
switch (classification.kind) {
|
|
19224
|
+
case "success":
|
|
19225
|
+
handleLoginSuccess(ctx);
|
|
19226
|
+
return;
|
|
19227
|
+
case "version-incompatible":
|
|
19228
|
+
case "failed":
|
|
19229
|
+
ctx.log({
|
|
19230
|
+
event: "anthropic_login_failed",
|
|
19231
|
+
requestId: ctx.requestId,
|
|
19232
|
+
exitCode,
|
|
19233
|
+
output,
|
|
19234
|
+
reason: classification.kind
|
|
19235
|
+
});
|
|
19236
|
+
ctx.sendStatus({
|
|
19237
|
+
type: "anthropic_login_status",
|
|
19238
|
+
requestId: ctx.requestId,
|
|
19239
|
+
status: "error",
|
|
19240
|
+
loginUrl: null,
|
|
19241
|
+
error: classification.error
|
|
19242
|
+
});
|
|
19243
|
+
return;
|
|
19244
|
+
default:
|
|
19245
|
+
assertNever(classification);
|
|
19246
|
+
}
|
|
19247
|
+
}
|
|
18520
19248
|
function spawnAnthropicLogin(callbacks) {
|
|
18521
19249
|
const requestId = crypto.randomUUID();
|
|
18522
19250
|
const { sendStatus, onAuthRefreshed, log, method } = callbacks;
|
|
@@ -18529,7 +19257,9 @@ function spawnAnthropicLogin(callbacks) {
|
|
|
18529
19257
|
error: null
|
|
18530
19258
|
});
|
|
18531
19259
|
const args = buildLoginArgs(method);
|
|
18532
|
-
const
|
|
19260
|
+
const { binary, usedPathFallback } = resolveLoginBinary(log);
|
|
19261
|
+
log({ event: "anthropic_login_spawn", requestId, binaryPath: binary, usedPathFallback });
|
|
19262
|
+
const child = spawn7(binary, args, {
|
|
18533
19263
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18534
19264
|
});
|
|
18535
19265
|
let output = "";
|
|
@@ -18550,44 +19280,7 @@ function spawnAnthropicLogin(callbacks) {
|
|
|
18550
19280
|
child.stdout?.on("data", handleOutput);
|
|
18551
19281
|
child.stderr?.on("data", handleOutput);
|
|
18552
19282
|
child.on("exit", (exitCode) => {
|
|
18553
|
-
|
|
18554
|
-
log({ event: "anthropic_login_completed", requestId });
|
|
18555
|
-
detectAnthropicAuth(method).then((result) => {
|
|
18556
|
-
if (result.kind === "preserved") {
|
|
18557
|
-
log({
|
|
18558
|
-
event: "anthropic_auth_refresh_preserved",
|
|
18559
|
-
requestId,
|
|
18560
|
-
reason: result.reason
|
|
18561
|
-
});
|
|
18562
|
-
return;
|
|
18563
|
-
}
|
|
18564
|
-
onAuthRefreshed(result.auth);
|
|
18565
|
-
log({ event: "anthropic_auth_refreshed", requestId, authStatus: result.auth });
|
|
18566
|
-
}).catch((err3) => {
|
|
18567
|
-
log({
|
|
18568
|
-
event: "anthropic_auth_refresh_failed",
|
|
18569
|
-
requestId,
|
|
18570
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
18571
|
-
});
|
|
18572
|
-
}).finally(() => {
|
|
18573
|
-
sendStatus({
|
|
18574
|
-
type: "anthropic_login_status",
|
|
18575
|
-
requestId,
|
|
18576
|
-
status: "done",
|
|
18577
|
-
loginUrl: null,
|
|
18578
|
-
error: null
|
|
18579
|
-
});
|
|
18580
|
-
});
|
|
18581
|
-
} else {
|
|
18582
|
-
log({ event: "anthropic_login_failed", requestId, exitCode, output });
|
|
18583
|
-
sendStatus({
|
|
18584
|
-
type: "anthropic_login_status",
|
|
18585
|
-
requestId,
|
|
18586
|
-
status: "error",
|
|
18587
|
-
loginUrl: null,
|
|
18588
|
-
error: `Login failed (exit code ${exitCode})`
|
|
18589
|
-
});
|
|
18590
|
-
}
|
|
19283
|
+
handleLoginExit(exitCode, output, { requestId, method, sendStatus, onAuthRefreshed, log });
|
|
18591
19284
|
});
|
|
18592
19285
|
child.on("error", (err3) => {
|
|
18593
19286
|
log({ event: "anthropic_login_spawn_failed", requestId, error: err3.message });
|
|
@@ -18604,7 +19297,9 @@ function spawnAnthropicLogin(callbacks) {
|
|
|
18604
19297
|
function spawnAnthropicLogout(callbacks) {
|
|
18605
19298
|
const { onComplete, log } = callbacks;
|
|
18606
19299
|
log({ event: "anthropic_logout_requested" });
|
|
18607
|
-
const
|
|
19300
|
+
const { binary, usedPathFallback } = resolveLoginBinary(log);
|
|
19301
|
+
log({ event: "anthropic_logout_spawn", binaryPath: binary, usedPathFallback });
|
|
19302
|
+
const child = spawn7(binary, ["auth", "logout"], {
|
|
18608
19303
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18609
19304
|
});
|
|
18610
19305
|
child.on("exit", (exitCode) => {
|
|
@@ -18637,7 +19332,7 @@ function spawnAnthropicLogout(callbacks) {
|
|
|
18637
19332
|
}
|
|
18638
19333
|
|
|
18639
19334
|
// src/shared/capabilities/codex-account-event.ts
|
|
18640
|
-
function
|
|
19335
|
+
function assertNever3(x2) {
|
|
18641
19336
|
throw new Error(`Unhandled Codex AuthMode: ${JSON.stringify(x2)}`);
|
|
18642
19337
|
}
|
|
18643
19338
|
function decideCodexAuthFromAccountUpdated(authMode) {
|
|
@@ -18650,7 +19345,7 @@ function decideCodexAuthFromAccountUpdated(authMode) {
|
|
|
18650
19345
|
case "agentIdentity":
|
|
18651
19346
|
return { status: "authenticated", method: "chatgpt" };
|
|
18652
19347
|
default:
|
|
18653
|
-
return
|
|
19348
|
+
return assertNever3(authMode);
|
|
18654
19349
|
}
|
|
18655
19350
|
}
|
|
18656
19351
|
|
|
@@ -28525,60 +29220,306 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
28525
29220
|
});
|
|
28526
29221
|
}
|
|
28527
29222
|
|
|
28528
|
-
// src/services/collab/
|
|
28529
|
-
function
|
|
28530
|
-
|
|
29223
|
+
// src/services/collab/collab-hosting-rehydrate.ts
|
|
29224
|
+
function classifyPersistedRooms(records, now) {
|
|
29225
|
+
const plan = { rejoin: [], skip: [] };
|
|
29226
|
+
for (const record of records) {
|
|
29227
|
+
if (!record.token) {
|
|
29228
|
+
plan.skip.push({ record, reason: "missing-token" });
|
|
29229
|
+
} else if (record.expiresAt <= now) {
|
|
29230
|
+
plan.skip.push({ record, reason: "expired" });
|
|
29231
|
+
} else {
|
|
29232
|
+
plan.rejoin.push(record);
|
|
29233
|
+
}
|
|
29234
|
+
}
|
|
29235
|
+
return plan;
|
|
28531
29236
|
}
|
|
28532
|
-
function
|
|
28533
|
-
const
|
|
28534
|
-
if (
|
|
28535
|
-
|
|
28536
|
-
|
|
29237
|
+
async function rehydrateCollabRooms(deps) {
|
|
29238
|
+
const records = await deps.store.list();
|
|
29239
|
+
if (records.length === 0) return;
|
|
29240
|
+
const plan = classifyPersistedRooms(records, deps.now);
|
|
29241
|
+
for (const { record, reason } of plan.skip) {
|
|
29242
|
+
deps.log({ event: "collab_rehydrate_skip", roomId: record.roomId, reason });
|
|
29243
|
+
await deps.store.remove(record.roomId).catch((err3) => {
|
|
29244
|
+
deps.log({
|
|
29245
|
+
event: "collab_rehydrate_sweep_failed",
|
|
29246
|
+
roomId: record.roomId,
|
|
29247
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
29248
|
+
});
|
|
29249
|
+
});
|
|
29250
|
+
}
|
|
29251
|
+
for (const record of plan.rejoin) {
|
|
29252
|
+
try {
|
|
29253
|
+
deps.joinRoom({
|
|
29254
|
+
roomId: record.roomId,
|
|
29255
|
+
taskId: record.taskId,
|
|
29256
|
+
token: record.token,
|
|
29257
|
+
expiresAt: record.expiresAt,
|
|
29258
|
+
signalingBaseUrl: deps.signalingBaseUrl,
|
|
29259
|
+
userToken: deps.userToken,
|
|
29260
|
+
machineId: deps.machineId
|
|
29261
|
+
});
|
|
29262
|
+
deps.log({ event: "collab_rehydrate_rejoin", roomId: record.roomId, taskId: record.taskId });
|
|
29263
|
+
} catch (err3) {
|
|
29264
|
+
deps.log({
|
|
29265
|
+
event: "collab_rehydrate_rejoin_failed",
|
|
29266
|
+
roomId: record.roomId,
|
|
29267
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
29268
|
+
});
|
|
29269
|
+
}
|
|
28537
29270
|
}
|
|
28538
|
-
return void 0;
|
|
28539
29271
|
}
|
|
28540
|
-
|
|
28541
|
-
|
|
28542
|
-
|
|
28543
|
-
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
|
|
28547
|
-
|
|
28548
|
-
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
29272
|
+
function startCollabRehydrate(args) {
|
|
29273
|
+
const signalingBaseUrl = args.signalingBaseUrl;
|
|
29274
|
+
if (!signalingBaseUrl) return;
|
|
29275
|
+
void rehydrateCollabRooms({
|
|
29276
|
+
store: args.store,
|
|
29277
|
+
joinRoom: (config) => {
|
|
29278
|
+
args.collabRoomManager.joinRoom(config);
|
|
29279
|
+
},
|
|
29280
|
+
signalingBaseUrl,
|
|
29281
|
+
userToken: args.userToken,
|
|
29282
|
+
machineId: args.machineId,
|
|
29283
|
+
now: Date.now(),
|
|
29284
|
+
log: args.log
|
|
29285
|
+
}).catch((err3) => {
|
|
29286
|
+
args.log({
|
|
29287
|
+
event: "collab_rehydrate_failed",
|
|
29288
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
29289
|
+
});
|
|
29290
|
+
});
|
|
29291
|
+
}
|
|
29292
|
+
|
|
29293
|
+
// src/services/collab/collab-hosting-store.ts
|
|
29294
|
+
import { join as join34 } from "path";
|
|
29295
|
+
|
|
29296
|
+
// src/services/storage/json-document-store.ts
|
|
29297
|
+
import { mkdir as mkdir14, readFile as readFile20 } from "fs/promises";
|
|
29298
|
+
import { dirname as dirname15 } from "path";
|
|
29299
|
+
var WRITE_DEBOUNCE_MS = 50;
|
|
29300
|
+
function applyMutations(records, mutations) {
|
|
29301
|
+
for (const [id, mutation] of mutations) {
|
|
29302
|
+
if (mutation.kind === "delete") {
|
|
29303
|
+
delete records[id];
|
|
29304
|
+
} else {
|
|
29305
|
+
records[id] = mutation.data;
|
|
29306
|
+
}
|
|
29307
|
+
}
|
|
29308
|
+
}
|
|
29309
|
+
function resolveWaiters(waiters) {
|
|
29310
|
+
for (const waiter of waiters) waiter.resolve();
|
|
29311
|
+
}
|
|
29312
|
+
function rejectWaiters(waiters, error) {
|
|
29313
|
+
for (const waiter of waiters) waiter.reject(error);
|
|
29314
|
+
}
|
|
29315
|
+
function buildJsonDocumentStore(opts) {
|
|
29316
|
+
const { filePath, recordSchema, currentVersion, migrate, storeName, docType } = opts;
|
|
29317
|
+
let cache2 = null;
|
|
29318
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
29319
|
+
let writeQueue = Promise.resolve();
|
|
29320
|
+
const lockPath2 = `${filePath}.lock`;
|
|
29321
|
+
let pendingMutations = /* @__PURE__ */ new Map();
|
|
29322
|
+
let pendingFlushTimer = null;
|
|
29323
|
+
let pendingFlushWaiters = [];
|
|
29324
|
+
function notify(event) {
|
|
29325
|
+
for (const listener of listeners) {
|
|
29326
|
+
try {
|
|
29327
|
+
listener(event);
|
|
29328
|
+
} catch {
|
|
29329
|
+
}
|
|
29330
|
+
}
|
|
29331
|
+
}
|
|
29332
|
+
async function ensureDir() {
|
|
29333
|
+
await mkdir14(dirname15(filePath), { recursive: true });
|
|
29334
|
+
}
|
|
29335
|
+
async function readFromDisk() {
|
|
29336
|
+
try {
|
|
29337
|
+
const raw = await readFile20(filePath, "utf-8");
|
|
29338
|
+
return migrate(JSON.parse(raw));
|
|
29339
|
+
} catch (err3) {
|
|
29340
|
+
if (isEnoent(err3)) {
|
|
29341
|
+
return { schemaVersion: currentVersion, records: {} };
|
|
29342
|
+
}
|
|
29343
|
+
if (isCorruptionError(err3)) {
|
|
29344
|
+
const event = await quarantineCorruptFile({
|
|
29345
|
+
path: filePath,
|
|
29346
|
+
storeName,
|
|
29347
|
+
docType,
|
|
29348
|
+
error: err3,
|
|
29349
|
+
defaultsApplied: true,
|
|
29350
|
+
logger: opts.logger
|
|
29351
|
+
});
|
|
29352
|
+
opts.onCorrupt?.(event);
|
|
29353
|
+
return { schemaVersion: currentVersion, records: {} };
|
|
29354
|
+
}
|
|
29355
|
+
throw err3;
|
|
29356
|
+
}
|
|
29357
|
+
}
|
|
29358
|
+
async function readStore() {
|
|
29359
|
+
if (cache2) return cache2;
|
|
29360
|
+
cache2 = await readFromDisk();
|
|
29361
|
+
return cache2;
|
|
29362
|
+
}
|
|
29363
|
+
async function flushPendingWrite() {
|
|
29364
|
+
pendingFlushTimer = null;
|
|
29365
|
+
const waiters = pendingFlushWaiters;
|
|
29366
|
+
pendingFlushWaiters = [];
|
|
29367
|
+
const mutations = pendingMutations;
|
|
29368
|
+
pendingMutations = /* @__PURE__ */ new Map();
|
|
29369
|
+
if (mutations.size === 0) {
|
|
29370
|
+
resolveWaiters(waiters);
|
|
29371
|
+
return;
|
|
29372
|
+
}
|
|
29373
|
+
try {
|
|
29374
|
+
await commitMutations(mutations);
|
|
29375
|
+
resolveWaiters(waiters);
|
|
29376
|
+
} catch (err3) {
|
|
29377
|
+
requeueMutations(mutations);
|
|
29378
|
+
if (pendingMutations.size > 0 && pendingFlushTimer === null) {
|
|
29379
|
+
pendingFlushTimer = setTimeout(() => {
|
|
29380
|
+
void flushPendingWrite();
|
|
29381
|
+
}, WRITE_DEBOUNCE_MS);
|
|
29382
|
+
}
|
|
29383
|
+
rejectWaiters(waiters, err3);
|
|
29384
|
+
}
|
|
29385
|
+
}
|
|
29386
|
+
async function commitMutations(mutations) {
|
|
29387
|
+
await withFileLock(lockPath2, async () => {
|
|
29388
|
+
const disk = await readFromDisk();
|
|
29389
|
+
applyMutations(disk.records, mutations);
|
|
29390
|
+
await ensureDir();
|
|
29391
|
+
await atomicWriteFile(filePath, JSON.stringify(disk));
|
|
29392
|
+
applyMutations(disk.records, pendingMutations);
|
|
29393
|
+
cache2 = disk;
|
|
29394
|
+
});
|
|
29395
|
+
}
|
|
29396
|
+
function requeueMutations(mutations) {
|
|
29397
|
+
for (const [id, mutation] of mutations) {
|
|
29398
|
+
if (!pendingMutations.has(id)) pendingMutations.set(id, mutation);
|
|
29399
|
+
}
|
|
29400
|
+
}
|
|
29401
|
+
function scheduleFlush() {
|
|
29402
|
+
return new Promise((resolve12, reject) => {
|
|
29403
|
+
pendingFlushWaiters.push({ resolve: resolve12, reject });
|
|
29404
|
+
if (pendingFlushTimer) return;
|
|
29405
|
+
pendingFlushTimer = setTimeout(() => {
|
|
29406
|
+
void flushPendingWrite();
|
|
29407
|
+
}, WRITE_DEBOUNCE_MS);
|
|
29408
|
+
});
|
|
29409
|
+
}
|
|
29410
|
+
function enqueueWrite(op) {
|
|
29411
|
+
let resultFlush;
|
|
29412
|
+
const next = writeQueue.then(
|
|
29413
|
+
async () => {
|
|
29414
|
+
const out = await op();
|
|
29415
|
+
resultFlush = out?.flush;
|
|
29416
|
+
},
|
|
29417
|
+
async () => {
|
|
29418
|
+
const out = await op();
|
|
29419
|
+
resultFlush = out?.flush;
|
|
29420
|
+
}
|
|
29421
|
+
);
|
|
29422
|
+
writeQueue = next;
|
|
29423
|
+
return next.then(() => resultFlush);
|
|
28559
29424
|
}
|
|
28560
29425
|
return {
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28564
|
-
|
|
28565
|
-
if (colonIdx > 0 && OBSOLETE_PREFIXES.has(docId.slice(0, colonIdx))) return false;
|
|
28566
|
-
if (peer.channelKind === "storage") return true;
|
|
28567
|
-
if (peer.peerType === "service") return true;
|
|
28568
|
-
const entry = findEntry(registry, peer);
|
|
28569
|
-
if (!entry) return false;
|
|
28570
|
-
return isPersonalPeer(entry);
|
|
29426
|
+
async get(id) {
|
|
29427
|
+
const store = await readStore();
|
|
29428
|
+
const record = store.records[id];
|
|
29429
|
+
return record ?? null;
|
|
28571
29430
|
},
|
|
28572
|
-
|
|
28573
|
-
|
|
29431
|
+
async set(id, data) {
|
|
29432
|
+
await enqueueWrite(async () => {
|
|
29433
|
+
const store = await readStore();
|
|
29434
|
+
store.records[id] = data;
|
|
29435
|
+
pendingMutations.set(id, { kind: "set", data });
|
|
29436
|
+
const flush = scheduleFlush();
|
|
29437
|
+
notify({ kind: "set", id, data });
|
|
29438
|
+
return { flush };
|
|
29439
|
+
});
|
|
29440
|
+
},
|
|
29441
|
+
async update(id, fn) {
|
|
29442
|
+
await enqueueWrite(async () => {
|
|
29443
|
+
const store = await readStore();
|
|
29444
|
+
const existing = store.records[id];
|
|
29445
|
+
if (!existing) return void 0;
|
|
29446
|
+
const parsed = recordSchema.parse(existing);
|
|
29447
|
+
const updated = fn(parsed);
|
|
29448
|
+
if (updated === parsed) return void 0;
|
|
29449
|
+
store.records[id] = updated;
|
|
29450
|
+
pendingMutations.set(id, { kind: "set", data: updated });
|
|
29451
|
+
const flush = scheduleFlush();
|
|
29452
|
+
notify({ kind: "set", id, data: updated });
|
|
29453
|
+
return { flush };
|
|
29454
|
+
});
|
|
29455
|
+
},
|
|
29456
|
+
async delete(id) {
|
|
29457
|
+
await enqueueWrite(async () => {
|
|
29458
|
+
const store = await readStore();
|
|
29459
|
+
if (!(id in store.records)) return void 0;
|
|
29460
|
+
delete store.records[id];
|
|
29461
|
+
pendingMutations.set(id, { kind: "delete" });
|
|
29462
|
+
const flush = scheduleFlush();
|
|
29463
|
+
notify({ kind: "delete", id });
|
|
29464
|
+
return { flush };
|
|
29465
|
+
});
|
|
29466
|
+
},
|
|
29467
|
+
async list() {
|
|
29468
|
+
const store = await readStore();
|
|
29469
|
+
return { ...store.records };
|
|
29470
|
+
},
|
|
29471
|
+
subscribe(listener) {
|
|
29472
|
+
listeners.add(listener);
|
|
29473
|
+
return () => {
|
|
29474
|
+
listeners.delete(listener);
|
|
29475
|
+
};
|
|
28574
29476
|
}
|
|
28575
29477
|
};
|
|
28576
29478
|
}
|
|
28577
|
-
|
|
28578
|
-
|
|
29479
|
+
|
|
29480
|
+
// src/services/collab/collab-hosting-store.ts
|
|
29481
|
+
var CollabHostingRecordSchema = external_exports.object({
|
|
29482
|
+
roomId: external_exports.string().min(1),
|
|
29483
|
+
taskId: external_exports.string().min(1),
|
|
29484
|
+
token: external_exports.string().min(1),
|
|
29485
|
+
expiresAt: external_exports.number()
|
|
29486
|
+
});
|
|
29487
|
+
var COLLAB_HOSTING_STORE_VERSION = 1;
|
|
29488
|
+
var StoreEnvelopeSchema = external_exports.object({
|
|
29489
|
+
records: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
29490
|
+
});
|
|
29491
|
+
function migrateCollabHostingStore(raw) {
|
|
29492
|
+
const records = {};
|
|
29493
|
+
const envelope = StoreEnvelopeSchema.safeParse(raw);
|
|
29494
|
+
if (!envelope.success || !envelope.data.records) {
|
|
29495
|
+
return { schemaVersion: COLLAB_HOSTING_STORE_VERSION, records };
|
|
29496
|
+
}
|
|
29497
|
+
for (const [id, value] of Object.entries(envelope.data.records)) {
|
|
29498
|
+
const parsed = CollabHostingRecordSchema.safeParse(value);
|
|
29499
|
+
if (parsed.success) records[id] = parsed.data;
|
|
29500
|
+
}
|
|
29501
|
+
return { schemaVersion: COLLAB_HOSTING_STORE_VERSION, records };
|
|
28579
29502
|
}
|
|
28580
|
-
function
|
|
28581
|
-
|
|
29503
|
+
function buildCollabHostingStore(dataDir) {
|
|
29504
|
+
const store = buildJsonDocumentStore({
|
|
29505
|
+
storeName: "collab-hosting",
|
|
29506
|
+
docType: "generic",
|
|
29507
|
+
filePath: join34(dataDir, "collab-rooms.json"),
|
|
29508
|
+
recordSchema: CollabHostingRecordSchema,
|
|
29509
|
+
currentVersion: COLLAB_HOSTING_STORE_VERSION,
|
|
29510
|
+
migrate: migrateCollabHostingStore
|
|
29511
|
+
});
|
|
29512
|
+
return {
|
|
29513
|
+
async persist(record) {
|
|
29514
|
+
await store.set(record.roomId, record);
|
|
29515
|
+
},
|
|
29516
|
+
async remove(roomId) {
|
|
29517
|
+
await store.delete(roomId);
|
|
29518
|
+
},
|
|
29519
|
+
async list() {
|
|
29520
|
+
return Object.values(await store.list());
|
|
29521
|
+
}
|
|
29522
|
+
};
|
|
28582
29523
|
}
|
|
28583
29524
|
|
|
28584
29525
|
// src/services/collab/peer-role-registry.ts
|
|
@@ -28651,7 +29592,7 @@ function createPeerRoleRegistry() {
|
|
|
28651
29592
|
|
|
28652
29593
|
// src/services/epoch-pruning.ts
|
|
28653
29594
|
import { readdir as readdir13, rm as rm7 } from "fs/promises";
|
|
28654
|
-
import { join as
|
|
29595
|
+
import { join as join35 } from "path";
|
|
28655
29596
|
var LEGACY_PREFIXES = [
|
|
28656
29597
|
"task-meta",
|
|
28657
29598
|
"task-conv",
|
|
@@ -28691,7 +29632,7 @@ async function pruneOldEpochData(dataDir, log) {
|
|
|
28691
29632
|
}
|
|
28692
29633
|
if (!shouldPrune(decoded)) continue;
|
|
28693
29634
|
removals.push(
|
|
28694
|
-
rm7(
|
|
29635
|
+
rm7(join35(dataDir, entry.name), { recursive: true }).then(() => {
|
|
28695
29636
|
pruned++;
|
|
28696
29637
|
}).catch((err3) => {
|
|
28697
29638
|
log({
|
|
@@ -28710,7 +29651,7 @@ async function pruneOldEpochData(dataDir, log) {
|
|
|
28710
29651
|
|
|
28711
29652
|
// src/services/file-watcher.ts
|
|
28712
29653
|
import { existsSync as existsSync4 } from "fs";
|
|
28713
|
-
import { dirname as
|
|
29654
|
+
import { dirname as dirname16, relative as relative6 } from "path";
|
|
28714
29655
|
var IGNORE_GLOBS = [
|
|
28715
29656
|
".git",
|
|
28716
29657
|
"node_modules",
|
|
@@ -28844,7 +29785,7 @@ async function startWatcher(cwd, entry, log) {
|
|
|
28844
29785
|
event: "file_watcher_pool_create_failed",
|
|
28845
29786
|
cwd,
|
|
28846
29787
|
cwdExists: pathExistsForLog(cwd),
|
|
28847
|
-
parentExists: pathExistsForLog(
|
|
29788
|
+
parentExists: pathExistsForLog(dirname16(cwd)),
|
|
28848
29789
|
error: err3 instanceof Error ? err3.message : String(err3)
|
|
28849
29790
|
});
|
|
28850
29791
|
}
|
|
@@ -29310,7 +30251,7 @@ function routeDataChannel(label) {
|
|
|
29310
30251
|
}
|
|
29311
30252
|
return { kind: "unknown", label };
|
|
29312
30253
|
}
|
|
29313
|
-
function
|
|
30254
|
+
function assertNever4(x2) {
|
|
29314
30255
|
throw new Error(`Unhandled channel route: ${JSON.stringify(x2)}`);
|
|
29315
30256
|
}
|
|
29316
30257
|
function machineIdToPeerId(machineId) {
|
|
@@ -29463,7 +30404,14 @@ function installControlGuard(dc, label, machineId, log, logAdapter, dcBufferedSa
|
|
|
29463
30404
|
var HANDSHAKE_TIMEOUT_MS = 3e4;
|
|
29464
30405
|
function noopLogAdapter(_entry) {
|
|
29465
30406
|
}
|
|
30407
|
+
function decideStaleSignal(entry, incoming) {
|
|
30408
|
+
if (!entry) return { action: "drop" };
|
|
30409
|
+
if (entry.generationId === incoming.generationId) return { action: "apply" };
|
|
30410
|
+
if (entry.lastState === "new") return { action: "reoffer", generationId: entry.generationId };
|
|
30411
|
+
return { action: "drop" };
|
|
30412
|
+
}
|
|
29466
30413
|
var DISCONNECT_WATCHDOG_MS = 5e3;
|
|
30414
|
+
var REOFFER_THROTTLE_MS = 1e3;
|
|
29467
30415
|
function createPeerManager(config) {
|
|
29468
30416
|
const peers = /* @__PURE__ */ new Map();
|
|
29469
30417
|
const pendingCreates = /* @__PURE__ */ new Map();
|
|
@@ -29594,6 +30542,46 @@ function createPeerManager(config) {
|
|
|
29594
30542
|
}
|
|
29595
30543
|
};
|
|
29596
30544
|
}
|
|
30545
|
+
function logStaleAnswer(fromMachineId, currentGenerationId, staleGenerationId) {
|
|
30546
|
+
config.logAdapter?.({
|
|
30547
|
+
event: "webrtc_answer_stale",
|
|
30548
|
+
fromMachineId,
|
|
30549
|
+
currentGenerationId,
|
|
30550
|
+
staleGenerationId
|
|
30551
|
+
});
|
|
30552
|
+
config.log.debug(
|
|
30553
|
+
{ fromMachineId, currentGenerationId, staleGenerationId },
|
|
30554
|
+
"Dropping stale WebRTC answer"
|
|
30555
|
+
);
|
|
30556
|
+
}
|
|
30557
|
+
function logStaleIce(fromMachineId, currentGenerationId, staleGenerationId) {
|
|
30558
|
+
config.logAdapter?.({
|
|
30559
|
+
event: "webrtc_ice_candidate_stale",
|
|
30560
|
+
fromMachineId,
|
|
30561
|
+
currentGenerationId,
|
|
30562
|
+
staleGenerationId
|
|
30563
|
+
});
|
|
30564
|
+
config.log.debug(
|
|
30565
|
+
{ fromMachineId, currentGenerationId, staleGenerationId },
|
|
30566
|
+
"Dropping stale ICE candidate"
|
|
30567
|
+
);
|
|
30568
|
+
}
|
|
30569
|
+
function reemitOfferForDesync(machineId, entry, trigger) {
|
|
30570
|
+
if (!entry.offerSdp || !config.onOffer) return false;
|
|
30571
|
+
const now = Date.now();
|
|
30572
|
+
if (entry.lastReofferAt !== void 0 && now - entry.lastReofferAt < REOFFER_THROTTLE_MS) {
|
|
30573
|
+
return true;
|
|
30574
|
+
}
|
|
30575
|
+
entry.lastReofferAt = now;
|
|
30576
|
+
config.onOffer(machineId, { type: "offer", sdp: entry.offerSdp }, entry.generationId);
|
|
30577
|
+
config.logAdapter?.({
|
|
30578
|
+
event: "webrtc_reoffer_sent",
|
|
30579
|
+
machineId,
|
|
30580
|
+
generationId: entry.generationId,
|
|
30581
|
+
trigger
|
|
30582
|
+
});
|
|
30583
|
+
return true;
|
|
30584
|
+
}
|
|
29597
30585
|
function tearDownPeer(machineId, reason) {
|
|
29598
30586
|
const peerId = machineIdToPeerId(machineId);
|
|
29599
30587
|
disposeLoroGuard(peerId);
|
|
@@ -29770,7 +30758,7 @@ function createPeerManager(config) {
|
|
|
29770
30758
|
return;
|
|
29771
30759
|
}
|
|
29772
30760
|
default:
|
|
29773
|
-
|
|
30761
|
+
assertNever4(route);
|
|
29774
30762
|
}
|
|
29775
30763
|
} catch (err3) {
|
|
29776
30764
|
config.log.warn(
|
|
@@ -29960,7 +30948,8 @@ function createPeerManager(config) {
|
|
|
29960
30948
|
generationId,
|
|
29961
30949
|
createdAt: now,
|
|
29962
30950
|
lastState: "new",
|
|
29963
|
-
lastStateAt: now
|
|
30951
|
+
lastStateAt: now,
|
|
30952
|
+
offerSdp: offer.sdp
|
|
29964
30953
|
};
|
|
29965
30954
|
if (pendingCreates.get(targetMachineId) !== promise) {
|
|
29966
30955
|
disposeLoroGuard(machineIdToPeerId(targetMachineId));
|
|
@@ -30007,44 +30996,44 @@ function createPeerManager(config) {
|
|
|
30007
30996
|
async handleAnswer(fromMachineId, answer, generationId) {
|
|
30008
30997
|
const entry = await resolveLiveEntry(fromMachineId, "answer", generationId);
|
|
30009
30998
|
if (!entry) return;
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30015
|
-
|
|
30016
|
-
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
|
|
30020
|
-
|
|
30021
|
-
|
|
30022
|
-
|
|
30023
|
-
|
|
30024
|
-
|
|
30025
|
-
|
|
30999
|
+
const decision = decideStaleSignal(
|
|
31000
|
+
{ generationId: entry.generationId, lastState: entry.lastState },
|
|
31001
|
+
{ kind: "answer", generationId }
|
|
31002
|
+
);
|
|
31003
|
+
switch (decision.action) {
|
|
31004
|
+
case "apply":
|
|
31005
|
+
break;
|
|
31006
|
+
case "reoffer":
|
|
31007
|
+
if (reemitOfferForDesync(fromMachineId, entry, "answer")) return;
|
|
31008
|
+
logStaleAnswer(fromMachineId, entry.generationId, generationId);
|
|
31009
|
+
return;
|
|
31010
|
+
case "drop":
|
|
31011
|
+
logStaleAnswer(fromMachineId, entry.generationId, generationId);
|
|
31012
|
+
return;
|
|
31013
|
+
default:
|
|
31014
|
+
return assertNever4(decision);
|
|
30026
31015
|
}
|
|
30027
31016
|
await entry.pc.setRemoteDescription(answer);
|
|
30028
31017
|
},
|
|
30029
31018
|
async handleIce(fromMachineId, candidate, generationId) {
|
|
30030
31019
|
const entry = await resolveLiveEntry(fromMachineId, "ice", generationId);
|
|
30031
31020
|
if (!entry) return;
|
|
30032
|
-
|
|
30033
|
-
|
|
30034
|
-
|
|
30035
|
-
|
|
30036
|
-
|
|
30037
|
-
|
|
30038
|
-
|
|
30039
|
-
|
|
30040
|
-
|
|
30041
|
-
|
|
30042
|
-
|
|
30043
|
-
|
|
30044
|
-
|
|
30045
|
-
|
|
30046
|
-
|
|
30047
|
-
|
|
31021
|
+
const decision = decideStaleSignal(
|
|
31022
|
+
{ generationId: entry.generationId, lastState: entry.lastState },
|
|
31023
|
+
{ kind: "ice", generationId }
|
|
31024
|
+
);
|
|
31025
|
+
switch (decision.action) {
|
|
31026
|
+
case "apply":
|
|
31027
|
+
break;
|
|
31028
|
+
case "reoffer":
|
|
31029
|
+
if (reemitOfferForDesync(fromMachineId, entry, "ice")) return;
|
|
31030
|
+
logStaleIce(fromMachineId, entry.generationId, generationId);
|
|
31031
|
+
return;
|
|
31032
|
+
case "drop":
|
|
31033
|
+
logStaleIce(fromMachineId, entry.generationId, generationId);
|
|
31034
|
+
return;
|
|
31035
|
+
default:
|
|
31036
|
+
return assertNever4(decision);
|
|
30048
31037
|
}
|
|
30049
31038
|
try {
|
|
30050
31039
|
await entry.pc.addIceCandidate(candidate);
|
|
@@ -30428,7 +31417,7 @@ function createLocalDirectPeer(config) {
|
|
|
30428
31417
|
return;
|
|
30429
31418
|
}
|
|
30430
31419
|
default:
|
|
30431
|
-
|
|
31420
|
+
assertNever5(route);
|
|
30432
31421
|
}
|
|
30433
31422
|
}
|
|
30434
31423
|
function handleOpen(label) {
|
|
@@ -30534,7 +31523,7 @@ function createLocalDirectPeer(config) {
|
|
|
30534
31523
|
);
|
|
30535
31524
|
return;
|
|
30536
31525
|
default:
|
|
30537
|
-
return
|
|
31526
|
+
return assertNever5(frame);
|
|
30538
31527
|
}
|
|
30539
31528
|
},
|
|
30540
31529
|
dispose(reason) {
|
|
@@ -30555,7 +31544,7 @@ function createLocalDirectPeer(config) {
|
|
|
30555
31544
|
}
|
|
30556
31545
|
};
|
|
30557
31546
|
}
|
|
30558
|
-
function
|
|
31547
|
+
function assertNever5(x2) {
|
|
30559
31548
|
throw new Error(`Unhandled local-direct route: ${JSON.stringify(x2)}`);
|
|
30560
31549
|
}
|
|
30561
31550
|
|
|
@@ -30932,8 +31921,8 @@ function onConnection(ws, deps, peers) {
|
|
|
30932
31921
|
}
|
|
30933
31922
|
|
|
30934
31923
|
// src/services/local-direct/local-direct-token.ts
|
|
30935
|
-
import { chmod as chmod2, mkdir as
|
|
30936
|
-
import { dirname as
|
|
31924
|
+
import { chmod as chmod2, mkdir as mkdir15, rename as rename12, rm as rm8, writeFile as writeFile14 } from "fs/promises";
|
|
31925
|
+
import { dirname as dirname17, join as join36 } from "path";
|
|
30937
31926
|
var ADVERTISEMENT_FILE = "local-direct.json";
|
|
30938
31927
|
var ADVERTISEMENT_MODE = 384;
|
|
30939
31928
|
var ADVERTISEMENT_DIR_MODE = 448;
|
|
@@ -30942,12 +31931,12 @@ function generateLocalDirectToken() {
|
|
|
30942
31931
|
return nanoid(TOKEN_LENGTH);
|
|
30943
31932
|
}
|
|
30944
31933
|
function advertisementPath(shipyardHome) {
|
|
30945
|
-
return
|
|
31934
|
+
return join36(shipyardHome, "data", ADVERTISEMENT_FILE);
|
|
30946
31935
|
}
|
|
30947
31936
|
async function writeAdvertisement(shipyardHome, ad) {
|
|
30948
31937
|
const target = advertisementPath(shipyardHome);
|
|
30949
|
-
const dir =
|
|
30950
|
-
await
|
|
31938
|
+
const dir = dirname17(target);
|
|
31939
|
+
await mkdir15(dir, { recursive: true, mode: ADVERTISEMENT_DIR_MODE });
|
|
30951
31940
|
try {
|
|
30952
31941
|
await chmod2(dir, ADVERTISEMENT_DIR_MODE);
|
|
30953
31942
|
} catch {
|
|
@@ -31208,8 +32197,8 @@ async function setupPluginEventWiring(deps) {
|
|
|
31208
32197
|
// src/services/plugins/plugin-file-watcher.ts
|
|
31209
32198
|
import { exec as execCb2 } from "child_process";
|
|
31210
32199
|
import { existsSync as existsSync5 } from "fs";
|
|
31211
|
-
import { readdir as readdir14, readFile as
|
|
31212
|
-
import { basename as basename7, dirname as
|
|
32200
|
+
import { readdir as readdir14, readFile as readFile21, stat as stat11 } from "fs/promises";
|
|
32201
|
+
import { basename as basename7, dirname as dirname18, join as join37 } from "path";
|
|
31213
32202
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
31214
32203
|
import { promisify as promisify7 } from "util";
|
|
31215
32204
|
|
|
@@ -31344,7 +32333,7 @@ var PluginFileWatcher = class {
|
|
|
31344
32333
|
}
|
|
31345
32334
|
const loaded = [];
|
|
31346
32335
|
for (const entry of entries) {
|
|
31347
|
-
const pluginDir =
|
|
32336
|
+
const pluginDir = join37(dir, entry);
|
|
31348
32337
|
let stats;
|
|
31349
32338
|
try {
|
|
31350
32339
|
stats = await stat11(pluginDir);
|
|
@@ -31359,10 +32348,10 @@ var PluginFileWatcher = class {
|
|
|
31359
32348
|
this.#reconcile(loaded);
|
|
31360
32349
|
}
|
|
31361
32350
|
async #loadPlugin(id, pluginDir) {
|
|
31362
|
-
const manifestPath =
|
|
32351
|
+
const manifestPath = join37(pluginDir, "plugin.json");
|
|
31363
32352
|
let manifestRaw;
|
|
31364
32353
|
try {
|
|
31365
|
-
manifestRaw = await
|
|
32354
|
+
manifestRaw = await readFile21(manifestPath, "utf-8");
|
|
31366
32355
|
} catch {
|
|
31367
32356
|
this.#config.log({ event: "plugin_manifest_missing", pluginId: id, pluginDir });
|
|
31368
32357
|
return null;
|
|
@@ -31396,9 +32385,9 @@ var PluginFileWatcher = class {
|
|
|
31396
32385
|
return null;
|
|
31397
32386
|
}
|
|
31398
32387
|
let template = "";
|
|
31399
|
-
const templatePath =
|
|
32388
|
+
const templatePath = join37(pluginDir, "template.html");
|
|
31400
32389
|
try {
|
|
31401
|
-
template = await
|
|
32390
|
+
template = await readFile21(templatePath, "utf-8");
|
|
31402
32391
|
} catch {
|
|
31403
32392
|
}
|
|
31404
32393
|
await this.#loadAndRegisterHandler(id, pluginDir, manifest.title, manifest);
|
|
@@ -31411,7 +32400,7 @@ var PluginFileWatcher = class {
|
|
|
31411
32400
|
};
|
|
31412
32401
|
}
|
|
31413
32402
|
async #loadAndRegisterHandler(id, pluginDir, title, manifest) {
|
|
31414
|
-
const handlerPath =
|
|
32403
|
+
const handlerPath = join37(pluginDir, "handler.mjs");
|
|
31415
32404
|
const loaded = await this.#resolveHandler(id, handlerPath);
|
|
31416
32405
|
const { handlerFn, provideResourcesFn } = loaded;
|
|
31417
32406
|
if (manifest.provideResources && provideResourcesFn && this.#config.resourceResolver) {
|
|
@@ -31575,7 +32564,7 @@ async function findNativeDependencyMarker(pluginDir) {
|
|
|
31575
32564
|
}
|
|
31576
32565
|
async function scanNativeDependencyDir(dir, stack) {
|
|
31577
32566
|
for (const entry of await readNativeScanEntries(dir)) {
|
|
31578
|
-
const fullPath =
|
|
32567
|
+
const fullPath = join37(dir, entry.name);
|
|
31579
32568
|
if (isNativeDependencyMarker(entry, dir)) return fullPath;
|
|
31580
32569
|
if (shouldDescendForNativeScan(entry)) stack.push(fullPath);
|
|
31581
32570
|
}
|
|
@@ -31595,12 +32584,12 @@ function shouldDescendForNativeScan(entry) {
|
|
|
31595
32584
|
return entry.isDirectory() && entry.name !== ".git" && entry.name !== NATIVE_DEP_MARKER_DIR;
|
|
31596
32585
|
}
|
|
31597
32586
|
function isNodeBuildReleaseDir(dir) {
|
|
31598
|
-
return basename7(dir) === "Release" && basename7(
|
|
32587
|
+
return basename7(dir) === "Release" && basename7(dirname18(dir)) === "build";
|
|
31599
32588
|
}
|
|
31600
32589
|
|
|
31601
32590
|
// src/services/port-detection.ts
|
|
31602
32591
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
31603
|
-
import { dirname as
|
|
32592
|
+
import { dirname as dirname19, join as join38 } from "path";
|
|
31604
32593
|
var LSOF_TIMEOUT_MS = 8e3;
|
|
31605
32594
|
var LSOF_TTL_MS = 5e3;
|
|
31606
32595
|
var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
@@ -31783,7 +32772,7 @@ function hasStringName(val) {
|
|
|
31783
32772
|
function resolveProjectRoot(cwd) {
|
|
31784
32773
|
let dir = cwd;
|
|
31785
32774
|
while (true) {
|
|
31786
|
-
const candidate =
|
|
32775
|
+
const candidate = join38(dir, "package.json");
|
|
31787
32776
|
if (existsSync6(candidate)) {
|
|
31788
32777
|
let packageName;
|
|
31789
32778
|
try {
|
|
@@ -31796,7 +32785,7 @@ function resolveProjectRoot(cwd) {
|
|
|
31796
32785
|
}
|
|
31797
32786
|
return { projectRoot: dir, packageName };
|
|
31798
32787
|
}
|
|
31799
|
-
const parent =
|
|
32788
|
+
const parent = dirname19(dir);
|
|
31800
32789
|
if (parent === dir) {
|
|
31801
32790
|
return {};
|
|
31802
32791
|
}
|
|
@@ -31907,7 +32896,7 @@ import {
|
|
|
31907
32896
|
unlinkSync as unlinkSync2,
|
|
31908
32897
|
writeFileSync as writeFileSync2
|
|
31909
32898
|
} from "fs";
|
|
31910
|
-
import { join as
|
|
32899
|
+
import { join as join39 } from "path";
|
|
31911
32900
|
function isEnoent6(err3) {
|
|
31912
32901
|
return err3 instanceof Error && Reflect.get(err3, "code") === "ENOENT";
|
|
31913
32902
|
}
|
|
@@ -31917,7 +32906,7 @@ function atomicWriteSync(filePath, content) {
|
|
|
31917
32906
|
renameSync(tmpPath, filePath);
|
|
31918
32907
|
}
|
|
31919
32908
|
function recordFilePath2(rootDir, taskId, elementId) {
|
|
31920
|
-
return
|
|
32909
|
+
return join39(rootDir, taskId, `${elementId}.json`);
|
|
31921
32910
|
}
|
|
31922
32911
|
function parseRecord2(filePath, logger2) {
|
|
31923
32912
|
let raw;
|
|
@@ -31957,7 +32946,7 @@ function createPreviewStateStore(opts) {
|
|
|
31957
32946
|
return taskMap;
|
|
31958
32947
|
}
|
|
31959
32948
|
function scanTaskDir(taskId) {
|
|
31960
|
-
const taskPath =
|
|
32949
|
+
const taskPath = join39(rootDir, taskId);
|
|
31961
32950
|
let files;
|
|
31962
32951
|
try {
|
|
31963
32952
|
files = readdirSync4(taskPath);
|
|
@@ -31966,7 +32955,7 @@ function createPreviewStateStore(opts) {
|
|
|
31966
32955
|
}
|
|
31967
32956
|
for (const file of files) {
|
|
31968
32957
|
if (!file.endsWith(".json")) continue;
|
|
31969
|
-
const record = parseRecord2(
|
|
32958
|
+
const record = parseRecord2(join39(taskPath, file), logger2);
|
|
31970
32959
|
if (record) getTaskMap(taskId).set(record.elementId, record);
|
|
31971
32960
|
}
|
|
31972
32961
|
}
|
|
@@ -31992,7 +32981,7 @@ function createPreviewStateStore(opts) {
|
|
|
31992
32981
|
function writeToDisk(taskId, state) {
|
|
31993
32982
|
const filePath = recordFilePath2(rootDir, taskId, state.elementId);
|
|
31994
32983
|
try {
|
|
31995
|
-
mkdirSync3(
|
|
32984
|
+
mkdirSync3(join39(rootDir, taskId), { recursive: true });
|
|
31996
32985
|
atomicWriteSync(filePath, JSON.stringify(state));
|
|
31997
32986
|
} catch (err3) {
|
|
31998
32987
|
logger2.warn(
|
|
@@ -33362,633 +34351,172 @@ function handleAssetChannel(dc, assetStore, deliverableStore, allowedTaskId, log
|
|
|
33362
34351
|
}).catch((err3) => {
|
|
33363
34352
|
log({
|
|
33364
34353
|
event: "asset_upload_failed",
|
|
33365
|
-
requestId,
|
|
33366
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33367
|
-
});
|
|
33368
|
-
rawSend(
|
|
33369
|
-
JSON.stringify({
|
|
33370
|
-
type: "upload-error",
|
|
33371
|
-
requestId,
|
|
33372
|
-
error: err3 instanceof Error ? err3.message : "Upload failed"
|
|
33373
|
-
})
|
|
33374
|
-
);
|
|
33375
|
-
});
|
|
33376
|
-
}
|
|
33377
|
-
async function handleDownloadRequest(requestId, assetId) {
|
|
33378
|
-
try {
|
|
33379
|
-
const { data, meta } = await assetStore.read(assetId);
|
|
33380
|
-
rawSend(
|
|
33381
|
-
JSON.stringify({
|
|
33382
|
-
type: "download-head",
|
|
33383
|
-
requestId,
|
|
33384
|
-
mimeType: meta.mimeType,
|
|
33385
|
-
totalBytes: data.byteLength
|
|
33386
|
-
})
|
|
33387
|
-
);
|
|
33388
|
-
const shell = createDownloadShell(requestId);
|
|
33389
|
-
downloadShells.set(requestId, shell);
|
|
33390
|
-
await shell.initialize();
|
|
33391
|
-
await shell.setConnected(true);
|
|
33392
|
-
const bytes = new Uint8Array(data);
|
|
33393
|
-
let chunkIndex = 0;
|
|
33394
|
-
let yieldCounter = 0;
|
|
33395
|
-
for (let offset = 0; offset < bytes.byteLength; offset += ASSET_CHUNK_SIZE) {
|
|
33396
|
-
const end = Math.min(offset + ASSET_CHUNK_SIZE, bytes.byteLength);
|
|
33397
|
-
const chunk = bytes.subarray(offset, end);
|
|
33398
|
-
await shell.send({
|
|
33399
|
-
kind: "download-chunk",
|
|
33400
|
-
requestId,
|
|
33401
|
-
chunkIndex,
|
|
33402
|
-
data: encodeChunk(chunk)
|
|
33403
|
-
});
|
|
33404
|
-
chunkIndex++;
|
|
33405
|
-
yieldCounter++;
|
|
33406
|
-
if (yieldCounter % YIELD_EVERY_CHUNKS === 0) {
|
|
33407
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
33408
|
-
}
|
|
33409
|
-
}
|
|
33410
|
-
rawSend(JSON.stringify({ type: "download-done", requestId }));
|
|
33411
|
-
log({ event: "asset_download_complete", requestId, assetId, size: data.byteLength });
|
|
33412
|
-
setTimeout(() => downloadShells.delete(requestId), UPLOAD_TIMEOUT_MS);
|
|
33413
|
-
} catch (err3) {
|
|
33414
|
-
downloadShells.delete(requestId);
|
|
33415
|
-
const { event, reason } = classifyDownloadError(assetId, err3);
|
|
33416
|
-
log({ event, requestId, assetId, error: reason });
|
|
33417
|
-
rawSend(
|
|
33418
|
-
JSON.stringify({
|
|
33419
|
-
type: "download-error",
|
|
33420
|
-
requestId,
|
|
33421
|
-
error: err3 instanceof Error ? err3.message : "Download failed"
|
|
33422
|
-
})
|
|
33423
|
-
);
|
|
33424
|
-
}
|
|
33425
|
-
}
|
|
33426
|
-
async function handleDeliverableFileRequest(requestId, taskId, deliverableId) {
|
|
33427
|
-
const resolved = await resolveDeliverableFile(deliverableStore, taskId, deliverableId);
|
|
33428
|
-
if (resolved.kind === "error") {
|
|
33429
|
-
log({
|
|
33430
|
-
event: resolved.event,
|
|
33431
|
-
requestId,
|
|
33432
|
-
taskId,
|
|
33433
|
-
deliverableId,
|
|
33434
|
-
...resolved.logExtras
|
|
33435
|
-
});
|
|
33436
|
-
rawSend(JSON.stringify({ type: "download-error", requestId, error: "" }));
|
|
33437
|
-
return;
|
|
33438
|
-
}
|
|
33439
|
-
if (allowedTaskId !== null && resolved.recordTaskId !== allowedTaskId) {
|
|
33440
|
-
log({
|
|
33441
|
-
event: "deliverable_file_task_scope_mismatch",
|
|
33442
|
-
level: "warn",
|
|
33443
|
-
requestId,
|
|
33444
|
-
allowedTaskId,
|
|
33445
|
-
requestedTaskId: taskId
|
|
33446
|
-
});
|
|
33447
|
-
rawSend(JSON.stringify({ type: "download-error", requestId, error: "" }));
|
|
33448
|
-
return;
|
|
33449
|
-
}
|
|
33450
|
-
const { filePath, mimeType } = resolved;
|
|
33451
|
-
try {
|
|
33452
|
-
const data = await fsReadFile(filePath);
|
|
33453
|
-
rawSend(
|
|
33454
|
-
JSON.stringify({
|
|
33455
|
-
type: "download-head",
|
|
33456
|
-
requestId,
|
|
33457
|
-
mimeType,
|
|
33458
|
-
totalBytes: data.byteLength
|
|
33459
|
-
})
|
|
33460
|
-
);
|
|
33461
|
-
const shell = createDownloadShell(requestId);
|
|
33462
|
-
downloadShells.set(requestId, shell);
|
|
33463
|
-
await shell.initialize();
|
|
33464
|
-
await shell.setConnected(true);
|
|
33465
|
-
const bytes = new Uint8Array(data);
|
|
33466
|
-
let chunkIndex = 0;
|
|
33467
|
-
let yieldCounter = 0;
|
|
33468
|
-
for (let offset = 0; offset < bytes.byteLength; offset += ASSET_CHUNK_SIZE) {
|
|
33469
|
-
const end = Math.min(offset + ASSET_CHUNK_SIZE, bytes.byteLength);
|
|
33470
|
-
const chunk = bytes.subarray(offset, end);
|
|
33471
|
-
await shell.send({
|
|
33472
|
-
kind: "download-chunk",
|
|
33473
|
-
requestId,
|
|
33474
|
-
chunkIndex,
|
|
33475
|
-
data: encodeChunk(chunk)
|
|
33476
|
-
});
|
|
33477
|
-
chunkIndex++;
|
|
33478
|
-
yieldCounter++;
|
|
33479
|
-
if (yieldCounter % YIELD_EVERY_CHUNKS === 0) {
|
|
33480
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
33481
|
-
}
|
|
33482
|
-
}
|
|
33483
|
-
rawSend(JSON.stringify({ type: "download-done", requestId }));
|
|
33484
|
-
log({
|
|
33485
|
-
event: "deliverable_file_download_complete",
|
|
33486
|
-
requestId,
|
|
33487
|
-
deliverableId,
|
|
33488
|
-
size: data.byteLength
|
|
33489
|
-
});
|
|
33490
|
-
setTimeout(() => downloadShells.delete(requestId), UPLOAD_TIMEOUT_MS);
|
|
33491
|
-
} catch (err3) {
|
|
33492
|
-
downloadShells.delete(requestId);
|
|
33493
|
-
const reason = err3 instanceof Error ? err3.message : String(err3);
|
|
33494
|
-
log({
|
|
33495
|
-
event: "deliverable_file_download_failed",
|
|
33496
|
-
level: "warn",
|
|
33497
|
-
requestId,
|
|
33498
|
-
deliverableId,
|
|
33499
|
-
error: reason
|
|
33500
|
-
});
|
|
33501
|
-
rawSend(JSON.stringify({ type: "download-error", requestId, error: "" }));
|
|
33502
|
-
}
|
|
33503
|
-
}
|
|
33504
|
-
dc.onmessage = (event) => {
|
|
33505
|
-
if (typeof event.data === "string") {
|
|
33506
|
-
const data = event.data;
|
|
33507
|
-
receiveQueue = receiveQueue.then(() => handleTextMessage(data)).catch((err3) => {
|
|
33508
|
-
log({
|
|
33509
|
-
event: "asset_channel_handler_error",
|
|
33510
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33511
|
-
});
|
|
33512
|
-
});
|
|
33513
|
-
}
|
|
33514
|
-
};
|
|
33515
|
-
dc.onclose = () => {
|
|
33516
|
-
for (const upload of pendingUploads.values()) clearTimeout(upload.timer);
|
|
33517
|
-
pendingUploads.clear();
|
|
33518
|
-
downloadShells.clear();
|
|
33519
|
-
guardedText.dispose();
|
|
33520
|
-
receiveQueue = Promise.resolve();
|
|
33521
|
-
};
|
|
33522
|
-
return {
|
|
33523
|
-
dispose() {
|
|
33524
|
-
dc.onmessage = null;
|
|
33525
|
-
dc.onclose = null;
|
|
33526
|
-
for (const upload of pendingUploads.values()) clearTimeout(upload.timer);
|
|
33527
|
-
pendingUploads.clear();
|
|
33528
|
-
downloadShells.clear();
|
|
33529
|
-
guardedText.dispose();
|
|
33530
|
-
receiveQueue = Promise.resolve();
|
|
33531
|
-
}
|
|
33532
|
-
};
|
|
33533
|
-
}
|
|
33534
|
-
|
|
33535
|
-
// src/services/collab/collab-repo-manager.ts
|
|
33536
|
-
var BRIDGE_EPHEMERAL_HOPS = 1;
|
|
33537
|
-
var DAEMON_IDENTITY = { name: "shipyard-daemon", type: "service" };
|
|
33538
|
-
function restoreEphemeralHops(message) {
|
|
33539
|
-
switch (message.type) {
|
|
33540
|
-
case "channel/ephemeral":
|
|
33541
|
-
if (message.hopsRemaining >= BRIDGE_EPHEMERAL_HOPS) return message;
|
|
33542
|
-
return { ...message, hopsRemaining: BRIDGE_EPHEMERAL_HOPS };
|
|
33543
|
-
case "channel/batch": {
|
|
33544
|
-
let changed = false;
|
|
33545
|
-
const nextMessages = message.messages.map((inner) => {
|
|
33546
|
-
if (inner.type !== "channel/ephemeral") return inner;
|
|
33547
|
-
if (inner.hopsRemaining >= BRIDGE_EPHEMERAL_HOPS) return inner;
|
|
33548
|
-
changed = true;
|
|
33549
|
-
return { ...inner, hopsRemaining: BRIDGE_EPHEMERAL_HOPS };
|
|
33550
|
-
});
|
|
33551
|
-
if (!changed) return message;
|
|
33552
|
-
return { ...message, messages: nextMessages };
|
|
33553
|
-
}
|
|
33554
|
-
case "channel/establish-request":
|
|
33555
|
-
case "channel/establish-response":
|
|
33556
|
-
case "channel/sync-request":
|
|
33557
|
-
case "channel/sync-response":
|
|
33558
|
-
case "channel/update":
|
|
33559
|
-
case "channel/directory-request":
|
|
33560
|
-
case "channel/directory-response":
|
|
33561
|
-
case "channel/new-doc":
|
|
33562
|
-
case "channel/delete-request":
|
|
33563
|
-
case "channel/delete-response":
|
|
33564
|
-
return message;
|
|
33565
|
-
default: {
|
|
33566
|
-
const _exhaustive = message;
|
|
33567
|
-
void _exhaustive;
|
|
33568
|
-
return message;
|
|
33569
|
-
}
|
|
33570
|
-
}
|
|
33571
|
-
}
|
|
33572
|
-
var HopRestoringBridgeAdapter = class extends BridgeAdapter {
|
|
33573
|
-
generate(context) {
|
|
33574
|
-
const channel = super.generate(context);
|
|
33575
|
-
const originalSend = channel.send;
|
|
33576
|
-
return {
|
|
33577
|
-
...channel,
|
|
33578
|
-
send: (msg) => originalSend(restoreEphemeralHops(msg))
|
|
33579
|
-
};
|
|
33580
|
-
}
|
|
33581
|
-
};
|
|
33582
|
-
function planCollabRoomResources(taskId, roomId, epochs) {
|
|
33583
|
-
return {
|
|
33584
|
-
docIds: [buildPlanDocId(taskId, epochs.plan), buildCanvasDocId(taskId, epochs.canvas)],
|
|
33585
|
-
personalBridgeType: `bridge-personal-${roomId}`,
|
|
33586
|
-
collabBridgeType: `bridge-collab-${roomId}`
|
|
33587
|
-
};
|
|
33588
|
-
}
|
|
33589
|
-
function buildCollabRepoPermissions(taskId, peerRoleRegistry) {
|
|
33590
|
-
return {
|
|
33591
|
-
visibility(doc, peer) {
|
|
33592
|
-
if (peer.channelKind === "storage") return true;
|
|
33593
|
-
if (peer.peerType === "service") return true;
|
|
33594
|
-
const parsed = parseDocumentId(doc.id);
|
|
33595
|
-
if (!parsed) return false;
|
|
33596
|
-
if (parsed.key !== taskId) return false;
|
|
33597
|
-
const entry = peerRoleRegistry.getEntry(peer.peerId);
|
|
33598
|
-
if (!entry) return false;
|
|
33599
|
-
return isCollabPrefixVisible(entry.role, parsed.prefix);
|
|
33600
|
-
},
|
|
33601
|
-
mutability(doc, peer) {
|
|
33602
|
-
if (peer.channelKind === "storage") return true;
|
|
33603
|
-
if (peer.peerType === "service") return true;
|
|
33604
|
-
const parsed = parseDocumentId(doc.id);
|
|
33605
|
-
if (!parsed) return false;
|
|
33606
|
-
if (parsed.key !== taskId) return false;
|
|
33607
|
-
const entry = peerRoleRegistry.getEntry(peer.peerId);
|
|
33608
|
-
if (!entry) return false;
|
|
33609
|
-
return isCollabPrefixMutable(entry.role, parsed.prefix);
|
|
33610
|
-
},
|
|
33611
|
-
creation(_docId, _peer) {
|
|
33612
|
-
return false;
|
|
33613
|
-
},
|
|
33614
|
-
deletion(_doc, _peer) {
|
|
33615
|
-
return false;
|
|
33616
|
-
}
|
|
33617
|
-
};
|
|
33618
|
-
}
|
|
33619
|
-
function createCollabRepo(personalRepo, taskId, roomId, epochs, peerRoleRegistry) {
|
|
33620
|
-
const resources = planCollabRoomResources(taskId, roomId, epochs);
|
|
33621
|
-
const bridge = new Bridge();
|
|
33622
|
-
const personalBridgeAdapter = new HopRestoringBridgeAdapter({
|
|
33623
|
-
adapterType: resources.personalBridgeType,
|
|
33624
|
-
bridge
|
|
33625
|
-
});
|
|
33626
|
-
const collabBridgeAdapter = new HopRestoringBridgeAdapter({
|
|
33627
|
-
adapterType: resources.collabBridgeType,
|
|
33628
|
-
bridge
|
|
33629
|
-
});
|
|
33630
|
-
const collabWebrtcAdapter = new WebRtcDataChannelAdapter();
|
|
33631
|
-
const collabRepo = new Repo({
|
|
33632
|
-
identity: { ...DAEMON_IDENTITY, name: `collab-room-${roomId}` },
|
|
33633
|
-
adapters: [collabBridgeAdapter, collabWebrtcAdapter],
|
|
33634
|
-
permissions: buildCollabRepoPermissions(taskId, peerRoleRegistry)
|
|
33635
|
-
});
|
|
33636
|
-
personalRepo.addAdapter(personalBridgeAdapter);
|
|
33637
|
-
return {
|
|
33638
|
-
repo: collabRepo,
|
|
33639
|
-
webrtcAdapter: collabWebrtcAdapter,
|
|
33640
|
-
async destroy() {
|
|
33641
|
-
personalRepo.removeAdapter(personalBridgeAdapter.adapterId);
|
|
33642
|
-
await collabRepo.shutdown();
|
|
33643
|
-
}
|
|
33644
|
-
};
|
|
33645
|
-
}
|
|
33646
|
-
|
|
33647
|
-
// src/services/collab/collab-room-manager.ts
|
|
33648
|
-
function assertNever5(x2) {
|
|
33649
|
-
throw new Error(`Unhandled message type: ${JSON.stringify(x2)}`);
|
|
33650
|
-
}
|
|
33651
|
-
function namespacePeerId(roomId, remoteUserId) {
|
|
33652
|
-
return `collab:${roomId}:${remoteUserId}`;
|
|
33653
|
-
}
|
|
33654
|
-
function stripCollabRoomPrefix(roomId, namespacedId) {
|
|
33655
|
-
const roomPrefix = `collab:${roomId}:`;
|
|
33656
|
-
if (!namespacedId.startsWith(roomPrefix)) {
|
|
33657
|
-
throw new Error(
|
|
33658
|
-
`Expected collab-namespaced peer ID with prefix "${roomPrefix}", got "${namespacedId}"`
|
|
33659
|
-
);
|
|
33660
|
-
}
|
|
33661
|
-
const browserMarker = ":browser:";
|
|
33662
|
-
const browserIdx = namespacedId.lastIndexOf(browserMarker);
|
|
33663
|
-
if (browserIdx === -1) {
|
|
33664
|
-
throw new Error(`Expected collab peer ID to contain "${browserMarker}", got "${namespacedId}"`);
|
|
33665
|
-
}
|
|
33666
|
-
return namespacedId.slice(browserIdx + browserMarker.length);
|
|
33667
|
-
}
|
|
33668
|
-
function bumpAndNotify(room, deps) {
|
|
33669
|
-
room.generation += 1;
|
|
33670
|
-
deps.onRoomStateChanged?.(room.taskId);
|
|
33671
|
-
}
|
|
33672
|
-
function handleParticipantsList(room, participants, deps) {
|
|
33673
|
-
if (!room.myUserId) return;
|
|
33674
|
-
room.participants = participants.map((p2) => ({
|
|
33675
|
-
userId: p2.userId,
|
|
33676
|
-
username: p2.username,
|
|
33677
|
-
avatarUrl: p2.avatarUrl,
|
|
33678
|
-
role: p2.role,
|
|
33679
|
-
connectionId: p2.connectionId,
|
|
33680
|
-
isHub: p2.isHub
|
|
33681
|
-
}));
|
|
33682
|
-
for (const p2 of participants) {
|
|
33683
|
-
room.connectionIdByUser.set(p2.userId, p2.connectionId);
|
|
33684
|
-
}
|
|
33685
|
-
bumpAndNotify(room, deps);
|
|
33686
|
-
for (const participant of participants) {
|
|
33687
|
-
if (participant.userId === room.myUserId) continue;
|
|
33688
|
-
if (room.knownPeers.has(participant.userId)) continue;
|
|
33689
|
-
registerCollabParticipant(
|
|
33690
|
-
room,
|
|
33691
|
-
participant.userId,
|
|
33692
|
-
participant.role,
|
|
33693
|
-
participant.username,
|
|
33694
|
-
deps
|
|
33695
|
-
);
|
|
33696
|
-
initiateOfferToParticipant(room, participant.userId, deps);
|
|
33697
|
-
}
|
|
33698
|
-
}
|
|
33699
|
-
function handleParticipantJoined(room, participant, deps) {
|
|
33700
|
-
if (!room.myUserId) return;
|
|
33701
|
-
if (participant.userId === room.myUserId) return;
|
|
33702
|
-
room.connectionIdByUser.set(participant.userId, participant.connectionId);
|
|
33703
|
-
if (room.knownPeers.has(participant.userId)) return;
|
|
33704
|
-
room.participants = [
|
|
33705
|
-
...room.participants,
|
|
33706
|
-
{
|
|
33707
|
-
userId: participant.userId,
|
|
33708
|
-
username: participant.username,
|
|
33709
|
-
avatarUrl: participant.avatarUrl,
|
|
33710
|
-
role: participant.role,
|
|
33711
|
-
connectionId: participant.connectionId,
|
|
33712
|
-
isHub: participant.isHub
|
|
33713
|
-
}
|
|
33714
|
-
];
|
|
33715
|
-
bumpAndNotify(room, deps);
|
|
33716
|
-
registerCollabParticipant(room, participant.userId, participant.role, participant.username, deps);
|
|
33717
|
-
initiateOfferToParticipant(room, participant.userId, deps);
|
|
33718
|
-
}
|
|
33719
|
-
function handleParticipantLeft(room, msg, deps) {
|
|
33720
|
-
const { userId, connectionId } = msg;
|
|
33721
|
-
const tracked = room.connectionIdByUser.get(userId);
|
|
33722
|
-
if (tracked !== void 0 && tracked !== connectionId) {
|
|
33723
|
-
deps.log({
|
|
33724
|
-
event: "participant_left_stale_dropped",
|
|
33725
|
-
roomId: room.roomId,
|
|
33726
|
-
userId,
|
|
33727
|
-
incomingConnectionId: connectionId,
|
|
33728
|
-
trackedConnectionId: tracked
|
|
33729
|
-
});
|
|
33730
|
-
return;
|
|
33731
|
-
}
|
|
33732
|
-
room.knownPeers.delete(userId);
|
|
33733
|
-
room.participants = room.participants.filter((p2) => p2.userId !== userId);
|
|
33734
|
-
room.connectionIdByUser.delete(userId);
|
|
33735
|
-
bumpAndNotify(room, deps);
|
|
33736
|
-
const peerId = namespacePeerId(room.roomId, userId);
|
|
33737
|
-
deps.peerRoleRegistry.unregisterPeer(peerId);
|
|
33738
|
-
room.peerManager.closePeer(peerId);
|
|
33739
|
-
deps.log({ event: "collab_participant_left", roomId: room.roomId, userId });
|
|
33740
|
-
}
|
|
33741
|
-
function handleWebrtcSignaling(room, type, targetUserId, generationId, payload, deps) {
|
|
33742
|
-
const peerId = namespacePeerId(room.roomId, targetUserId);
|
|
33743
|
-
const actions = {
|
|
33744
|
-
// eslint-disable-next-line no-restricted-syntax -- SDP is opaque z.unknown() from signaling
|
|
33745
|
-
offer: () => room.peerManager.handleOffer(peerId, payload, generationId),
|
|
33746
|
-
// eslint-disable-next-line no-restricted-syntax -- SDP is opaque z.unknown() from signaling
|
|
33747
|
-
answer: () => room.peerManager.handleAnswer(peerId, payload, generationId),
|
|
33748
|
-
// eslint-disable-next-line no-restricted-syntax -- ICE candidate is opaque z.unknown() from signaling
|
|
33749
|
-
ice: () => room.peerManager.handleIce(peerId, payload, generationId)
|
|
33750
|
-
};
|
|
33751
|
-
actions[type]().catch((err3) => {
|
|
33752
|
-
deps.log({
|
|
33753
|
-
event: `collab_${type}_failed`,
|
|
33754
|
-
roomId: room.roomId,
|
|
33755
|
-
generationId,
|
|
33756
|
-
error: String(err3)
|
|
33757
|
-
});
|
|
33758
|
-
});
|
|
33759
|
-
}
|
|
33760
|
-
function handleCollabRoomMessage(room, msg, deps) {
|
|
33761
|
-
switch (msg.type) {
|
|
33762
|
-
case "authenticated":
|
|
33763
|
-
room.myUserId = msg.userId;
|
|
33764
|
-
deps.log({ event: "collab_authenticated", roomId: room.roomId, userId: msg.userId });
|
|
33765
|
-
break;
|
|
33766
|
-
case "participants-list":
|
|
33767
|
-
handleParticipantsList(room, msg.participants, deps);
|
|
33768
|
-
break;
|
|
33769
|
-
case "participant-joined":
|
|
33770
|
-
handleParticipantJoined(room, msg.participant, deps);
|
|
33771
|
-
break;
|
|
33772
|
-
case "participant-left":
|
|
33773
|
-
handleParticipantLeft(room, { userId: msg.userId, connectionId: msg.connectionId }, deps);
|
|
33774
|
-
break;
|
|
33775
|
-
case "webrtc-offer":
|
|
33776
|
-
handleWebrtcSignaling(room, "offer", msg.targetUserId, msg.generationId, msg.offer, deps);
|
|
33777
|
-
break;
|
|
33778
|
-
case "webrtc-answer":
|
|
33779
|
-
handleWebrtcSignaling(room, "answer", msg.targetUserId, msg.generationId, msg.answer, deps);
|
|
33780
|
-
break;
|
|
33781
|
-
case "webrtc-ice":
|
|
33782
|
-
handleWebrtcSignaling(room, "ice", msg.targetUserId, msg.generationId, msg.candidate, deps);
|
|
33783
|
-
break;
|
|
33784
|
-
case "ice-servers":
|
|
33785
|
-
room.peerManager.updateIceServers(msg.iceServers);
|
|
33786
|
-
deps.log({ event: "collab_ice_servers", roomId: room.roomId, count: msg.iceServers.length });
|
|
33787
|
-
break;
|
|
33788
|
-
case "error":
|
|
33789
|
-
deps.log({ event: "collab_room_error", roomId: room.roomId, message: msg.message });
|
|
33790
|
-
break;
|
|
33791
|
-
default:
|
|
33792
|
-
assertNever5(msg);
|
|
33793
|
-
}
|
|
33794
|
-
}
|
|
33795
|
-
function registerCollabParticipant(room, userId, role, displayName, deps) {
|
|
33796
|
-
const peerId = namespacePeerId(room.roomId, userId);
|
|
33797
|
-
deps.peerRoleRegistry.registerCollabPeer(peerId, role, userId, displayName, room.taskId);
|
|
33798
|
-
}
|
|
33799
|
-
function initiateOfferToParticipant(room, remoteUserId, deps) {
|
|
33800
|
-
if (!room.myUserId) return;
|
|
33801
|
-
room.knownPeers.add(remoteUserId);
|
|
33802
|
-
deps.log({ event: "collab_initiating_offer", roomId: room.roomId, target: remoteUserId });
|
|
33803
|
-
const peerId = namespacePeerId(room.roomId, remoteUserId);
|
|
33804
|
-
room.peerManager.initiateOffer(peerId).catch((err3) => {
|
|
33805
|
-
deps.log({ event: "collab_offer_initiate_failed", roomId: room.roomId, error: String(err3) });
|
|
33806
|
-
});
|
|
33807
|
-
}
|
|
33808
|
-
function createCollabRoomManager(deps) {
|
|
33809
|
-
const rooms = /* @__PURE__ */ new Map();
|
|
33810
|
-
async function leave(roomId) {
|
|
33811
|
-
const room = rooms.get(roomId);
|
|
33812
|
-
if (!room) return;
|
|
33813
|
-
deps.log({ event: "collab_leaving", roomId });
|
|
33814
|
-
clearTimeout(room.expiryTimer);
|
|
33815
|
-
room.unsubMessage();
|
|
33816
|
-
room.unsubState();
|
|
33817
|
-
for (const userId of room.knownPeers) {
|
|
33818
|
-
deps.peerRoleRegistry.unregisterPeer(namespacePeerId(roomId, userId));
|
|
33819
|
-
}
|
|
33820
|
-
const destroyPromise = room.collabRepoHandle?.destroy();
|
|
33821
|
-
room.peerManager.destroy();
|
|
33822
|
-
room.connection.disconnect();
|
|
33823
|
-
const leftTaskId = room.taskId;
|
|
33824
|
-
rooms.delete(roomId);
|
|
33825
|
-
deps.onRoomStateChanged?.(leftTaskId);
|
|
33826
|
-
if (destroyPromise) {
|
|
33827
|
-
await destroyPromise.catch((err3) => {
|
|
33828
|
-
deps.log({
|
|
33829
|
-
event: "collab_repo_destroy_failed",
|
|
33830
|
-
roomId,
|
|
33831
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33832
|
-
});
|
|
33833
|
-
});
|
|
33834
|
-
}
|
|
33835
|
-
}
|
|
33836
|
-
return {
|
|
33837
|
-
joinRoom(config) {
|
|
33838
|
-
const { roomId, taskId, token, expiresAt, signalingBaseUrl, userToken, machineId } = config;
|
|
33839
|
-
const existing = rooms.get(roomId);
|
|
33840
|
-
if (existing) {
|
|
33841
|
-
const existingState = existing.connection.getState();
|
|
33842
|
-
if (existing.taskId === taskId && (existingState === "connected" || existingState === "connecting")) {
|
|
33843
|
-
deps.log({ event: "collab_join_dedup", roomId, state: existingState });
|
|
33844
|
-
return {
|
|
33845
|
-
roomId,
|
|
33846
|
-
taskId,
|
|
33847
|
-
async destroy() {
|
|
33848
|
-
await leave(roomId);
|
|
33849
|
-
}
|
|
33850
|
-
};
|
|
33851
|
-
}
|
|
33852
|
-
deps.log({ event: "collab_replacing", roomId });
|
|
33853
|
-
leave(roomId).catch((err3) => {
|
|
33854
|
-
deps.log({
|
|
33855
|
-
event: "collab_replace_leave_failed",
|
|
33856
|
-
roomId,
|
|
33857
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33858
|
-
});
|
|
33859
|
-
});
|
|
33860
|
-
}
|
|
33861
|
-
const wsUrl = new URL(signalingBaseUrl);
|
|
33862
|
-
wsUrl.pathname = ROUTES.WS_COLLAB.replace(":roomId", roomId);
|
|
33863
|
-
wsUrl.searchParams.set("token", token);
|
|
33864
|
-
wsUrl.searchParams.set("userToken", userToken);
|
|
33865
|
-
wsUrl.searchParams.set("clientType", "agent");
|
|
33866
|
-
wsUrl.searchParams.set("machineId", machineId);
|
|
33867
|
-
const connection = new CollabRoomConnection({
|
|
33868
|
-
url: wsUrl.toString(),
|
|
33869
|
-
maxRetries: -1,
|
|
33870
|
-
initialDelayMs: 1e3,
|
|
33871
|
-
maxDelayMs: 3e4,
|
|
33872
|
-
backoffMultiplier: 2
|
|
34354
|
+
requestId,
|
|
34355
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33873
34356
|
});
|
|
33874
|
-
|
|
33875
|
-
|
|
33876
|
-
|
|
33877
|
-
|
|
33878
|
-
|
|
33879
|
-
|
|
34357
|
+
rawSend(
|
|
34358
|
+
JSON.stringify({
|
|
34359
|
+
type: "upload-error",
|
|
34360
|
+
requestId,
|
|
34361
|
+
error: err3 instanceof Error ? err3.message : "Upload failed"
|
|
34362
|
+
})
|
|
33880
34363
|
);
|
|
33881
|
-
|
|
33882
|
-
|
|
33883
|
-
|
|
33884
|
-
|
|
33885
|
-
|
|
34364
|
+
});
|
|
34365
|
+
}
|
|
34366
|
+
async function handleDownloadRequest(requestId, assetId) {
|
|
34367
|
+
try {
|
|
34368
|
+
const { data, meta } = await assetStore.read(assetId);
|
|
34369
|
+
rawSend(
|
|
34370
|
+
JSON.stringify({
|
|
34371
|
+
type: "download-head",
|
|
34372
|
+
requestId,
|
|
34373
|
+
mimeType: meta.mimeType,
|
|
34374
|
+
totalBytes: data.byteLength
|
|
34375
|
+
})
|
|
33886
34376
|
);
|
|
33887
|
-
const
|
|
33888
|
-
|
|
33889
|
-
|
|
33890
|
-
|
|
33891
|
-
|
|
33892
|
-
|
|
33893
|
-
|
|
33894
|
-
|
|
33895
|
-
|
|
33896
|
-
|
|
33897
|
-
|
|
33898
|
-
|
|
33899
|
-
|
|
33900
|
-
|
|
33901
|
-
|
|
33902
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33903
|
-
});
|
|
34377
|
+
const shell = createDownloadShell(requestId);
|
|
34378
|
+
downloadShells.set(requestId, shell);
|
|
34379
|
+
await shell.initialize();
|
|
34380
|
+
await shell.setConnected(true);
|
|
34381
|
+
const bytes = new Uint8Array(data);
|
|
34382
|
+
let chunkIndex = 0;
|
|
34383
|
+
let yieldCounter = 0;
|
|
34384
|
+
for (let offset = 0; offset < bytes.byteLength; offset += ASSET_CHUNK_SIZE) {
|
|
34385
|
+
const end = Math.min(offset + ASSET_CHUNK_SIZE, bytes.byteLength);
|
|
34386
|
+
const chunk = bytes.subarray(offset, end);
|
|
34387
|
+
await shell.send({
|
|
34388
|
+
kind: "download-chunk",
|
|
34389
|
+
requestId,
|
|
34390
|
+
chunkIndex,
|
|
34391
|
+
data: encodeChunk(chunk)
|
|
33904
34392
|
});
|
|
33905
|
-
|
|
34393
|
+
chunkIndex++;
|
|
34394
|
+
yieldCounter++;
|
|
34395
|
+
if (yieldCounter % YIELD_EVERY_CHUNKS === 0) {
|
|
34396
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
34397
|
+
}
|
|
33906
34398
|
}
|
|
33907
|
-
|
|
33908
|
-
|
|
33909
|
-
|
|
34399
|
+
rawSend(JSON.stringify({ type: "download-done", requestId }));
|
|
34400
|
+
log({ event: "asset_download_complete", requestId, assetId, size: data.byteLength });
|
|
34401
|
+
setTimeout(() => downloadShells.delete(requestId), UPLOAD_TIMEOUT_MS);
|
|
34402
|
+
} catch (err3) {
|
|
34403
|
+
downloadShells.delete(requestId);
|
|
34404
|
+
const { event, reason } = classifyDownloadError(assetId, err3);
|
|
34405
|
+
log({ event, requestId, assetId, error: reason });
|
|
34406
|
+
rawSend(
|
|
34407
|
+
JSON.stringify({
|
|
34408
|
+
type: "download-error",
|
|
34409
|
+
requestId,
|
|
34410
|
+
error: err3 instanceof Error ? err3.message : "Download failed"
|
|
34411
|
+
})
|
|
34412
|
+
);
|
|
34413
|
+
}
|
|
34414
|
+
}
|
|
34415
|
+
async function handleDeliverableFileRequest(requestId, taskId, deliverableId) {
|
|
34416
|
+
const resolved = await resolveDeliverableFile(deliverableStore, taskId, deliverableId);
|
|
34417
|
+
if (resolved.kind === "error") {
|
|
34418
|
+
log({
|
|
34419
|
+
event: resolved.event,
|
|
34420
|
+
requestId,
|
|
33910
34421
|
taskId,
|
|
33911
|
-
|
|
33912
|
-
|
|
33913
|
-
collabRepoHandle,
|
|
33914
|
-
myUserId: null,
|
|
33915
|
-
knownPeers: /* @__PURE__ */ new Set(),
|
|
33916
|
-
participants: [],
|
|
33917
|
-
connectionIdByUser: /* @__PURE__ */ new Map(),
|
|
33918
|
-
expiresAt,
|
|
33919
|
-
generation: 0,
|
|
33920
|
-
expiryTimer: setTimeout(() => {
|
|
33921
|
-
deps.log({ event: "collab_token_expired", roomId });
|
|
33922
|
-
leave(roomId).catch((err3) => {
|
|
33923
|
-
deps.log({
|
|
33924
|
-
event: "collab_expiry_leave_failed",
|
|
33925
|
-
roomId,
|
|
33926
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
33927
|
-
});
|
|
33928
|
-
});
|
|
33929
|
-
}, safeDelayMs),
|
|
33930
|
-
unsubMessage: () => {
|
|
33931
|
-
},
|
|
33932
|
-
unsubState: () => {
|
|
33933
|
-
}
|
|
33934
|
-
};
|
|
33935
|
-
rooms.set(roomId, room);
|
|
33936
|
-
room.unsubMessage = connection.onMessage((msg) => {
|
|
33937
|
-
handleCollabRoomMessage(room, msg, deps);
|
|
34422
|
+
deliverableId,
|
|
34423
|
+
...resolved.logExtras
|
|
33938
34424
|
});
|
|
33939
|
-
|
|
33940
|
-
|
|
33941
|
-
|
|
33942
|
-
|
|
33943
|
-
|
|
33944
|
-
|
|
33945
|
-
|
|
33946
|
-
|
|
33947
|
-
|
|
33948
|
-
|
|
33949
|
-
room.myUserId = null;
|
|
33950
|
-
room.knownPeers.clear();
|
|
33951
|
-
room.connectionIdByUser.clear();
|
|
33952
|
-
}
|
|
34425
|
+
rawSend(JSON.stringify({ type: "download-error", requestId, error: "" }));
|
|
34426
|
+
return;
|
|
34427
|
+
}
|
|
34428
|
+
if (allowedTaskId !== null && resolved.recordTaskId !== allowedTaskId) {
|
|
34429
|
+
log({
|
|
34430
|
+
event: "deliverable_file_task_scope_mismatch",
|
|
34431
|
+
level: "warn",
|
|
34432
|
+
requestId,
|
|
34433
|
+
allowedTaskId,
|
|
34434
|
+
requestedTaskId: taskId
|
|
33953
34435
|
});
|
|
33954
|
-
|
|
33955
|
-
|
|
33956
|
-
|
|
33957
|
-
|
|
33958
|
-
|
|
33959
|
-
|
|
33960
|
-
|
|
33961
|
-
|
|
33962
|
-
|
|
33963
|
-
|
|
33964
|
-
|
|
33965
|
-
|
|
34436
|
+
rawSend(JSON.stringify({ type: "download-error", requestId, error: "" }));
|
|
34437
|
+
return;
|
|
34438
|
+
}
|
|
34439
|
+
const { filePath, mimeType } = resolved;
|
|
34440
|
+
try {
|
|
34441
|
+
const data = await fsReadFile(filePath);
|
|
34442
|
+
rawSend(
|
|
34443
|
+
JSON.stringify({
|
|
34444
|
+
type: "download-head",
|
|
34445
|
+
requestId,
|
|
34446
|
+
mimeType,
|
|
34447
|
+
totalBytes: data.byteLength
|
|
34448
|
+
})
|
|
34449
|
+
);
|
|
34450
|
+
const shell = createDownloadShell(requestId);
|
|
34451
|
+
downloadShells.set(requestId, shell);
|
|
34452
|
+
await shell.initialize();
|
|
34453
|
+
await shell.setConnected(true);
|
|
34454
|
+
const bytes = new Uint8Array(data);
|
|
34455
|
+
let chunkIndex = 0;
|
|
34456
|
+
let yieldCounter = 0;
|
|
34457
|
+
for (let offset = 0; offset < bytes.byteLength; offset += ASSET_CHUNK_SIZE) {
|
|
34458
|
+
const end = Math.min(offset + ASSET_CHUNK_SIZE, bytes.byteLength);
|
|
34459
|
+
const chunk = bytes.subarray(offset, end);
|
|
34460
|
+
await shell.send({
|
|
34461
|
+
kind: "download-chunk",
|
|
34462
|
+
requestId,
|
|
34463
|
+
chunkIndex,
|
|
34464
|
+
data: encodeChunk(chunk)
|
|
34465
|
+
});
|
|
34466
|
+
chunkIndex++;
|
|
34467
|
+
yieldCounter++;
|
|
34468
|
+
if (yieldCounter % YIELD_EVERY_CHUNKS === 0) {
|
|
34469
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
33966
34470
|
}
|
|
33967
34471
|
}
|
|
33968
|
-
|
|
33969
|
-
|
|
33970
|
-
|
|
33971
|
-
|
|
33972
|
-
|
|
33973
|
-
|
|
33974
|
-
|
|
33975
|
-
|
|
33976
|
-
|
|
33977
|
-
|
|
33978
|
-
|
|
33979
|
-
|
|
33980
|
-
|
|
33981
|
-
|
|
33982
|
-
|
|
33983
|
-
|
|
33984
|
-
|
|
33985
|
-
|
|
33986
|
-
|
|
33987
|
-
|
|
33988
|
-
|
|
33989
|
-
|
|
33990
|
-
|
|
33991
|
-
|
|
34472
|
+
rawSend(JSON.stringify({ type: "download-done", requestId }));
|
|
34473
|
+
log({
|
|
34474
|
+
event: "deliverable_file_download_complete",
|
|
34475
|
+
requestId,
|
|
34476
|
+
deliverableId,
|
|
34477
|
+
size: data.byteLength
|
|
34478
|
+
});
|
|
34479
|
+
setTimeout(() => downloadShells.delete(requestId), UPLOAD_TIMEOUT_MS);
|
|
34480
|
+
} catch (err3) {
|
|
34481
|
+
downloadShells.delete(requestId);
|
|
34482
|
+
const reason = err3 instanceof Error ? err3.message : String(err3);
|
|
34483
|
+
log({
|
|
34484
|
+
event: "deliverable_file_download_failed",
|
|
34485
|
+
level: "warn",
|
|
34486
|
+
requestId,
|
|
34487
|
+
deliverableId,
|
|
34488
|
+
error: reason
|
|
34489
|
+
});
|
|
34490
|
+
rawSend(JSON.stringify({ type: "download-error", requestId, error: "" }));
|
|
34491
|
+
}
|
|
34492
|
+
}
|
|
34493
|
+
dc.onmessage = (event) => {
|
|
34494
|
+
if (typeof event.data === "string") {
|
|
34495
|
+
const data = event.data;
|
|
34496
|
+
receiveQueue = receiveQueue.then(() => handleTextMessage(data)).catch((err3) => {
|
|
34497
|
+
log({
|
|
34498
|
+
event: "asset_channel_handler_error",
|
|
34499
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
34500
|
+
});
|
|
34501
|
+
});
|
|
34502
|
+
}
|
|
34503
|
+
};
|
|
34504
|
+
dc.onclose = () => {
|
|
34505
|
+
for (const upload of pendingUploads.values()) clearTimeout(upload.timer);
|
|
34506
|
+
pendingUploads.clear();
|
|
34507
|
+
downloadShells.clear();
|
|
34508
|
+
guardedText.dispose();
|
|
34509
|
+
receiveQueue = Promise.resolve();
|
|
34510
|
+
};
|
|
34511
|
+
return {
|
|
34512
|
+
dispose() {
|
|
34513
|
+
dc.onmessage = null;
|
|
34514
|
+
dc.onclose = null;
|
|
34515
|
+
for (const upload of pendingUploads.values()) clearTimeout(upload.timer);
|
|
34516
|
+
pendingUploads.clear();
|
|
34517
|
+
downloadShells.clear();
|
|
34518
|
+
guardedText.dispose();
|
|
34519
|
+
receiveQueue = Promise.resolve();
|
|
33992
34520
|
}
|
|
33993
34521
|
};
|
|
33994
34522
|
}
|
|
@@ -34333,19 +34861,19 @@ function wireThreadErrorFallback(daemon, dc, taskId, threadId, channelId, log) {
|
|
|
34333
34861
|
}
|
|
34334
34862
|
|
|
34335
34863
|
// src/services/terminal-handler.ts
|
|
34336
|
-
import { join as
|
|
34864
|
+
import { join as join40 } from "path";
|
|
34337
34865
|
|
|
34338
34866
|
// src/shared/pty-manager.ts
|
|
34339
34867
|
import { accessSync, chmodSync, constants as constants2, createWriteStream } from "fs";
|
|
34340
34868
|
import { rename as rename13, unlink as unlink8 } from "fs/promises";
|
|
34341
34869
|
import { createRequire as createRequire2 } from "module";
|
|
34342
|
-
import { dirname as
|
|
34870
|
+
import { dirname as dirname20, resolve as resolve7 } from "path";
|
|
34343
34871
|
import * as pty from "node-pty";
|
|
34344
34872
|
function ensureSpawnHelperExecutable() {
|
|
34345
34873
|
if (globalThis.process.platform === "win32") return;
|
|
34346
34874
|
try {
|
|
34347
34875
|
const req = createRequire2(import.meta.url);
|
|
34348
|
-
const nodePtyDir =
|
|
34876
|
+
const nodePtyDir = dirname20(req.resolve("node-pty/package.json"));
|
|
34349
34877
|
const spawnHelper = resolve7(
|
|
34350
34878
|
nodePtyDir,
|
|
34351
34879
|
"prebuilds",
|
|
@@ -34614,7 +35142,7 @@ function createPtyManager() {
|
|
|
34614
35142
|
// src/services/terminal-handler.ts
|
|
34615
35143
|
var MAX_PTYS_DEFAULT = 20;
|
|
34616
35144
|
function terminalLogPathFor(dir, terminalId) {
|
|
34617
|
-
return
|
|
35145
|
+
return join40(dir, `${terminalId}.log`);
|
|
34618
35146
|
}
|
|
34619
35147
|
function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
|
|
34620
35148
|
const maxPtys = deps.maxPtys ?? MAX_PTYS_DEFAULT;
|
|
@@ -34814,13 +35342,15 @@ function buildCollabRoomManager(deps) {
|
|
|
34814
35342
|
fileWatcherPool,
|
|
34815
35343
|
terminalPtys,
|
|
34816
35344
|
terminalLogsDir,
|
|
34817
|
-
housekeepingBarrier
|
|
35345
|
+
housekeepingBarrier,
|
|
35346
|
+
collabHostingStore
|
|
34818
35347
|
} = deps;
|
|
34819
35348
|
let managerRef = null;
|
|
34820
35349
|
const emitCollabRoomState = (taskId) => {
|
|
34821
35350
|
const mgr2 = managerRef;
|
|
34822
35351
|
if (!mgr2) return;
|
|
34823
35352
|
const state = mgr2.getStateForTask(taskId);
|
|
35353
|
+
if (!shouldBroadcastRoomState(state)) return;
|
|
34824
35354
|
daemon.taskManager.broadcastControl({
|
|
34825
35355
|
type: "collab_room_state",
|
|
34826
35356
|
taskId,
|
|
@@ -35216,6 +35746,7 @@ function buildCollabRoomManager(deps) {
|
|
|
35216
35746
|
onRoomStateChanged: (taskId) => {
|
|
35217
35747
|
emitCollabRoomState(taskId);
|
|
35218
35748
|
},
|
|
35749
|
+
collabHostingStore,
|
|
35219
35750
|
log: logAdapter
|
|
35220
35751
|
});
|
|
35221
35752
|
managerRef = mgr;
|
|
@@ -35225,7 +35756,7 @@ function buildCollabRoomManager(deps) {
|
|
|
35225
35756
|
// src/services/serve-factory.ts
|
|
35226
35757
|
import { randomUUID as randomUUID25 } from "crypto";
|
|
35227
35758
|
import { mkdir as mkdir36 } from "fs/promises";
|
|
35228
|
-
import { join as
|
|
35759
|
+
import { join as join79 } from "path";
|
|
35229
35760
|
|
|
35230
35761
|
// src/shared/capabilities/cursor-boot-auth.ts
|
|
35231
35762
|
async function mergeCursorAuthFromVault(caps, detect, log) {
|
|
@@ -35266,12 +35797,12 @@ async function mergeCursorAuthFromVault(caps, detect, log) {
|
|
|
35266
35797
|
|
|
35267
35798
|
// src/shared/capabilities/cursor-hook-shim-path.ts
|
|
35268
35799
|
import { statSync as statSync4 } from "fs";
|
|
35269
|
-
import { join as
|
|
35800
|
+
import { join as join41 } from "path";
|
|
35270
35801
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
35271
35802
|
function getCursorHookShimPath() {
|
|
35272
35803
|
const resourcesPath = Reflect.get(process, "resourcesPath");
|
|
35273
35804
|
if (typeof resourcesPath === "string" && resourcesPath.length > 0) {
|
|
35274
|
-
return
|
|
35805
|
+
return join41(resourcesPath, "daemon", "cursor-hook-shim.js");
|
|
35275
35806
|
}
|
|
35276
35807
|
return fileURLToPath2(new URL("./cursor-hook-shim.js", import.meta.url));
|
|
35277
35808
|
}
|
|
@@ -35504,9 +36035,9 @@ function resolveRuntimeAuthIdentity(runtimeAuth, runtimeId) {
|
|
|
35504
36035
|
}
|
|
35505
36036
|
|
|
35506
36037
|
// src/shared/capabilities/spinner-verbs-reader.ts
|
|
35507
|
-
import { readFile as
|
|
36038
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
35508
36039
|
import { homedir as homedir6 } from "os";
|
|
35509
|
-
import { join as
|
|
36040
|
+
import { join as join42 } from "path";
|
|
35510
36041
|
var ClaudeSpinnerVerbsSchema = external_exports.object({
|
|
35511
36042
|
mode: external_exports.enum(["append", "replace"]),
|
|
35512
36043
|
verbs: external_exports.array(external_exports.string())
|
|
@@ -35531,12 +36062,12 @@ function toPersistedVerbs(resolved, defaults) {
|
|
|
35531
36062
|
return resolved.every((v2, i) => v2 === defaults[i]) ? [] : [...resolved];
|
|
35532
36063
|
}
|
|
35533
36064
|
async function readSpinnerVerbs() {
|
|
35534
|
-
const claudeDir =
|
|
35535
|
-
const paths = [
|
|
36065
|
+
const claudeDir = join42(homedir6(), ".claude");
|
|
36066
|
+
const paths = [join42(claudeDir, "settings.json"), join42(claudeDir, "settings.local.json")];
|
|
35536
36067
|
const parsed = await Promise.all(
|
|
35537
36068
|
paths.map(async (p2) => {
|
|
35538
36069
|
try {
|
|
35539
|
-
const raw = await
|
|
36070
|
+
const raw = await readFile22(p2, "utf-8");
|
|
35540
36071
|
const json = JSON.parse(raw);
|
|
35541
36072
|
if (json === null || typeof json !== "object") return null;
|
|
35542
36073
|
const field2 = Reflect.get(json, "spinnerVerbs");
|
|
@@ -35573,8 +36104,8 @@ async function reimportSpinnerVerbs(store, log) {
|
|
|
35573
36104
|
}
|
|
35574
36105
|
|
|
35575
36106
|
// src/shared/mcp/oauth-state-store.ts
|
|
35576
|
-
import { readFile as
|
|
35577
|
-
import { join as
|
|
36107
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
36108
|
+
import { join as join43 } from "path";
|
|
35578
36109
|
import {
|
|
35579
36110
|
OAuthClientInformationFullSchema as OAuthClientInformationFullSchema2,
|
|
35580
36111
|
OAuthClientInformationSchema as OAuthClientInformationSchema2,
|
|
@@ -35620,7 +36151,7 @@ var MCPOAuthStateStore = class {
|
|
|
35620
36151
|
this.#shipyardHome = shipyardHome;
|
|
35621
36152
|
}
|
|
35622
36153
|
#filePath() {
|
|
35623
|
-
return
|
|
36154
|
+
return join43(this.#shipyardHome, STATE_FILE);
|
|
35624
36155
|
}
|
|
35625
36156
|
#lockPath() {
|
|
35626
36157
|
return `${this.#filePath()}.lock`;
|
|
@@ -35628,7 +36159,7 @@ var MCPOAuthStateStore = class {
|
|
|
35628
36159
|
/** Always reads the state map from disk (never the cache). */
|
|
35629
36160
|
async #readFreshMap() {
|
|
35630
36161
|
try {
|
|
35631
|
-
const raw = await
|
|
36162
|
+
const raw = await readFile23(this.#filePath(), "utf-8");
|
|
35632
36163
|
const parsed = StateMapSchema.safeParse(JSON.parse(raw));
|
|
35633
36164
|
return parsed.success ? parsed.data : {};
|
|
35634
36165
|
} catch {
|
|
@@ -35718,12 +36249,12 @@ function getShipyardSubagentCatalog() {
|
|
|
35718
36249
|
}
|
|
35719
36250
|
|
|
35720
36251
|
// src/services/bootstrap/rehydrate.ts
|
|
35721
|
-
import { readFile as
|
|
35722
|
-
import { join as
|
|
36252
|
+
import { readFile as readFile25, rename as rename15, writeFile as writeFile16 } from "fs/promises";
|
|
36253
|
+
import { join as join45 } from "path";
|
|
35723
36254
|
|
|
35724
36255
|
// src/services/collab/collab-queue-persistence.ts
|
|
35725
|
-
import { appendFile as appendFile2, mkdir as
|
|
35726
|
-
import { join as
|
|
36256
|
+
import { appendFile as appendFile2, mkdir as mkdir16, readdir as readdir15, readFile as readFile24, rename as rename14, rm as rm9, writeFile as writeFile15 } from "fs/promises";
|
|
36257
|
+
import { join as join44 } from "path";
|
|
35727
36258
|
var PersistedQueueEntrySchema = external_exports.object({
|
|
35728
36259
|
content: external_exports.array(ContentBlockSchema),
|
|
35729
36260
|
settings: DaemonSettingsSchema.optional(),
|
|
@@ -35747,12 +36278,12 @@ function parsePersistedLine(line) {
|
|
|
35747
36278
|
};
|
|
35748
36279
|
}
|
|
35749
36280
|
function buildCollabQueuePersistence(dataDir) {
|
|
35750
|
-
const queuesDir =
|
|
36281
|
+
const queuesDir = join44(dataDir, "collab-queues");
|
|
35751
36282
|
function queuePath(queueKey) {
|
|
35752
|
-
return
|
|
36283
|
+
return join44(queuesDir, `${queueKey}.jsonl`);
|
|
35753
36284
|
}
|
|
35754
36285
|
async function ensureDir() {
|
|
35755
|
-
await
|
|
36286
|
+
await mkdir16(queuesDir, { recursive: true });
|
|
35756
36287
|
}
|
|
35757
36288
|
const writeChain = /* @__PURE__ */ new Map();
|
|
35758
36289
|
async function chained(queueKey, work) {
|
|
@@ -35777,7 +36308,7 @@ function buildCollabQueuePersistence(dataDir) {
|
|
|
35777
36308
|
async read(queueKey) {
|
|
35778
36309
|
let raw;
|
|
35779
36310
|
try {
|
|
35780
|
-
raw = await
|
|
36311
|
+
raw = await readFile24(queuePath(queueKey), "utf-8");
|
|
35781
36312
|
} catch (err3) {
|
|
35782
36313
|
if (isEnoent(err3)) return [];
|
|
35783
36314
|
throw err3;
|
|
@@ -35842,7 +36373,7 @@ function buildCollabQueuePersistence(dataDir) {
|
|
|
35842
36373
|
}
|
|
35843
36374
|
for (const name of names) {
|
|
35844
36375
|
if (!name.includes(".tmp-")) continue;
|
|
35845
|
-
await rm9(
|
|
36376
|
+
await rm9(join44(queuesDir, name), { force: true }).catch(() => {
|
|
35846
36377
|
});
|
|
35847
36378
|
}
|
|
35848
36379
|
}
|
|
@@ -36104,11 +36635,11 @@ async function readRawTasksEnvelope(tasksPath) {
|
|
|
36104
36635
|
let raw;
|
|
36105
36636
|
let fromMigrated = false;
|
|
36106
36637
|
try {
|
|
36107
|
-
raw = await
|
|
36638
|
+
raw = await readFile25(tasksPath, "utf-8");
|
|
36108
36639
|
} catch (err3) {
|
|
36109
36640
|
if (!isEnoent(err3)) throw err3;
|
|
36110
36641
|
try {
|
|
36111
|
-
raw = await
|
|
36642
|
+
raw = await readFile25(`${tasksPath}.migrated`, "utf-8");
|
|
36112
36643
|
fromMigrated = true;
|
|
36113
36644
|
} catch (err22) {
|
|
36114
36645
|
if (isEnoent(err22)) return null;
|
|
@@ -36161,7 +36692,7 @@ async function stripPinnedFieldFromTasksFile(tasksPath, envelope, records) {
|
|
|
36161
36692
|
}
|
|
36162
36693
|
}
|
|
36163
36694
|
async function migratePinnedToFavoriteTasks(dataDir, userSettingsStore, log) {
|
|
36164
|
-
const tasksPath =
|
|
36695
|
+
const tasksPath = join45(dataDir, "tasks.json");
|
|
36165
36696
|
const parsed = await readRawTasksEnvelope(tasksPath);
|
|
36166
36697
|
if (!parsed) return;
|
|
36167
36698
|
const { records, envelope, fromMigrated } = parsed;
|
|
@@ -36276,8 +36807,8 @@ async function sweepStaleTasks(taskStateStore, taskManager, log) {
|
|
|
36276
36807
|
}
|
|
36277
36808
|
|
|
36278
36809
|
// src/services/bootstrap/update-status.ts
|
|
36279
|
-
import { readFile as
|
|
36280
|
-
import { join as
|
|
36810
|
+
import { readFile as readFile26, unlink as unlink9 } from "fs/promises";
|
|
36811
|
+
import { join as join46 } from "path";
|
|
36281
36812
|
var UPDATE_STATUS_FILENAME = "update-status.json";
|
|
36282
36813
|
var RAW_LOG_MAX_CHARS = 500;
|
|
36283
36814
|
var UpdateStatusSchema = external_exports.object({
|
|
@@ -36290,10 +36821,10 @@ var UpdateStatusSchema = external_exports.object({
|
|
|
36290
36821
|
rolledBack: external_exports.boolean()
|
|
36291
36822
|
});
|
|
36292
36823
|
async function readAndClearUpdateStatus(shipyardHome, log) {
|
|
36293
|
-
const path5 =
|
|
36824
|
+
const path5 = join46(shipyardHome, UPDATE_STATUS_FILENAME);
|
|
36294
36825
|
let raw;
|
|
36295
36826
|
try {
|
|
36296
|
-
raw = await
|
|
36827
|
+
raw = await readFile26(path5, "utf-8");
|
|
36297
36828
|
} catch (err3) {
|
|
36298
36829
|
if (isEnoent(err3)) return null;
|
|
36299
36830
|
log.warn({ err: err3, path: path5 }, "failed to read update-status.json");
|
|
@@ -36377,11 +36908,11 @@ function wireUpdateStatusForwarding(report, sink2, onMessage, log) {
|
|
|
36377
36908
|
|
|
36378
36909
|
// src/services/bootstrap/updates-cleanup.ts
|
|
36379
36910
|
import { readdir as readdir16, rm as rm10, stat as stat13, unlink as unlink11 } from "fs/promises";
|
|
36380
|
-
import { join as
|
|
36911
|
+
import { join as join48 } from "path";
|
|
36381
36912
|
|
|
36382
36913
|
// src/services/bootstrap/self-update-lock.ts
|
|
36383
|
-
import { mkdir as
|
|
36384
|
-
import { dirname as
|
|
36914
|
+
import { mkdir as mkdir17, readFile as readFile27, stat as stat12, unlink as unlink10, writeFile as writeFile17 } from "fs/promises";
|
|
36915
|
+
import { dirname as dirname21, join as join47 } from "path";
|
|
36385
36916
|
var LOCK_FILENAME = ".lock";
|
|
36386
36917
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
36387
36918
|
var LockFileSchema = external_exports.object({
|
|
@@ -36389,7 +36920,7 @@ var LockFileSchema = external_exports.object({
|
|
|
36389
36920
|
startedAt: external_exports.number()
|
|
36390
36921
|
});
|
|
36391
36922
|
function lockPath(shipyardHome) {
|
|
36392
|
-
return
|
|
36923
|
+
return join47(shipyardHome, "updates", LOCK_FILENAME);
|
|
36393
36924
|
}
|
|
36394
36925
|
function isStaleLock(lock, now, isProcessAlive2) {
|
|
36395
36926
|
const age = now - lock.startedAt;
|
|
@@ -36401,7 +36932,7 @@ async function readLockFile(shipyardHome) {
|
|
|
36401
36932
|
const path5 = lockPath(shipyardHome);
|
|
36402
36933
|
let raw;
|
|
36403
36934
|
try {
|
|
36404
|
-
raw = await
|
|
36935
|
+
raw = await readFile27(path5, "utf-8");
|
|
36405
36936
|
} catch {
|
|
36406
36937
|
return null;
|
|
36407
36938
|
}
|
|
@@ -36418,7 +36949,7 @@ async function readLockFileWithOutcome(shipyardHome) {
|
|
|
36418
36949
|
const path5 = lockPath(shipyardHome);
|
|
36419
36950
|
let raw;
|
|
36420
36951
|
try {
|
|
36421
|
-
raw = await
|
|
36952
|
+
raw = await readFile27(path5, "utf-8");
|
|
36422
36953
|
} catch (err3) {
|
|
36423
36954
|
if (isEnoent(err3)) return { kind: "absent" };
|
|
36424
36955
|
return { kind: "read-error", err: err3 };
|
|
@@ -36436,7 +36967,7 @@ async function readLockFileWithOutcome(shipyardHome) {
|
|
|
36436
36967
|
async function unlinkStaleLock(path5, expected) {
|
|
36437
36968
|
let current;
|
|
36438
36969
|
try {
|
|
36439
|
-
const raw = await
|
|
36970
|
+
const raw = await readFile27(path5, "utf-8");
|
|
36440
36971
|
const parsed = LockFileSchema.safeParse(JSON.parse(raw));
|
|
36441
36972
|
current = parsed.success ? parsed.data : null;
|
|
36442
36973
|
} catch (err3) {
|
|
@@ -36473,7 +37004,7 @@ async function resolveEexist(shipyardHome, path5, ownerPid, now, isProcessAlive2
|
|
|
36473
37004
|
}
|
|
36474
37005
|
async function tryAcquireLockExclusive(shipyardHome, pid, now, isProcessAlive2) {
|
|
36475
37006
|
const path5 = lockPath(shipyardHome);
|
|
36476
|
-
await
|
|
37007
|
+
await mkdir17(dirname21(path5), { recursive: true });
|
|
36477
37008
|
const body = JSON.stringify({ pid, startedAt: now() });
|
|
36478
37009
|
while (true) {
|
|
36479
37010
|
try {
|
|
@@ -36512,7 +37043,7 @@ async function cleanupUpdatesDir(shipyardHome, deps = {}) {
|
|
|
36512
37043
|
const now = deps.now ?? Date.now;
|
|
36513
37044
|
const isProcessAlive2 = deps.isProcessAlive ?? defaultIsProcessAlive;
|
|
36514
37045
|
const log = deps.log;
|
|
36515
|
-
const updatesDir =
|
|
37046
|
+
const updatesDir = join48(shipyardHome, "updates");
|
|
36516
37047
|
const empty = {
|
|
36517
37048
|
tarballsKept: [],
|
|
36518
37049
|
tarballsDeleted: [],
|
|
@@ -36636,7 +37167,7 @@ async function sortByMtimeDesc(baseDir, names, log) {
|
|
|
36636
37167
|
const rows = [];
|
|
36637
37168
|
for (const name of names) {
|
|
36638
37169
|
try {
|
|
36639
|
-
const s2 = await stat13(
|
|
37170
|
+
const s2 = await stat13(join48(baseDir, name));
|
|
36640
37171
|
rows.push({ name, mtimeMs: s2.mtimeMs });
|
|
36641
37172
|
} catch (err3) {
|
|
36642
37173
|
log?.info({ err: err3, entry: name }, "updates cleanup: stat failed, skipping entry");
|
|
@@ -36649,7 +37180,7 @@ async function filterOlderThan(baseDir, names, nowMs, thresholdMs, log) {
|
|
|
36649
37180
|
const out = [];
|
|
36650
37181
|
for (const name of names) {
|
|
36651
37182
|
try {
|
|
36652
|
-
const s2 = await stat13(
|
|
37183
|
+
const s2 = await stat13(join48(baseDir, name));
|
|
36653
37184
|
if (nowMs - s2.mtimeMs > thresholdMs) {
|
|
36654
37185
|
out.push(name);
|
|
36655
37186
|
}
|
|
@@ -36663,7 +37194,7 @@ async function deleteFiles(baseDir, names, log) {
|
|
|
36663
37194
|
const deleted = [];
|
|
36664
37195
|
for (const name of names) {
|
|
36665
37196
|
try {
|
|
36666
|
-
await unlink11(
|
|
37197
|
+
await unlink11(join48(baseDir, name));
|
|
36667
37198
|
deleted.push(name);
|
|
36668
37199
|
} catch (err3) {
|
|
36669
37200
|
if (isEnoent(err3)) {
|
|
@@ -36679,7 +37210,7 @@ async function deleteDirs(baseDir, names, log) {
|
|
|
36679
37210
|
const deleted = [];
|
|
36680
37211
|
for (const name of names) {
|
|
36681
37212
|
try {
|
|
36682
|
-
await rm10(
|
|
37213
|
+
await rm10(join48(baseDir, name), { recursive: true, force: true });
|
|
36683
37214
|
deleted.push(name);
|
|
36684
37215
|
} catch (err3) {
|
|
36685
37216
|
log?.info({ err: err3, entry: name }, "updates cleanup: rm -rf failed");
|
|
@@ -36688,7 +37219,7 @@ async function deleteDirs(baseDir, names, log) {
|
|
|
36688
37219
|
return deleted;
|
|
36689
37220
|
}
|
|
36690
37221
|
async function maybeClearLock(shipyardHome, nowMs, isProcessAlive2, log) {
|
|
36691
|
-
const lockFilePath =
|
|
37222
|
+
const lockFilePath = join48(shipyardHome, "updates", LOCK_FILENAME);
|
|
36692
37223
|
const outcome = await readLockFileWithOutcome(shipyardHome);
|
|
36693
37224
|
switch (outcome.kind) {
|
|
36694
37225
|
case "absent":
|
|
@@ -37992,8 +38523,8 @@ function wireDevServersAndTerminalsResolvers(deps) {
|
|
|
37992
38523
|
}
|
|
37993
38524
|
|
|
37994
38525
|
// src/services/file-resource-resolver.ts
|
|
37995
|
-
import { readFile as
|
|
37996
|
-
import { basename as basename8, join as
|
|
38526
|
+
import { readFile as readFile28, stat as stat14 } from "fs/promises";
|
|
38527
|
+
import { basename as basename8, join as join49, normalize as normalize4, sep as sep2 } from "path";
|
|
37997
38528
|
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
37998
38529
|
var MIME_BY_EXT2 = {
|
|
37999
38530
|
".ts": "text/typescript",
|
|
@@ -38062,7 +38593,7 @@ function createFileResourceResolver(deps) {
|
|
|
38062
38593
|
const { taskId, relativePath } = parseFileUri2(uri);
|
|
38063
38594
|
const cwd = deps.getTaskCwd(taskId) ?? deps.workspaceRoot;
|
|
38064
38595
|
const normalizedCwd = normalize4(cwd);
|
|
38065
|
-
const absolutePath = normalize4(
|
|
38596
|
+
const absolutePath = normalize4(join49(cwd, relativePath));
|
|
38066
38597
|
const cwdPrefix = normalizedCwd.endsWith(sep2) ? normalizedCwd : `${normalizedCwd}${sep2}`;
|
|
38067
38598
|
if (!absolutePath.startsWith(cwdPrefix) && absolutePath !== normalizedCwd) {
|
|
38068
38599
|
throw new Error(`Path traversal rejected: ${relativePath}`);
|
|
@@ -38080,14 +38611,14 @@ function createFileResourceResolver(deps) {
|
|
|
38080
38611
|
};
|
|
38081
38612
|
}
|
|
38082
38613
|
if (isImageExtension(absolutePath)) {
|
|
38083
|
-
const buffer2 = await
|
|
38614
|
+
const buffer2 = await readFile28(absolutePath);
|
|
38084
38615
|
return {
|
|
38085
38616
|
uri,
|
|
38086
38617
|
mimeType: guessMimeType(absolutePath),
|
|
38087
38618
|
blob: buffer2.toString("base64")
|
|
38088
38619
|
};
|
|
38089
38620
|
}
|
|
38090
|
-
const buffer = await
|
|
38621
|
+
const buffer = await readFile28(absolutePath);
|
|
38091
38622
|
if (looksLikeBinary(buffer)) {
|
|
38092
38623
|
return {
|
|
38093
38624
|
uri,
|
|
@@ -38106,8 +38637,8 @@ function createFileResourceResolver(deps) {
|
|
|
38106
38637
|
|
|
38107
38638
|
// src/services/git-checkpoint.ts
|
|
38108
38639
|
import { execFile as execFileCb2 } from "child_process";
|
|
38109
|
-
import { readFile as
|
|
38110
|
-
import { join as
|
|
38640
|
+
import { readFile as readFile29, stat as stat15, unlink as unlink12, writeFile as writeFile18 } from "fs/promises";
|
|
38641
|
+
import { join as join50 } from "path";
|
|
38111
38642
|
import { promisify as promisify8 } from "util";
|
|
38112
38643
|
var execFile9 = promisify8(execFileCb2);
|
|
38113
38644
|
var NOT_A_GIT_REPO = "Not a git repository";
|
|
@@ -38119,10 +38650,10 @@ async function resolveGitPaths(repoDir, taskId) {
|
|
|
38119
38650
|
try {
|
|
38120
38651
|
const { stdout } = await execFile9("git", ["rev-parse", "--git-dir"], { cwd: repoDir });
|
|
38121
38652
|
const gitDir = stdout.trim();
|
|
38122
|
-
const absoluteGitDir = gitDir.startsWith("/") ? gitDir :
|
|
38653
|
+
const absoluteGitDir = gitDir.startsWith("/") ? gitDir : join50(repoDir, gitDir);
|
|
38123
38654
|
return {
|
|
38124
38655
|
gitDir: absoluteGitDir,
|
|
38125
|
-
shadowIndex:
|
|
38656
|
+
shadowIndex: join50(absoluteGitDir, `shipyard-index-${taskId}`)
|
|
38126
38657
|
};
|
|
38127
38658
|
} catch {
|
|
38128
38659
|
return null;
|
|
@@ -38217,7 +38748,7 @@ async function rewindToCheckpointImpl(repoDir, taskId, turnNo) {
|
|
|
38217
38748
|
const untrackedFiles = untrackedRaw.trim().split("\n").filter((f2) => f2.length > 0);
|
|
38218
38749
|
for (const file of untrackedFiles) {
|
|
38219
38750
|
try {
|
|
38220
|
-
const fullPath =
|
|
38751
|
+
const fullPath = join50(repoDir, file);
|
|
38221
38752
|
const s2 = await stat15(fullPath);
|
|
38222
38753
|
if (s2.isFile()) {
|
|
38223
38754
|
await unlink12(fullPath);
|
|
@@ -38484,7 +39015,7 @@ async function revertSingleFile(repoDir, file, restoreCommit, mode) {
|
|
|
38484
39015
|
const isDeletedByAgent = file.status === "deleted";
|
|
38485
39016
|
const shouldDelete = mode === "revert" && isAddedByAgent || mode === "unrevert" && isDeletedByAgent;
|
|
38486
39017
|
if (shouldDelete) {
|
|
38487
|
-
await unlink12(
|
|
39018
|
+
await unlink12(join50(repoDir, file.path)).catch((err3) => {
|
|
38488
39019
|
if (!isEnoent(err3)) throw err3;
|
|
38489
39020
|
});
|
|
38490
39021
|
await execFile9("git", ["reset", "HEAD", "--", file.path], { cwd: repoDir }).catch(() => {
|
|
@@ -38496,7 +39027,7 @@ async function revertSingleFile(repoDir, file, restoreCommit, mode) {
|
|
|
38496
39027
|
maxBuffer: 10 * 1024 * 1024,
|
|
38497
39028
|
encoding: "buffer"
|
|
38498
39029
|
});
|
|
38499
|
-
await writeFile18(
|
|
39030
|
+
await writeFile18(join50(repoDir, file.path), stdout);
|
|
38500
39031
|
await execFile9("git", ["reset", "HEAD", "--", file.path], { cwd: repoDir }).catch(() => {
|
|
38501
39032
|
});
|
|
38502
39033
|
return true;
|
|
@@ -38589,7 +39120,7 @@ async function diffCheckpointToWorkingTreeImpl(repoDir, taskId, fromTurnNo, scop
|
|
|
38589
39120
|
if (!line || statusMap.has(line)) continue;
|
|
38590
39121
|
let ins = 0;
|
|
38591
39122
|
try {
|
|
38592
|
-
ins = countLines(await
|
|
39123
|
+
ins = countLines(await readFile29(join50(repoDir, line), "utf-8"));
|
|
38593
39124
|
} catch {
|
|
38594
39125
|
}
|
|
38595
39126
|
entries.push({ path: line, status: "added", insertions: ins, deletions: 0 });
|
|
@@ -38609,7 +39140,7 @@ async function getFileVsWorkingTreeImpl(repoDir, taskId, fromTurnNo, filePath) {
|
|
|
38609
39140
|
if (from === "error") return null;
|
|
38610
39141
|
let modifiedContent;
|
|
38611
39142
|
try {
|
|
38612
|
-
modifiedContent = await
|
|
39143
|
+
modifiedContent = await readFile29(join50(repoDir, filePath), "utf-8");
|
|
38613
39144
|
} catch {
|
|
38614
39145
|
modifiedContent = "";
|
|
38615
39146
|
}
|
|
@@ -38706,8 +39237,8 @@ function unregisterSubprocessEpoch(taskId, registry = subprocessEpochs) {
|
|
|
38706
39237
|
|
|
38707
39238
|
// src/services/harness/deliverable-server.ts
|
|
38708
39239
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
38709
|
-
import { copyFile, rm as fsRm, stat as fsStat3, mkdir as
|
|
38710
|
-
import { basename as basename9, extname as extname2, join as
|
|
39240
|
+
import { copyFile, rm as fsRm, stat as fsStat3, mkdir as mkdir18 } from "fs/promises";
|
|
39241
|
+
import { basename as basename9, extname as extname2, join as join51, resolve as resolve8, sep as sep3 } from "path";
|
|
38711
39242
|
var TOOL_DESCRIPTION2 = [
|
|
38712
39243
|
"Register a proof-of-work deliverable for this task \u2014 proof a reviewer cannot get from the diff alone.",
|
|
38713
39244
|
"",
|
|
@@ -38771,7 +39302,7 @@ var RegisterDeliverableInput = {
|
|
|
38771
39302
|
};
|
|
38772
39303
|
var COPY_MAX_BYTES = 50 * 1024 * 1024;
|
|
38773
39304
|
function isInternedFile(dataDir, filePath) {
|
|
38774
|
-
const internBase = resolve8(
|
|
39305
|
+
const internBase = resolve8(join51(dataDir, "deliverable-files")) + sep3;
|
|
38775
39306
|
return resolve8(filePath).startsWith(internBase);
|
|
38776
39307
|
}
|
|
38777
39308
|
async function internDeliverableFile(deliverableId, taskId, filePath, storedSizeBytes, dataDir, prevInternedPath) {
|
|
@@ -38795,10 +39326,10 @@ async function internDeliverableFile(deliverableId, taskId, filePath, storedSize
|
|
|
38795
39326
|
};
|
|
38796
39327
|
}
|
|
38797
39328
|
const ext = extname2(filePath);
|
|
38798
|
-
const destDir =
|
|
38799
|
-
const destPath =
|
|
39329
|
+
const destDir = join51(dataDir, "deliverable-files", taskId);
|
|
39330
|
+
const destPath = join51(destDir, `${deliverableId}${ext}`);
|
|
38800
39331
|
try {
|
|
38801
|
-
await
|
|
39332
|
+
await mkdir18(destDir, { recursive: true });
|
|
38802
39333
|
await copyFile(filePath, destPath);
|
|
38803
39334
|
} catch (err3) {
|
|
38804
39335
|
return {
|
|
@@ -39221,8 +39752,8 @@ function createDeliverableTools(ctx) {
|
|
|
39221
39752
|
}
|
|
39222
39753
|
|
|
39223
39754
|
// src/services/harness/plugin-server.ts
|
|
39224
|
-
import { mkdir as
|
|
39225
|
-
import { join as
|
|
39755
|
+
import { mkdir as mkdir19, writeFile as writeFile19 } from "fs/promises";
|
|
39756
|
+
import { join as join52 } from "path";
|
|
39226
39757
|
|
|
39227
39758
|
// src/services/harness/sandbox-docs.ts
|
|
39228
39759
|
var SANDBOX_HTML_RULES = `## HTML Rules
|
|
@@ -39851,14 +40382,14 @@ ${addendum}`;
|
|
|
39851
40382
|
events: input.events,
|
|
39852
40383
|
provideResources: input.provideResources
|
|
39853
40384
|
});
|
|
39854
|
-
const pluginDir =
|
|
39855
|
-
await
|
|
39856
|
-
await writeFile19(
|
|
40385
|
+
const pluginDir = join52(ctx.pluginsDir, input.pluginId);
|
|
40386
|
+
await mkdir19(pluginDir, { recursive: true });
|
|
40387
|
+
await writeFile19(join52(pluginDir, "template.html"), input.template, "utf-8");
|
|
39857
40388
|
if (input.handler) {
|
|
39858
|
-
await writeFile19(
|
|
40389
|
+
await writeFile19(join52(pluginDir, "handler.mjs"), input.handler, "utf-8");
|
|
39859
40390
|
}
|
|
39860
40391
|
await writeFile19(
|
|
39861
|
-
|
|
40392
|
+
join52(pluginDir, "plugin.json"),
|
|
39862
40393
|
JSON.stringify(manifest, null, 2),
|
|
39863
40394
|
"utf-8"
|
|
39864
40395
|
);
|
|
@@ -40595,7 +41126,7 @@ function errorResult(message) {
|
|
|
40595
41126
|
}
|
|
40596
41127
|
|
|
40597
41128
|
// src/services/harness/visualize-server.ts
|
|
40598
|
-
import { readFile as
|
|
41129
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
40599
41130
|
import * as net from "net";
|
|
40600
41131
|
|
|
40601
41132
|
// src/services/harness/html-validator.ts
|
|
@@ -49358,7 +49889,7 @@ async function resolveContent(inlineContent, filePath) {
|
|
|
49358
49889
|
}
|
|
49359
49890
|
if (filePath) {
|
|
49360
49891
|
try {
|
|
49361
|
-
return { ok: true, content: await
|
|
49892
|
+
return { ok: true, content: await readFile30(filePath, "utf-8") };
|
|
49362
49893
|
} catch (err3) {
|
|
49363
49894
|
const msg = err3 instanceof Error ? err3.message : String(err3);
|
|
49364
49895
|
return { ok: false, error: `Failed to read file: ${msg}` };
|
|
@@ -49440,7 +49971,7 @@ function textResult3(text, isError) {
|
|
|
49440
49971
|
}
|
|
49441
49972
|
async function handlePresentInline(filePath, vizType) {
|
|
49442
49973
|
try {
|
|
49443
|
-
const content = await
|
|
49974
|
+
const content = await readFile30(filePath, "utf-8");
|
|
49444
49975
|
return {
|
|
49445
49976
|
content: [
|
|
49446
49977
|
{ type: "text", text: content },
|
|
@@ -49480,7 +50011,7 @@ async function refreshCanvasPresentationFromFile(vizWatcher, slug) {
|
|
|
49480
50011
|
if (!viz) return { ok: false, title: slug, reason: "no_canvas_element" };
|
|
49481
50012
|
if (!viz.canvasElementId) return { ok: false, title: viz.title, reason: "no_canvas_element" };
|
|
49482
50013
|
try {
|
|
49483
|
-
const content = await
|
|
50014
|
+
const content = await readFile30(viz.filePath, "utf-8");
|
|
49484
50015
|
const effects = vizWatcher.registry.refreshCanvasPresentation(slug, content);
|
|
49485
50016
|
await vizWatcher.executeEffects(effects);
|
|
49486
50017
|
return { ok: true, title: viz.title };
|
|
@@ -49567,7 +50098,7 @@ Fix the content and try again.`
|
|
|
49567
50098
|
await ctx.vizWatcher.executeEffects([
|
|
49568
50099
|
{ type: "write_file", filePath: existingFilePath, content }
|
|
49569
50100
|
]);
|
|
49570
|
-
const persisted = await
|
|
50101
|
+
const persisted = await readFile30(existingFilePath, "utf-8").catch((err3) => {
|
|
49571
50102
|
ctx.log({
|
|
49572
50103
|
event: "viz_update_reconcile_read_failed",
|
|
49573
50104
|
filePath: existingFilePath,
|
|
@@ -50085,7 +50616,7 @@ import { pathToFileURL as pathToFileURL3 } from "url";
|
|
|
50085
50616
|
// src/services/lsp/language-server-registry.ts
|
|
50086
50617
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
50087
50618
|
import { createRequire as createRequire3 } from "module";
|
|
50088
|
-
import { dirname as
|
|
50619
|
+
import { dirname as dirname22, isAbsolute as isAbsolute4, join as join53 } from "path";
|
|
50089
50620
|
function decideTypescriptServer(d) {
|
|
50090
50621
|
if (d.hasAngular || d.hasNest || d.hasTsserverPlugins) return "vtsls";
|
|
50091
50622
|
return "tsgo";
|
|
@@ -50175,11 +50706,11 @@ function isRecord7(value) {
|
|
|
50175
50706
|
return typeof value === "object" && value !== null;
|
|
50176
50707
|
}
|
|
50177
50708
|
function gatherTsDetection(cwd, deps) {
|
|
50178
|
-
const pkg = deps.readJsonFile(
|
|
50709
|
+
const pkg = deps.readJsonFile(join53(cwd, "package.json"));
|
|
50179
50710
|
const depNames = isRecord7(pkg) ? [...keysOf(pkg.dependencies), ...keysOf(pkg.devDependencies)] : [];
|
|
50180
|
-
const hasAngular = depNames.includes("@angular/core") || deps.fileExists(
|
|
50181
|
-
const hasNest = depNames.includes("@nestjs/core") || deps.fileExists(
|
|
50182
|
-
const tsconfig = deps.readJsonFile(
|
|
50711
|
+
const hasAngular = depNames.includes("@angular/core") || deps.fileExists(join53(cwd, "angular.json"));
|
|
50712
|
+
const hasNest = depNames.includes("@nestjs/core") || deps.fileExists(join53(cwd, "nest-cli.json"));
|
|
50713
|
+
const tsconfig = deps.readJsonFile(join53(cwd, "tsconfig.json"));
|
|
50183
50714
|
const compilerOptions = isRecord7(tsconfig) ? tsconfig.compilerOptions : void 0;
|
|
50184
50715
|
const plugins2 = isRecord7(compilerOptions) ? compilerOptions.plugins : void 0;
|
|
50185
50716
|
const hasTsserverPlugins = Array.isArray(plugins2) && plugins2.length > 0;
|
|
@@ -50194,11 +50725,11 @@ function detectPythonVenv(cwd, deps) {
|
|
|
50194
50725
|
if (envVenv && isAbsolute4(envVenv) && deps.fileExists(envVenv)) {
|
|
50195
50726
|
candidates.push(envVenv);
|
|
50196
50727
|
}
|
|
50197
|
-
candidates.push(
|
|
50728
|
+
candidates.push(join53(cwd, ".venv"), join53(cwd, "venv"));
|
|
50198
50729
|
const isWin = deps.platform === "win32";
|
|
50199
50730
|
for (const root of candidates) {
|
|
50200
|
-
const python = isWin ?
|
|
50201
|
-
const python3 = isWin ? python :
|
|
50731
|
+
const python = isWin ? join53(root, "Scripts", "python.exe") : join53(root, "bin", "python");
|
|
50732
|
+
const python3 = isWin ? python : join53(root, "bin", "python3");
|
|
50202
50733
|
if (deps.fileExists(python) || deps.fileExists(python3)) return root;
|
|
50203
50734
|
}
|
|
50204
50735
|
return null;
|
|
@@ -50231,8 +50762,8 @@ function resolveTsgoBinaryPath(req) {
|
|
|
50231
50762
|
`Unable to resolve ${platformPkg}: ${err3 instanceof Error ? err3.message : String(err3)}`
|
|
50232
50763
|
);
|
|
50233
50764
|
}
|
|
50234
|
-
const exeDir =
|
|
50235
|
-
const exe = process.platform === "win32" ?
|
|
50765
|
+
const exeDir = join53(dirname22(pkgJsonPath), "lib");
|
|
50766
|
+
const exe = process.platform === "win32" ? join53(exeDir, "tsgo.exe") : join53(exeDir, "tsgo");
|
|
50236
50767
|
if (!existsSync7(exe)) {
|
|
50237
50768
|
throw new Error(`tsgo native binary not found: ${exe}`);
|
|
50238
50769
|
}
|
|
@@ -50245,7 +50776,7 @@ function resolveBinViaPackageJson(pkg, binName, req) {
|
|
|
50245
50776
|
const named = isRecord7(binField) ? binField[binName] : void 0;
|
|
50246
50777
|
const bin = typeof binField === "string" ? binField : typeof named === "string" ? named : void 0;
|
|
50247
50778
|
if (!bin) throw new Error(`package '${pkg}' has no bin entry '${binName}'`);
|
|
50248
|
-
return
|
|
50779
|
+
return join53(dirname22(pkgJsonPath), bin);
|
|
50249
50780
|
}
|
|
50250
50781
|
function errorMessage3(err3) {
|
|
50251
50782
|
return err3 instanceof Error ? err3.message : String(err3);
|
|
@@ -51400,6 +51931,7 @@ function assertNever6(x2, context) {
|
|
|
51400
51931
|
|
|
51401
51932
|
// src/services/mcp/server-fsm.ts
|
|
51402
51933
|
var CONNECT_TIMEOUT_MS = 3e4;
|
|
51934
|
+
var COLD_CONNECT_TIMEOUT_MS = 12e4;
|
|
51403
51935
|
function snapshotToStatusEntry(snap) {
|
|
51404
51936
|
const entry = {
|
|
51405
51937
|
name: snap.name,
|
|
@@ -51427,7 +51959,22 @@ function initialSnapshot(name, startEnabled) {
|
|
|
51427
51959
|
config: null,
|
|
51428
51960
|
toolCount: 0,
|
|
51429
51961
|
serverInfo: null,
|
|
51430
|
-
error: null
|
|
51962
|
+
error: null,
|
|
51963
|
+
connectAttemptCount: startEnabled ? 1 : 0
|
|
51964
|
+
};
|
|
51965
|
+
}
|
|
51966
|
+
function connectTimeoutMsForAttempt(attemptCount) {
|
|
51967
|
+
return attemptCount <= 1 ? COLD_CONNECT_TIMEOUT_MS : CONNECT_TIMEOUT_MS;
|
|
51968
|
+
}
|
|
51969
|
+
function formatConnectTimeoutError(ms) {
|
|
51970
|
+
return `Connect timeout (${Math.round(ms / 1e3)}s)`;
|
|
51971
|
+
}
|
|
51972
|
+
function enterConnecting(snap) {
|
|
51973
|
+
return {
|
|
51974
|
+
...snap,
|
|
51975
|
+
state: "connecting",
|
|
51976
|
+
error: null,
|
|
51977
|
+
connectAttemptCount: snap.connectAttemptCount + 1
|
|
51431
51978
|
};
|
|
51432
51979
|
}
|
|
51433
51980
|
function illegal(state, eventType) {
|
|
@@ -51452,17 +51999,16 @@ function transition3(snap, event) {
|
|
|
51452
51999
|
function fromDisabled(snap, event) {
|
|
51453
52000
|
switch (event.type) {
|
|
51454
52001
|
case "enable": {
|
|
51455
|
-
const next = {
|
|
51456
|
-
...snap,
|
|
51457
|
-
state: "connecting",
|
|
51458
|
-
config: event.config,
|
|
51459
|
-
error: null
|
|
51460
|
-
};
|
|
52002
|
+
const next = { ...enterConnecting(snap), config: event.config };
|
|
51461
52003
|
return {
|
|
51462
52004
|
snapshot: next,
|
|
51463
52005
|
effects: [
|
|
51464
52006
|
{ type: "OpenTransport", name: snap.name, config: event.config },
|
|
51465
|
-
{
|
|
52007
|
+
{
|
|
52008
|
+
type: "StartConnectTimer",
|
|
52009
|
+
name: snap.name,
|
|
52010
|
+
ms: connectTimeoutMsForAttempt(next.connectAttemptCount)
|
|
52011
|
+
},
|
|
51466
52012
|
{ type: "BroadcastStatus", entry: snapshotToStatusEntry(next) }
|
|
51467
52013
|
]
|
|
51468
52014
|
};
|
|
@@ -51555,7 +52101,7 @@ function fromConnecting(snap, event) {
|
|
|
51555
52101
|
const next = {
|
|
51556
52102
|
...snap,
|
|
51557
52103
|
state: "failed",
|
|
51558
|
-
error:
|
|
52104
|
+
error: formatConnectTimeoutError(event.ms)
|
|
51559
52105
|
};
|
|
51560
52106
|
return {
|
|
51561
52107
|
snapshot: next,
|
|
@@ -51667,16 +52213,16 @@ function fromNeedsAuth(snap, event) {
|
|
|
51667
52213
|
if (snap.config === null) {
|
|
51668
52214
|
throw new Error(`MCP FSM: reauth_completed for "${snap.name}" without retained config`);
|
|
51669
52215
|
}
|
|
51670
|
-
const next =
|
|
51671
|
-
...snap,
|
|
51672
|
-
state: "connecting",
|
|
51673
|
-
error: null
|
|
51674
|
-
};
|
|
52216
|
+
const next = enterConnecting(snap);
|
|
51675
52217
|
return {
|
|
51676
52218
|
snapshot: next,
|
|
51677
52219
|
effects: [
|
|
51678
52220
|
{ type: "OpenTransport", name: snap.name, config: snap.config },
|
|
51679
|
-
{
|
|
52221
|
+
{
|
|
52222
|
+
type: "StartConnectTimer",
|
|
52223
|
+
name: snap.name,
|
|
52224
|
+
ms: connectTimeoutMsForAttempt(next.connectAttemptCount)
|
|
52225
|
+
},
|
|
51680
52226
|
{ type: "EmitReauthCompleted", name: snap.name },
|
|
51681
52227
|
{ type: "BroadcastStatus", entry: snapshotToStatusEntry(next) }
|
|
51682
52228
|
]
|
|
@@ -51715,17 +52261,16 @@ function fromFailed(snap, event) {
|
|
|
51715
52261
|
};
|
|
51716
52262
|
}
|
|
51717
52263
|
case "enable": {
|
|
51718
|
-
const next = {
|
|
51719
|
-
...snap,
|
|
51720
|
-
state: "connecting",
|
|
51721
|
-
config: event.config,
|
|
51722
|
-
error: null
|
|
51723
|
-
};
|
|
52264
|
+
const next = { ...enterConnecting(snap), config: event.config };
|
|
51724
52265
|
return {
|
|
51725
52266
|
snapshot: next,
|
|
51726
52267
|
effects: [
|
|
51727
52268
|
{ type: "OpenTransport", name: snap.name, config: event.config },
|
|
51728
|
-
{
|
|
52269
|
+
{
|
|
52270
|
+
type: "StartConnectTimer",
|
|
52271
|
+
name: snap.name,
|
|
52272
|
+
ms: connectTimeoutMsForAttempt(next.connectAttemptCount)
|
|
52273
|
+
},
|
|
51729
52274
|
{ type: "BroadcastStatus", entry: snapshotToStatusEntry(next) }
|
|
51730
52275
|
]
|
|
51731
52276
|
};
|
|
@@ -52118,7 +52663,7 @@ var ConnectTimer = class {
|
|
|
52118
52663
|
const set = this.#deps.setTimeoutFn ?? setTimeout;
|
|
52119
52664
|
const handle = set(() => {
|
|
52120
52665
|
this.#handles.delete(name);
|
|
52121
|
-
this.#deps.onTimeout(name);
|
|
52666
|
+
this.#deps.onTimeout(name, ms);
|
|
52122
52667
|
}, ms);
|
|
52123
52668
|
if (hasUnref(handle)) handle.unref();
|
|
52124
52669
|
this.#handles.set(name, handle);
|
|
@@ -52182,7 +52727,6 @@ var BOOTSTRAP_OPEN_JITTER_BASE_MS = 100;
|
|
|
52182
52727
|
var BOOTSTRAP_OPEN_JITTER_MS = 500;
|
|
52183
52728
|
var OPEN_TRANSPORT_BURST_WINDOW_MS = 200;
|
|
52184
52729
|
var STDIO_NO_AGENT_SENTINEL_MS = 5 * 6e4;
|
|
52185
|
-
var CONNECT_TIMEOUT_MS2 = 3e4;
|
|
52186
52730
|
var McpCoordinator = class {
|
|
52187
52731
|
#deps;
|
|
52188
52732
|
#registry;
|
|
@@ -52225,8 +52769,8 @@ var McpCoordinator = class {
|
|
|
52225
52769
|
} : void 0;
|
|
52226
52770
|
this.#dedup = new TerminalReconnectDedup(deps.log, gatedSendUserActionRequired);
|
|
52227
52771
|
this.#timer = new ConnectTimer({
|
|
52228
|
-
onTimeout: (name) => {
|
|
52229
|
-
this.#registry.dispatch(name, { type: "timeout" });
|
|
52772
|
+
onTimeout: (name, ms) => {
|
|
52773
|
+
this.#registry.dispatch(name, { type: "timeout", ms });
|
|
52230
52774
|
},
|
|
52231
52775
|
...deps.setTimeoutFn ? { setTimeoutFn: deps.setTimeoutFn } : {},
|
|
52232
52776
|
...deps.clearTimeoutFn ? { clearTimeoutFn: deps.clearTimeoutFn } : {}
|
|
@@ -52765,7 +53309,7 @@ var McpCoordinator = class {
|
|
|
52765
53309
|
}
|
|
52766
53310
|
for (const snap of this.#snapshots()) {
|
|
52767
53311
|
if (snap.state === "connecting" && snap.config?.transport === "stdio") {
|
|
52768
|
-
this.#timer.start(snap.name,
|
|
53312
|
+
this.#timer.start(snap.name, CONNECT_TIMEOUT_MS);
|
|
52769
53313
|
}
|
|
52770
53314
|
}
|
|
52771
53315
|
for (const snap of this.#snapshots()) {
|
|
@@ -53109,9 +53653,9 @@ import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
|
53109
53653
|
// src/shared/mcp/codex-credentials.ts
|
|
53110
53654
|
import { execFile as execFileCb3 } from "child_process";
|
|
53111
53655
|
import { createHash as createHash3 } from "crypto";
|
|
53112
|
-
import { readFile as
|
|
53656
|
+
import { readFile as readFile31 } from "fs/promises";
|
|
53113
53657
|
import { homedir as homedir7 } from "os";
|
|
53114
|
-
import { join as
|
|
53658
|
+
import { join as join54 } from "path";
|
|
53115
53659
|
import { promisify as promisify9 } from "util";
|
|
53116
53660
|
var execFile10 = promisify9(execFileCb3);
|
|
53117
53661
|
var CodexCredentialsEntrySchema = external_exports.object({
|
|
@@ -53172,10 +53716,10 @@ async function readKeychainStore() {
|
|
|
53172
53716
|
return null;
|
|
53173
53717
|
}
|
|
53174
53718
|
}
|
|
53175
|
-
async function readCodexMcpCredentials(serverName, serverUrl, codexHome =
|
|
53719
|
+
async function readCodexMcpCredentials(serverName, serverUrl, codexHome = join54(homedir7(), ".codex")) {
|
|
53176
53720
|
if (!serverUrl) return null;
|
|
53177
53721
|
try {
|
|
53178
|
-
const raw = await
|
|
53722
|
+
const raw = await readFile31(join54(codexHome, FALLBACK_FILENAME), "utf-8");
|
|
53179
53723
|
const fileResult = CodexCredentialsFileSchema.safeParse(JSON.parse(raw));
|
|
53180
53724
|
if (fileResult.success) {
|
|
53181
53725
|
const entry = findEntry2(fileResult.data, serverName, serverUrl);
|
|
@@ -55676,9 +56220,9 @@ function startPlanDocPersistenceGapReporter(deps) {
|
|
|
55676
56220
|
}
|
|
55677
56221
|
|
|
55678
56222
|
// src/services/metrics/stall-profiler.ts
|
|
55679
|
-
import { mkdir as
|
|
56223
|
+
import { mkdir as mkdir20, readdir as readdir18, unlink as unlink13, writeFile as writeFile20 } from "fs/promises";
|
|
55680
56224
|
import { Session as Session2 } from "inspector/promises";
|
|
55681
|
-
import { join as
|
|
56225
|
+
import { join as join55 } from "path";
|
|
55682
56226
|
import { monitorEventLoopDelay as monitorEventLoopDelay3 } from "perf_hooks";
|
|
55683
56227
|
function hasProfile2(value) {
|
|
55684
56228
|
return typeof value === "object" && value !== null && "profile" in value;
|
|
@@ -55788,8 +56332,8 @@ var StallProfiler = class {
|
|
|
55788
56332
|
const isoTs = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
55789
56333
|
const stallRounded = Math.round(stallMs);
|
|
55790
56334
|
const filename = `stall-${isoTs}-${stallRounded}ms.cpuprofile`;
|
|
55791
|
-
const profilePath =
|
|
55792
|
-
await
|
|
56335
|
+
const profilePath = join55(this.#outDir, filename);
|
|
56336
|
+
await mkdir20(this.#outDir, { recursive: true });
|
|
55793
56337
|
await writeFile20(profilePath, JSON.stringify(stopResult.profile));
|
|
55794
56338
|
this.#log({
|
|
55795
56339
|
event: "stall_profile_captured",
|
|
@@ -55822,7 +56366,7 @@ var StallProfiler = class {
|
|
|
55822
56366
|
const entries = await readdir18(this.#outDir);
|
|
55823
56367
|
const toDelete = planProfileEviction(entries, this.#maxProfiles, STALL_PROFILE_PREFIX);
|
|
55824
56368
|
if (toDelete.length === 0) return;
|
|
55825
|
-
const results = await Promise.allSettled(toDelete.map((f2) => unlink13(
|
|
56369
|
+
const results = await Promise.allSettled(toDelete.map((f2) => unlink13(join55(this.#outDir, f2))));
|
|
55826
56370
|
const failures = results.filter((r) => r.status === "rejected").length;
|
|
55827
56371
|
if (failures > 0) {
|
|
55828
56372
|
this.#log({
|
|
@@ -66359,12 +66903,12 @@ function handleTaskStoreBroadcast(deps, event) {
|
|
|
66359
66903
|
}
|
|
66360
66904
|
|
|
66361
66905
|
// src/services/serve-factory/viz-preview.ts
|
|
66362
|
-
import { join as
|
|
66906
|
+
import { join as join58 } from "path";
|
|
66363
66907
|
|
|
66364
66908
|
// src/services/harness/visualization-file-watcher.ts
|
|
66365
66909
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
66366
|
-
import { mkdir as
|
|
66367
|
-
import { basename as basename10, dirname as
|
|
66910
|
+
import { mkdir as mkdir21, readFile as readFile32, rename as rename16, writeFile as writeFile21 } from "fs/promises";
|
|
66911
|
+
import { basename as basename10, dirname as dirname23, join as join56 } from "path";
|
|
66368
66912
|
var PREVIEW_DEFAULT_W = 1200;
|
|
66369
66913
|
var PREVIEW_DEFAULT_H = 800;
|
|
66370
66914
|
function previewDataToLoroValue(data) {
|
|
@@ -66417,7 +66961,7 @@ function mergedPreviewData(data, state) {
|
|
|
66417
66961
|
}
|
|
66418
66962
|
var DEBOUNCE_MS3 = 200;
|
|
66419
66963
|
async function atomicWrite2(filePath, content) {
|
|
66420
|
-
await
|
|
66964
|
+
await mkdir21(dirname23(filePath), { recursive: true });
|
|
66421
66965
|
const tmpPath = `${filePath}.${randomUUID16()}.tmp`;
|
|
66422
66966
|
await writeFile21(tmpPath, content, "utf-8");
|
|
66423
66967
|
await rename16(tmpPath, filePath);
|
|
@@ -66513,7 +67057,7 @@ var VisualizationFileWatcher = class {
|
|
|
66513
67057
|
const byTask = /* @__PURE__ */ new Map();
|
|
66514
67058
|
for (const viz of allViz) {
|
|
66515
67059
|
try {
|
|
66516
|
-
const content = await
|
|
67060
|
+
const content = await readFile32(viz.filePath, "utf-8");
|
|
66517
67061
|
let items = byTask.get(viz.taskId);
|
|
66518
67062
|
if (!items) {
|
|
66519
67063
|
items = [];
|
|
@@ -66576,7 +67120,7 @@ var VisualizationFileWatcher = class {
|
|
|
66576
67120
|
}
|
|
66577
67121
|
case "persist_registry":
|
|
66578
67122
|
await atomicWrite2(
|
|
66579
|
-
|
|
67123
|
+
join56(this.#deps.vizDir, effect.taskId, "registry.json"),
|
|
66580
67124
|
JSON.stringify(effect.data, null, 2)
|
|
66581
67125
|
);
|
|
66582
67126
|
break;
|
|
@@ -66588,7 +67132,7 @@ var VisualizationFileWatcher = class {
|
|
|
66588
67132
|
if (this.#disposed) return;
|
|
66589
67133
|
if (this.#watchedFiles.has(filePath)) return;
|
|
66590
67134
|
this.#watchedFiles.add(filePath);
|
|
66591
|
-
const dirPath =
|
|
67135
|
+
const dirPath = dirname23(filePath);
|
|
66592
67136
|
const existing = this.#dirWatchers.get(dirPath);
|
|
66593
67137
|
if (existing) {
|
|
66594
67138
|
existing.refCount += 1;
|
|
@@ -66602,7 +67146,7 @@ var VisualizationFileWatcher = class {
|
|
|
66602
67146
|
const findWatchedFile = (eventPath) => {
|
|
66603
67147
|
const eventBase = basename10(eventPath);
|
|
66604
67148
|
for (const wf of this.#watchedFiles) {
|
|
66605
|
-
if (
|
|
67149
|
+
if (dirname23(wf) === dirPath && basename10(wf) === eventBase) return wf;
|
|
66606
67150
|
}
|
|
66607
67151
|
return null;
|
|
66608
67152
|
};
|
|
@@ -66655,7 +67199,7 @@ var VisualizationFileWatcher = class {
|
|
|
66655
67199
|
}
|
|
66656
67200
|
#stopFileWatch(filePath) {
|
|
66657
67201
|
if (!this.#watchedFiles.delete(filePath)) return;
|
|
66658
|
-
const dirPath =
|
|
67202
|
+
const dirPath = dirname23(filePath);
|
|
66659
67203
|
const entry = this.#dirWatchers.get(dirPath);
|
|
66660
67204
|
if (entry) {
|
|
66661
67205
|
entry.refCount -= 1;
|
|
@@ -66777,7 +67321,7 @@ var VisualizationFileWatcher = class {
|
|
|
66777
67321
|
let content = effect.content;
|
|
66778
67322
|
if (!content && effect.filePath) {
|
|
66779
67323
|
try {
|
|
66780
|
-
content = await
|
|
67324
|
+
content = await readFile32(effect.filePath, "utf-8");
|
|
66781
67325
|
} catch {
|
|
66782
67326
|
content = "";
|
|
66783
67327
|
}
|
|
@@ -66823,7 +67367,7 @@ var VisualizationFileWatcher = class {
|
|
|
66823
67367
|
async #handleFileChange(filePath) {
|
|
66824
67368
|
let content;
|
|
66825
67369
|
try {
|
|
66826
|
-
content = await
|
|
67370
|
+
content = await readFile32(filePath, "utf-8");
|
|
66827
67371
|
} catch {
|
|
66828
67372
|
return;
|
|
66829
67373
|
}
|
|
@@ -66874,7 +67418,7 @@ var VisualizationFileWatcher = class {
|
|
|
66874
67418
|
|
|
66875
67419
|
// src/services/harness/visualization-registry.ts
|
|
66876
67420
|
import { createHash as createHash5 } from "crypto";
|
|
66877
|
-
import { join as
|
|
67421
|
+
import { join as join57 } from "path";
|
|
66878
67422
|
function hashContent(content) {
|
|
66879
67423
|
return createHash5("sha256").update(content).digest("hex");
|
|
66880
67424
|
}
|
|
@@ -66888,7 +67432,7 @@ var VisualizationRegistry = class {
|
|
|
66888
67432
|
return null;
|
|
66889
67433
|
}
|
|
66890
67434
|
const ext = vizFileExtension(vizType);
|
|
66891
|
-
const filePath =
|
|
67435
|
+
const filePath = join57(vizDir, taskId, `${slug}${ext}`);
|
|
66892
67436
|
const contentHash3 = hashContent(content);
|
|
66893
67437
|
const viz = {
|
|
66894
67438
|
slug,
|
|
@@ -67039,7 +67583,7 @@ var VisualizationRegistry = class {
|
|
|
67039
67583
|
// src/services/serve-factory/viz-preview.ts
|
|
67040
67584
|
function createVizPreviewRegistry(deps) {
|
|
67041
67585
|
const { canvasRepo } = deps;
|
|
67042
|
-
const vizDir =
|
|
67586
|
+
const vizDir = join58(deps.dataDir, "visualizations");
|
|
67043
67587
|
const vizWatchers = /* @__PURE__ */ new Map();
|
|
67044
67588
|
function getOrCreateVizWatcher(taskId) {
|
|
67045
67589
|
const existing = vizWatchers.get(taskId);
|
|
@@ -67138,7 +67682,7 @@ function createVizPreviewRegistry(deps) {
|
|
|
67138
67682
|
import { createHmac, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
67139
67683
|
import { promises as fs } from "fs";
|
|
67140
67684
|
import { createServer as createServer5 } from "net";
|
|
67141
|
-
import { dirname as
|
|
67685
|
+
import { dirname as dirname24 } from "path";
|
|
67142
67686
|
var NONCE_TTL_MS = 12e4;
|
|
67143
67687
|
var NONCE_CACHE_MAX = 1e5;
|
|
67144
67688
|
var CursorHookSocketServer = class {
|
|
@@ -67162,7 +67706,7 @@ var CursorHookSocketServer = class {
|
|
|
67162
67706
|
if (this.#hmacSecret.length === 0) {
|
|
67163
67707
|
throw new Error(`cursor-hook-socket: empty HMAC keyfile at ${this.#opts.hmacKeyfile}`);
|
|
67164
67708
|
}
|
|
67165
|
-
await fs.mkdir(
|
|
67709
|
+
await fs.mkdir(dirname24(this.#opts.socketPath), { recursive: true, mode: 448 });
|
|
67166
67710
|
await fs.rm(this.#opts.socketPath, { force: true });
|
|
67167
67711
|
const server = createServer5((conn) => {
|
|
67168
67712
|
this.#handleConnection(conn);
|
|
@@ -70347,7 +70891,7 @@ function createCursorVaultKeyCache() {
|
|
|
70347
70891
|
}
|
|
70348
70892
|
|
|
70349
70893
|
// src/services/session/skills/cursor-skill-body-resolver.ts
|
|
70350
|
-
import { readFile as
|
|
70894
|
+
import { readFile as readFile33 } from "fs/promises";
|
|
70351
70895
|
function skillKey(name, namespace) {
|
|
70352
70896
|
return namespace ? `${namespace}:${name}` : name;
|
|
70353
70897
|
}
|
|
@@ -70400,7 +70944,7 @@ function collectInvocations(blocks) {
|
|
|
70400
70944
|
return out;
|
|
70401
70945
|
}
|
|
70402
70946
|
async function readUtf8(path5) {
|
|
70403
|
-
return
|
|
70947
|
+
return readFile33(path5, "utf-8");
|
|
70404
70948
|
}
|
|
70405
70949
|
async function resolveCursorSkillBodiesFromTrustedSkills(blocks, skills, log, readSkillFile = readUtf8) {
|
|
70406
70950
|
const invocations = collectInvocations(blocks);
|
|
@@ -70513,7 +71057,7 @@ function buildCreationSkillDefaults(entries) {
|
|
|
70513
71057
|
|
|
70514
71058
|
// src/services/skills/skill-resolution-service.ts
|
|
70515
71059
|
import { mkdir as mkdir23, readdir as readdir19, readFile as readFile34, writeFile as writeFile23 } from "fs/promises";
|
|
70516
|
-
import { basename as basename11, dirname as dirname26, join as
|
|
71060
|
+
import { basename as basename11, dirname as dirname26, join as join62, relative as relative8, resolve as resolve11, sep as sep6 } from "path";
|
|
70517
71061
|
|
|
70518
71062
|
// src/services/skills/bundle-schema.ts
|
|
70519
71063
|
var SKILL_BUNDLE_VERSION = 2;
|
|
@@ -70641,196 +71185,10 @@ function migrateSkillBundle(raw) {
|
|
|
70641
71185
|
|
|
70642
71186
|
// src/services/skills/bundle-store.ts
|
|
70643
71187
|
import { createHash as createHash9 } from "crypto";
|
|
70644
|
-
import { join as
|
|
70645
|
-
|
|
70646
|
-
// src/services/storage/json-document-store.ts
|
|
70647
|
-
import { mkdir as mkdir21, readFile as readFile33 } from "fs/promises";
|
|
70648
|
-
import { dirname as dirname24 } from "path";
|
|
70649
|
-
var WRITE_DEBOUNCE_MS = 50;
|
|
70650
|
-
function applyMutations(records, mutations) {
|
|
70651
|
-
for (const [id, mutation] of mutations) {
|
|
70652
|
-
if (mutation.kind === "delete") {
|
|
70653
|
-
delete records[id];
|
|
70654
|
-
} else {
|
|
70655
|
-
records[id] = mutation.data;
|
|
70656
|
-
}
|
|
70657
|
-
}
|
|
70658
|
-
}
|
|
70659
|
-
function resolveWaiters(waiters) {
|
|
70660
|
-
for (const waiter of waiters) waiter.resolve();
|
|
70661
|
-
}
|
|
70662
|
-
function rejectWaiters(waiters, error) {
|
|
70663
|
-
for (const waiter of waiters) waiter.reject(error);
|
|
70664
|
-
}
|
|
70665
|
-
function buildJsonDocumentStore(opts) {
|
|
70666
|
-
const { filePath, recordSchema, currentVersion, migrate, storeName, docType } = opts;
|
|
70667
|
-
let cache2 = null;
|
|
70668
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
70669
|
-
let writeQueue = Promise.resolve();
|
|
70670
|
-
const lockPath2 = `${filePath}.lock`;
|
|
70671
|
-
let pendingMutations = /* @__PURE__ */ new Map();
|
|
70672
|
-
let pendingFlushTimer = null;
|
|
70673
|
-
let pendingFlushWaiters = [];
|
|
70674
|
-
function notify(event) {
|
|
70675
|
-
for (const listener of listeners) {
|
|
70676
|
-
try {
|
|
70677
|
-
listener(event);
|
|
70678
|
-
} catch {
|
|
70679
|
-
}
|
|
70680
|
-
}
|
|
70681
|
-
}
|
|
70682
|
-
async function ensureDir() {
|
|
70683
|
-
await mkdir21(dirname24(filePath), { recursive: true });
|
|
70684
|
-
}
|
|
70685
|
-
async function readFromDisk() {
|
|
70686
|
-
try {
|
|
70687
|
-
const raw = await readFile33(filePath, "utf-8");
|
|
70688
|
-
return migrate(JSON.parse(raw));
|
|
70689
|
-
} catch (err3) {
|
|
70690
|
-
if (isEnoent(err3)) {
|
|
70691
|
-
return { schemaVersion: currentVersion, records: {} };
|
|
70692
|
-
}
|
|
70693
|
-
if (isCorruptionError(err3)) {
|
|
70694
|
-
const event = await quarantineCorruptFile({
|
|
70695
|
-
path: filePath,
|
|
70696
|
-
storeName,
|
|
70697
|
-
docType,
|
|
70698
|
-
error: err3,
|
|
70699
|
-
defaultsApplied: true,
|
|
70700
|
-
logger: opts.logger
|
|
70701
|
-
});
|
|
70702
|
-
opts.onCorrupt?.(event);
|
|
70703
|
-
return { schemaVersion: currentVersion, records: {} };
|
|
70704
|
-
}
|
|
70705
|
-
throw err3;
|
|
70706
|
-
}
|
|
70707
|
-
}
|
|
70708
|
-
async function readStore() {
|
|
70709
|
-
if (cache2) return cache2;
|
|
70710
|
-
cache2 = await readFromDisk();
|
|
70711
|
-
return cache2;
|
|
70712
|
-
}
|
|
70713
|
-
async function flushPendingWrite() {
|
|
70714
|
-
pendingFlushTimer = null;
|
|
70715
|
-
const waiters = pendingFlushWaiters;
|
|
70716
|
-
pendingFlushWaiters = [];
|
|
70717
|
-
const mutations = pendingMutations;
|
|
70718
|
-
pendingMutations = /* @__PURE__ */ new Map();
|
|
70719
|
-
if (mutations.size === 0) {
|
|
70720
|
-
resolveWaiters(waiters);
|
|
70721
|
-
return;
|
|
70722
|
-
}
|
|
70723
|
-
try {
|
|
70724
|
-
await commitMutations(mutations);
|
|
70725
|
-
resolveWaiters(waiters);
|
|
70726
|
-
} catch (err3) {
|
|
70727
|
-
requeueMutations(mutations);
|
|
70728
|
-
if (pendingMutations.size > 0 && pendingFlushTimer === null) {
|
|
70729
|
-
pendingFlushTimer = setTimeout(() => {
|
|
70730
|
-
void flushPendingWrite();
|
|
70731
|
-
}, WRITE_DEBOUNCE_MS);
|
|
70732
|
-
}
|
|
70733
|
-
rejectWaiters(waiters, err3);
|
|
70734
|
-
}
|
|
70735
|
-
}
|
|
70736
|
-
async function commitMutations(mutations) {
|
|
70737
|
-
await withFileLock(lockPath2, async () => {
|
|
70738
|
-
const disk = await readFromDisk();
|
|
70739
|
-
applyMutations(disk.records, mutations);
|
|
70740
|
-
await ensureDir();
|
|
70741
|
-
await atomicWriteFile(filePath, JSON.stringify(disk));
|
|
70742
|
-
applyMutations(disk.records, pendingMutations);
|
|
70743
|
-
cache2 = disk;
|
|
70744
|
-
});
|
|
70745
|
-
}
|
|
70746
|
-
function requeueMutations(mutations) {
|
|
70747
|
-
for (const [id, mutation] of mutations) {
|
|
70748
|
-
if (!pendingMutations.has(id)) pendingMutations.set(id, mutation);
|
|
70749
|
-
}
|
|
70750
|
-
}
|
|
70751
|
-
function scheduleFlush() {
|
|
70752
|
-
return new Promise((resolve12, reject) => {
|
|
70753
|
-
pendingFlushWaiters.push({ resolve: resolve12, reject });
|
|
70754
|
-
if (pendingFlushTimer) return;
|
|
70755
|
-
pendingFlushTimer = setTimeout(() => {
|
|
70756
|
-
void flushPendingWrite();
|
|
70757
|
-
}, WRITE_DEBOUNCE_MS);
|
|
70758
|
-
});
|
|
70759
|
-
}
|
|
70760
|
-
function enqueueWrite(op) {
|
|
70761
|
-
let resultFlush;
|
|
70762
|
-
const next = writeQueue.then(
|
|
70763
|
-
async () => {
|
|
70764
|
-
const out = await op();
|
|
70765
|
-
resultFlush = out?.flush;
|
|
70766
|
-
},
|
|
70767
|
-
async () => {
|
|
70768
|
-
const out = await op();
|
|
70769
|
-
resultFlush = out?.flush;
|
|
70770
|
-
}
|
|
70771
|
-
);
|
|
70772
|
-
writeQueue = next;
|
|
70773
|
-
return next.then(() => resultFlush);
|
|
70774
|
-
}
|
|
70775
|
-
return {
|
|
70776
|
-
async get(id) {
|
|
70777
|
-
const store = await readStore();
|
|
70778
|
-
const record = store.records[id];
|
|
70779
|
-
return record ?? null;
|
|
70780
|
-
},
|
|
70781
|
-
async set(id, data) {
|
|
70782
|
-
await enqueueWrite(async () => {
|
|
70783
|
-
const store = await readStore();
|
|
70784
|
-
store.records[id] = data;
|
|
70785
|
-
pendingMutations.set(id, { kind: "set", data });
|
|
70786
|
-
const flush = scheduleFlush();
|
|
70787
|
-
notify({ kind: "set", id, data });
|
|
70788
|
-
return { flush };
|
|
70789
|
-
});
|
|
70790
|
-
},
|
|
70791
|
-
async update(id, fn) {
|
|
70792
|
-
await enqueueWrite(async () => {
|
|
70793
|
-
const store = await readStore();
|
|
70794
|
-
const existing = store.records[id];
|
|
70795
|
-
if (!existing) return void 0;
|
|
70796
|
-
const parsed = recordSchema.parse(existing);
|
|
70797
|
-
const updated = fn(parsed);
|
|
70798
|
-
if (updated === parsed) return void 0;
|
|
70799
|
-
store.records[id] = updated;
|
|
70800
|
-
pendingMutations.set(id, { kind: "set", data: updated });
|
|
70801
|
-
const flush = scheduleFlush();
|
|
70802
|
-
notify({ kind: "set", id, data: updated });
|
|
70803
|
-
return { flush };
|
|
70804
|
-
});
|
|
70805
|
-
},
|
|
70806
|
-
async delete(id) {
|
|
70807
|
-
await enqueueWrite(async () => {
|
|
70808
|
-
const store = await readStore();
|
|
70809
|
-
if (!(id in store.records)) return void 0;
|
|
70810
|
-
delete store.records[id];
|
|
70811
|
-
pendingMutations.set(id, { kind: "delete" });
|
|
70812
|
-
const flush = scheduleFlush();
|
|
70813
|
-
notify({ kind: "delete", id });
|
|
70814
|
-
return { flush };
|
|
70815
|
-
});
|
|
70816
|
-
},
|
|
70817
|
-
async list() {
|
|
70818
|
-
const store = await readStore();
|
|
70819
|
-
return { ...store.records };
|
|
70820
|
-
},
|
|
70821
|
-
subscribe(listener) {
|
|
70822
|
-
listeners.add(listener);
|
|
70823
|
-
return () => {
|
|
70824
|
-
listeners.delete(listener);
|
|
70825
|
-
};
|
|
70826
|
-
}
|
|
70827
|
-
};
|
|
70828
|
-
}
|
|
70829
|
-
|
|
70830
|
-
// src/services/skills/bundle-store.ts
|
|
71188
|
+
import { join as join60 } from "path";
|
|
70831
71189
|
var BUNDLE_RECORD_KEY = "bundle";
|
|
70832
71190
|
function defaultSkillBundleDir(shipyardHome) {
|
|
70833
|
-
return
|
|
71191
|
+
return join60(shipyardHome, "state", "skill-bundles");
|
|
70834
71192
|
}
|
|
70835
71193
|
function skillBundleSlug(ownerRepo) {
|
|
70836
71194
|
return ownerRepo.replace(/[^A-Za-z0-9._-]/g, (ch) => {
|
|
@@ -70852,7 +71210,7 @@ function buildProjectSkillBundleStore(baseDir) {
|
|
|
70852
71210
|
const store = buildJsonDocumentStore({
|
|
70853
71211
|
storeName: `skill-bundle:${slug}`,
|
|
70854
71212
|
docType: "generic",
|
|
70855
|
-
filePath:
|
|
71213
|
+
filePath: join60(baseDir, `${slug}.json`),
|
|
70856
71214
|
recordSchema: SkillBundleRecordSchema,
|
|
70857
71215
|
currentVersion: SKILL_BUNDLE_VERSION,
|
|
70858
71216
|
migrate: migrateSkillBundle
|
|
@@ -71244,9 +71602,9 @@ function nativeSkillIdentifier(skill) {
|
|
|
71244
71602
|
// src/services/skills/skill-library-cache.ts
|
|
71245
71603
|
import { createHash as createHash11 } from "crypto";
|
|
71246
71604
|
import { mkdir as mkdir22, writeFile as writeFile22 } from "fs/promises";
|
|
71247
|
-
import { join as
|
|
71605
|
+
import { join as join61 } from "path";
|
|
71248
71606
|
function defaultSkillLibraryCacheDir(shipyardHome) {
|
|
71249
|
-
return
|
|
71607
|
+
return join61(shipyardHome, "cache", "library-skills");
|
|
71250
71608
|
}
|
|
71251
71609
|
function skillCacheSlug(name, contentHash3) {
|
|
71252
71610
|
const safeName = name.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
@@ -71254,9 +71612,9 @@ function skillCacheSlug(name, contentHash3) {
|
|
|
71254
71612
|
return `${safeName}-${hash}`;
|
|
71255
71613
|
}
|
|
71256
71614
|
async function materializeLibrarySkill(input) {
|
|
71257
|
-
const dir =
|
|
71615
|
+
const dir = join61(input.baseDir, skillCacheSlug(input.name, input.contentHash));
|
|
71258
71616
|
await mkdir22(dir, { recursive: true });
|
|
71259
|
-
const skillMdPath =
|
|
71617
|
+
const skillMdPath = join61(dir, "SKILL.md");
|
|
71260
71618
|
await writeFile22(skillMdPath, input.body, "utf-8");
|
|
71261
71619
|
return { skillMdPath, dir };
|
|
71262
71620
|
}
|
|
@@ -71515,7 +71873,7 @@ function buildSkillResolutionService(deps) {
|
|
|
71515
71873
|
for (const file of files) {
|
|
71516
71874
|
const fileSlug = basename11(file, ".json");
|
|
71517
71875
|
if (!file.endsWith(".json") || fileSlug === currentSlug) continue;
|
|
71518
|
-
const bundle = await readBundleFile(
|
|
71876
|
+
const bundle = await readBundleFile(join62(dir, file));
|
|
71519
71877
|
if (bundle !== null) bundlesBySlug.set(fileSlug, bundle);
|
|
71520
71878
|
}
|
|
71521
71879
|
return bundlesBySlug;
|
|
@@ -71879,14 +72237,14 @@ function buildSkillResolutionService(deps) {
|
|
|
71879
72237
|
if (body === null || body.length === 0) {
|
|
71880
72238
|
return { ok: false, error: `No body supplied and no library skill named "${args.name}"` };
|
|
71881
72239
|
}
|
|
71882
|
-
const skillsRoot =
|
|
72240
|
+
const skillsRoot = join62(repoRoot, ".claude", "skills");
|
|
71883
72241
|
const skillDir = resolve11(skillsRoot, args.name);
|
|
71884
72242
|
const rel = relative8(skillsRoot, skillDir);
|
|
71885
72243
|
if (rel.startsWith("..") || rel.includes(`..${sep6}`) || resolve11(skillsRoot, rel) !== skillDir) {
|
|
71886
72244
|
return { ok: false, error: "Invalid skill path" };
|
|
71887
72245
|
}
|
|
71888
72246
|
await mkdir23(skillDir, { recursive: true });
|
|
71889
|
-
await writeFile23(
|
|
72247
|
+
await writeFile23(join62(skillDir, "SKILL.md"), body, "utf-8");
|
|
71890
72248
|
return { ok: true, name: args.name };
|
|
71891
72249
|
}
|
|
71892
72250
|
async function libraryBodyByName(cwd, name) {
|
|
@@ -71916,7 +72274,7 @@ function buildSkillResolutionService(deps) {
|
|
|
71916
72274
|
|
|
71917
72275
|
// src/services/stack-detection.ts
|
|
71918
72276
|
import { access as access5 } from "fs/promises";
|
|
71919
|
-
import { join as
|
|
72277
|
+
import { join as join63 } from "path";
|
|
71920
72278
|
var STACK_TIMEOUT_MS = TIMEOUT_MS;
|
|
71921
72279
|
async function defaultIsCommandAvailable(command) {
|
|
71922
72280
|
try {
|
|
@@ -71958,7 +72316,7 @@ async function detectStack(cwd, currentBranch, deps = {}) {
|
|
|
71958
72316
|
async function detectGraphite(cwd, currentBranch, isCommandAvailable, fileExists2, exec, getDefaultBranch2) {
|
|
71959
72317
|
const gtAvailable = await isCommandAvailable("gt");
|
|
71960
72318
|
if (!gtAvailable) return null;
|
|
71961
|
-
const markerPresent = await fileExists2(
|
|
72319
|
+
const markerPresent = await fileExists2(join63(cwd, ".git", ".graphite_repo_config"));
|
|
71962
72320
|
if (!markerPresent) return null;
|
|
71963
72321
|
const ancestor = await exec("gt", ["parent"], cwd).then((s2) => normalizeBranchName(s2)).catch(() => null);
|
|
71964
72322
|
const childrenRaw = await exec("gt", ["children"], cwd).then((s2) => s2).catch(() => "");
|
|
@@ -71971,7 +72329,7 @@ async function detectGraphite(cwd, currentBranch, isCommandAvailable, fileExists
|
|
|
71971
72329
|
return { ancestor: resolvedAncestor, descendants };
|
|
71972
72330
|
}
|
|
71973
72331
|
async function detectJujutsu(cwd, fileExists2, exec) {
|
|
71974
|
-
const jjPresent = await fileExists2(
|
|
72332
|
+
const jjPresent = await fileExists2(join63(cwd, ".jj"));
|
|
71975
72333
|
if (!jjPresent) return null;
|
|
71976
72334
|
const ancestorRaw = await exec(
|
|
71977
72335
|
"jj",
|
|
@@ -72020,7 +72378,7 @@ function parseJjLines(raw) {
|
|
|
72020
72378
|
|
|
72021
72379
|
// src/services/storage/annotation-store.ts
|
|
72022
72380
|
import { mkdir as mkdir24, readFile as readFile35 } from "fs/promises";
|
|
72023
|
-
import { dirname as dirname27, join as
|
|
72381
|
+
import { dirname as dirname27, join as join64 } from "path";
|
|
72024
72382
|
var LegacyBaseFields = external_exports.object({
|
|
72025
72383
|
commentId: external_exports.string(),
|
|
72026
72384
|
body: external_exports.string(),
|
|
@@ -72047,7 +72405,7 @@ var LegacyPlanStoreSchema = external_exports.object({
|
|
|
72047
72405
|
versions: external_exports.array(PlanVersionZodSchema)
|
|
72048
72406
|
});
|
|
72049
72407
|
function buildAnnotationStore(dataDir) {
|
|
72050
|
-
const baseDir =
|
|
72408
|
+
const baseDir = join64(dataDir, "annotations");
|
|
72051
72409
|
const cache2 = /* @__PURE__ */ new Map();
|
|
72052
72410
|
const listeners = /* @__PURE__ */ new Set();
|
|
72053
72411
|
const writeQueues = /* @__PURE__ */ new Map();
|
|
@@ -72060,7 +72418,7 @@ function buildAnnotationStore(dataDir) {
|
|
|
72060
72418
|
}
|
|
72061
72419
|
}
|
|
72062
72420
|
function filePath(taskId) {
|
|
72063
|
-
return
|
|
72421
|
+
return join64(baseDir, `${taskId}.json`);
|
|
72064
72422
|
}
|
|
72065
72423
|
function emptyData() {
|
|
72066
72424
|
return {
|
|
@@ -72125,9 +72483,9 @@ function buildAnnotationStore(dataDir) {
|
|
|
72125
72483
|
}
|
|
72126
72484
|
async function migrateLegacyFiles(taskId) {
|
|
72127
72485
|
const [diffRaw, previewRaw, planRaw] = await Promise.all([
|
|
72128
|
-
readLegacyFile(
|
|
72129
|
-
readLegacyFile(
|
|
72130
|
-
readLegacyFile(
|
|
72486
|
+
readLegacyFile(join64(dataDir, "diff-review", `${taskId}.json`)),
|
|
72487
|
+
readLegacyFile(join64(dataDir, "preview-annotations", `${taskId}.json`)),
|
|
72488
|
+
readLegacyFile(join64(dataDir, "plan-comments", `${taskId}.json`))
|
|
72131
72489
|
]);
|
|
72132
72490
|
if (diffRaw === null && previewRaw === null && planRaw === null) {
|
|
72133
72491
|
return null;
|
|
@@ -72333,7 +72691,7 @@ function buildAnnotationStore(dataDir) {
|
|
|
72333
72691
|
|
|
72334
72692
|
// src/services/storage/asset-store.ts
|
|
72335
72693
|
import { mkdir as mkdir25, readFile as readFile36, rename as rename17, writeFile as writeFile24 } from "fs/promises";
|
|
72336
|
-
import { join as
|
|
72694
|
+
import { join as join65 } from "path";
|
|
72337
72695
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
72338
72696
|
function isValidAssetId(id) {
|
|
72339
72697
|
return UUID_RE.test(id);
|
|
@@ -72359,10 +72717,10 @@ function buildAssetStore(assetsDir) {
|
|
|
72359
72717
|
}
|
|
72360
72718
|
}
|
|
72361
72719
|
function binaryPath(assetId) {
|
|
72362
|
-
return
|
|
72720
|
+
return join65(assetsDir, `${assetId}.bin`);
|
|
72363
72721
|
}
|
|
72364
72722
|
function metaPath(assetId) {
|
|
72365
|
-
return
|
|
72723
|
+
return join65(assetsDir, `${assetId}.meta.json`);
|
|
72366
72724
|
}
|
|
72367
72725
|
function parseMeta(raw) {
|
|
72368
72726
|
return AssetMetadataSchema.parse(JSON.parse(raw));
|
|
@@ -72386,8 +72744,8 @@ function buildAssetStore(assetsDir) {
|
|
|
72386
72744
|
size: data.byteLength,
|
|
72387
72745
|
createdAt: Date.now()
|
|
72388
72746
|
};
|
|
72389
|
-
const tmpBin =
|
|
72390
|
-
const tmpMeta =
|
|
72747
|
+
const tmpBin = join65(assetsDir, `${assetId}.bin.tmp`);
|
|
72748
|
+
const tmpMeta = join65(assetsDir, `${assetId}.meta.tmp`);
|
|
72391
72749
|
await writeFile24(tmpBin, data);
|
|
72392
72750
|
await writeFile24(tmpMeta, JSON.stringify(metadata), "utf-8");
|
|
72393
72751
|
await rename17(tmpBin, binaryPath(assetId));
|
|
@@ -72612,7 +72970,7 @@ function buildCredentialsVaultStore(filePath, corruptionLogger) {
|
|
|
72612
72970
|
|
|
72613
72971
|
// src/services/storage/deliverable-store.ts
|
|
72614
72972
|
import { mkdir as mkdir27, readFile as readFile38 } from "fs/promises";
|
|
72615
|
-
import { dirname as dirname29, join as
|
|
72973
|
+
import { dirname as dirname29, join as join66 } from "path";
|
|
72616
72974
|
var DELIVERABLE_STORE_VERSION = 1;
|
|
72617
72975
|
var DeliverableStoreSchema = external_exports.object({
|
|
72618
72976
|
schemaVersion: external_exports.number(),
|
|
@@ -72626,7 +72984,7 @@ function migrateDeliverableStore(raw) {
|
|
|
72626
72984
|
return { schemaVersion: DELIVERABLE_STORE_VERSION, records: [] };
|
|
72627
72985
|
}
|
|
72628
72986
|
function buildDeliverableStore(dataDir) {
|
|
72629
|
-
const baseDir =
|
|
72987
|
+
const baseDir = join66(dataDir, "deliverables");
|
|
72630
72988
|
const cache2 = /* @__PURE__ */ new Map();
|
|
72631
72989
|
const listeners = /* @__PURE__ */ new Set();
|
|
72632
72990
|
const writeQueues = /* @__PURE__ */ new Map();
|
|
@@ -72639,7 +72997,7 @@ function buildDeliverableStore(dataDir) {
|
|
|
72639
72997
|
}
|
|
72640
72998
|
}
|
|
72641
72999
|
function filePath(taskId) {
|
|
72642
|
-
return
|
|
73000
|
+
return join66(baseDir, `${taskId}.json`);
|
|
72643
73001
|
}
|
|
72644
73002
|
function emptyData() {
|
|
72645
73003
|
return { schemaVersion: DELIVERABLE_STORE_VERSION, records: [] };
|
|
@@ -72779,7 +73137,7 @@ function buildDeliverableStore(dataDir) {
|
|
|
72779
73137
|
|
|
72780
73138
|
// src/services/storage/jsonl-conversation-store.ts
|
|
72781
73139
|
import { appendFile as appendFile3, mkdir as mkdir28, open, readFile as readFile39, stat as stat17 } from "fs/promises";
|
|
72782
|
-
import { join as
|
|
73140
|
+
import { join as join67 } from "path";
|
|
72783
73141
|
var StoredMessageSchema = MessageSchema.omit({ seqNo: true, channelId: true });
|
|
72784
73142
|
function logPerf(entry) {
|
|
72785
73143
|
try {
|
|
@@ -72789,12 +73147,12 @@ function logPerf(entry) {
|
|
|
72789
73147
|
}
|
|
72790
73148
|
}
|
|
72791
73149
|
function buildJsonlConversationStore(dataDir, opts = {}) {
|
|
72792
|
-
const channelsDir =
|
|
73150
|
+
const channelsDir = join67(dataDir, "channels");
|
|
72793
73151
|
const seqCounters = /* @__PURE__ */ new Map();
|
|
72794
73152
|
const channelQueues = /* @__PURE__ */ new Map();
|
|
72795
73153
|
const readCache = /* @__PURE__ */ new Map();
|
|
72796
73154
|
function channelPath(channelId) {
|
|
72797
|
-
return
|
|
73155
|
+
return join67(channelsDir, `${channelId}.jsonl`);
|
|
72798
73156
|
}
|
|
72799
73157
|
async function ensureDir() {
|
|
72800
73158
|
await mkdir28(channelsDir, { recursive: true });
|
|
@@ -73566,7 +73924,7 @@ function buildProjectsStore(filePath) {
|
|
|
73566
73924
|
|
|
73567
73925
|
// src/services/storage/rate-limit-store.ts
|
|
73568
73926
|
import { mkdir as mkdir30, readFile as readFile41 } from "fs/promises";
|
|
73569
|
-
import { dirname as dirname31, join as
|
|
73927
|
+
import { dirname as dirname31, join as join68 } from "path";
|
|
73570
73928
|
var RATE_LIMIT_STORE_VERSION = 4;
|
|
73571
73929
|
var RateLimitRecordSchema = external_exports.object({
|
|
73572
73930
|
info: RateLimitInfoSchema,
|
|
@@ -73605,7 +73963,7 @@ function isWindowKey(x2) {
|
|
|
73605
73963
|
return x2 !== void 0 && WINDOW_KEYS.has(x2);
|
|
73606
73964
|
}
|
|
73607
73965
|
async function buildRateLimitStore(dataDir, opts) {
|
|
73608
|
-
const filePath =
|
|
73966
|
+
const filePath = join68(dataDir, "rate-limits.json");
|
|
73609
73967
|
const lockPath2 = `${filePath}.lock`;
|
|
73610
73968
|
const initial = await loadStoreFile(filePath, opts.logger);
|
|
73611
73969
|
const records = { ...initial.records };
|
|
@@ -73858,12 +74216,12 @@ async function atomicWrite4(filePath, data) {
|
|
|
73858
74216
|
}
|
|
73859
74217
|
|
|
73860
74218
|
// src/services/storage/schedule-store.ts
|
|
73861
|
-
import { join as
|
|
74219
|
+
import { join as join69 } from "path";
|
|
73862
74220
|
function buildScheduleStore(dataDir) {
|
|
73863
74221
|
const store = buildJsonDocumentStore({
|
|
73864
74222
|
storeName: "schedules",
|
|
73865
74223
|
docType: "schedule",
|
|
73866
|
-
filePath:
|
|
74224
|
+
filePath: join69(dataDir, "schedules.json"),
|
|
73867
74225
|
recordSchema: ScheduleRecordSchema,
|
|
73868
74226
|
currentVersion: SCHEDULE_STORE_VERSION,
|
|
73869
74227
|
migrate(raw) {
|
|
@@ -73902,7 +74260,7 @@ function buildScheduleStore(dataDir) {
|
|
|
73902
74260
|
|
|
73903
74261
|
// src/services/storage/session-persistence.ts
|
|
73904
74262
|
import { mkdir as mkdir31, readdir as readdir20, readFile as readFile42, rm as rm11 } from "fs/promises";
|
|
73905
|
-
import { join as
|
|
74263
|
+
import { join as join70 } from "path";
|
|
73906
74264
|
var PersistedSessionSchema = external_exports.object({
|
|
73907
74265
|
sessionId: external_exports.string(),
|
|
73908
74266
|
channelId: external_exports.string(),
|
|
@@ -73959,9 +74317,9 @@ async function loadOneSessionFile(originalPath, corruptionLogger) {
|
|
|
73959
74317
|
}
|
|
73960
74318
|
}
|
|
73961
74319
|
function buildSessionPersistence(dataDir, corruptionLogger) {
|
|
73962
|
-
const channelsDir =
|
|
74320
|
+
const channelsDir = join70(dataDir, "channels");
|
|
73963
74321
|
function sessionPath(channelId) {
|
|
73964
|
-
return
|
|
74322
|
+
return join70(channelsDir, `${channelId}.session.json`);
|
|
73965
74323
|
}
|
|
73966
74324
|
async function ensureDir() {
|
|
73967
74325
|
await mkdir31(channelsDir, { recursive: true });
|
|
@@ -74019,7 +74377,7 @@ function buildSessionPersistence(dataDir, corruptionLogger) {
|
|
|
74019
74377
|
if (i >= sessionEntries.length) return;
|
|
74020
74378
|
const entry = sessionEntries[i];
|
|
74021
74379
|
if (!entry) return;
|
|
74022
|
-
results[i] = await loadOneSessionFile(
|
|
74380
|
+
results[i] = await loadOneSessionFile(join70(channelsDir, entry), corruptionLogger);
|
|
74023
74381
|
}
|
|
74024
74382
|
}
|
|
74025
74383
|
const workers = Array.from(
|
|
@@ -74033,12 +74391,12 @@ function buildSessionPersistence(dataDir, corruptionLogger) {
|
|
|
74033
74391
|
}
|
|
74034
74392
|
|
|
74035
74393
|
// src/services/storage/template-store.ts
|
|
74036
|
-
import { join as
|
|
74394
|
+
import { join as join71 } from "path";
|
|
74037
74395
|
function buildTemplateStore(dataDir) {
|
|
74038
74396
|
const store = buildJsonDocumentStore({
|
|
74039
74397
|
storeName: "templates",
|
|
74040
74398
|
docType: "template",
|
|
74041
|
-
filePath:
|
|
74399
|
+
filePath: join71(dataDir, "templates.json"),
|
|
74042
74400
|
recordSchema: TaskTemplateRecordSchema,
|
|
74043
74401
|
currentVersion: TEMPLATE_STORE_VERSION,
|
|
74044
74402
|
migrate(raw) {
|
|
@@ -79770,7 +80128,7 @@ import { randomUUID as randomUUID21 } from "crypto";
|
|
|
79770
80128
|
import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
79771
80129
|
import { mkdir as mkdir33, readFile as readFile45, writeFile as writeFile26 } from "fs/promises";
|
|
79772
80130
|
import { homedir as homedir10 } from "os";
|
|
79773
|
-
import { dirname as dirname34, join as
|
|
80131
|
+
import { dirname as dirname34, join as join73 } from "path";
|
|
79774
80132
|
|
|
79775
80133
|
// src/services/plan-backfill/plan-lazy-rebuild.ts
|
|
79776
80134
|
var SETTLE_BUDGET_MS = 2e3;
|
|
@@ -80540,8 +80898,8 @@ function greedyLCS(a, b2) {
|
|
|
80540
80898
|
// src/services/plan/plan-file-watcher.ts
|
|
80541
80899
|
import { existsSync as existsSync9 } from "fs";
|
|
80542
80900
|
import { homedir as homedir9 } from "os";
|
|
80543
|
-
import { join as
|
|
80544
|
-
var DEFAULT_PLANS_DIR =
|
|
80901
|
+
import { join as join72 } from "path";
|
|
80902
|
+
var DEFAULT_PLANS_DIR = join72(homedir9(), ".claude", "plans");
|
|
80545
80903
|
var PLAN_WATCH_TIMEOUT_MS = 1e4;
|
|
80546
80904
|
var PLAN_WATCH_DEBOUNCE_MS = 250;
|
|
80547
80905
|
function createPlanFileWatcher(deps) {
|
|
@@ -81079,7 +81437,7 @@ var PlanHandler = class {
|
|
|
81079
81437
|
* Called when a Write tool result mentions a file in ~/.claude/plans/.
|
|
81080
81438
|
*/
|
|
81081
81439
|
trackCreatedFile(filePath) {
|
|
81082
|
-
const plansDir =
|
|
81440
|
+
const plansDir = join73(homedir10(), ".claude", "plans");
|
|
81083
81441
|
if (filePath.startsWith(plansDir) && filePath.endsWith(".md")) {
|
|
81084
81442
|
this.#createdPlanFiles.add(filePath);
|
|
81085
81443
|
this.#deps.log({
|
|
@@ -81111,7 +81469,7 @@ var PlanHandler = class {
|
|
|
81111
81469
|
}
|
|
81112
81470
|
async #runHandleCodexPlanReady(content) {
|
|
81113
81471
|
if (this.#deps.isDisposed()) return;
|
|
81114
|
-
const filePath =
|
|
81472
|
+
const filePath = join73(getShipyardHome(), "plans", `${this.#deps.taskId}.md`);
|
|
81115
81473
|
try {
|
|
81116
81474
|
await mkdir33(dirname34(filePath), { recursive: true });
|
|
81117
81475
|
await writeFile26(filePath, content, "utf-8");
|
|
@@ -81590,12 +81948,12 @@ var PlanHandler = class {
|
|
|
81590
81948
|
}).filter((f2) => f2 !== null).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
81591
81949
|
return newest?.path ?? null;
|
|
81592
81950
|
}
|
|
81593
|
-
const plansDir =
|
|
81951
|
+
const plansDir = join73(homedir10(), ".claude", "plans");
|
|
81594
81952
|
if (!existsSync10(plansDir)) return null;
|
|
81595
81953
|
try {
|
|
81596
81954
|
const files = readdirSync5(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
|
|
81597
|
-
path:
|
|
81598
|
-
mtime: statSync5(
|
|
81955
|
+
path: join73(plansDir, f2),
|
|
81956
|
+
mtime: statSync5(join73(plansDir, f2)).mtimeMs
|
|
81599
81957
|
})).sort((a, b2) => b2.mtime - a.mtime);
|
|
81600
81958
|
const recent = files[0];
|
|
81601
81959
|
if (recent && Date.now() - recent.mtime < 3e4) return recent.path;
|
|
@@ -83822,7 +84180,7 @@ var RewindCheckpointHandler = class {
|
|
|
83822
84180
|
|
|
83823
84181
|
// src/services/task/side-thread-registry.ts
|
|
83824
84182
|
import { mkdir as mkdir34, readFile as readFile46, rename as rename19, writeFile as writeFile27 } from "fs/promises";
|
|
83825
|
-
import { dirname as dirname35, join as
|
|
84183
|
+
import { dirname as dirname35, join as join74 } from "path";
|
|
83826
84184
|
var ThreadFileSchema = external_exports.object({
|
|
83827
84185
|
threads: external_exports.record(external_exports.string(), ThreadMetadataSchema)
|
|
83828
84186
|
});
|
|
@@ -84198,7 +84556,7 @@ var SideThreadRegistry = class {
|
|
|
84198
84556
|
}
|
|
84199
84557
|
/** Persistence */
|
|
84200
84558
|
#filePath() {
|
|
84201
|
-
return
|
|
84559
|
+
return join74(this.#deps.dataDir, "threads", `${this.#deps.taskId}.json`);
|
|
84202
84560
|
}
|
|
84203
84561
|
async #ensureLoaded() {
|
|
84204
84562
|
if (this.#loadedFromDisk.has(this.#deps.taskId)) return;
|
|
@@ -87115,7 +87473,7 @@ async function resolveTaskListPrepend(ctx) {
|
|
|
87115
87473
|
import { existsSync as existsSync11 } from "fs";
|
|
87116
87474
|
import { readdir as readdir21, readFile as readFile47 } from "fs/promises";
|
|
87117
87475
|
import { homedir as homedir11 } from "os";
|
|
87118
|
-
import { basename as basename13, dirname as dirname36, join as
|
|
87476
|
+
import { basename as basename13, dirname as dirname36, join as join75 } from "path";
|
|
87119
87477
|
var VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
|
|
87120
87478
|
var IDLE_DETACH_MS = 5 * 60 * 1e3;
|
|
87121
87479
|
var IDLE_SWEEP_INTERVAL_MS = 6e4;
|
|
@@ -87131,7 +87489,7 @@ function isCCTaskFile(value) {
|
|
|
87131
87489
|
}
|
|
87132
87490
|
function createCCTaskFileWatcher(listId, log) {
|
|
87133
87491
|
const sanitizedId = sanitize(listId);
|
|
87134
|
-
const dir =
|
|
87492
|
+
const dir = join75(homedir11(), ".claude", "tasks", sanitizedId);
|
|
87135
87493
|
const targetDirName = basename13(dir);
|
|
87136
87494
|
const parentDir = dirname36(dir);
|
|
87137
87495
|
const watchState = {
|
|
@@ -87167,7 +87525,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
87167
87525
|
return tasks;
|
|
87168
87526
|
}
|
|
87169
87527
|
async function readTask(taskId) {
|
|
87170
|
-
const filePath =
|
|
87528
|
+
const filePath = join75(dir, `${taskId}.json`);
|
|
87171
87529
|
try {
|
|
87172
87530
|
const raw = await readFile47(filePath, "utf-8");
|
|
87173
87531
|
const parsed = JSON.parse(raw);
|
|
@@ -87234,7 +87592,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
87234
87592
|
watchState.parentSub = null;
|
|
87235
87593
|
return;
|
|
87236
87594
|
}
|
|
87237
|
-
if (ev.path.endsWith(targetDirName) || ev.path ===
|
|
87595
|
+
if (ev.path.endsWith(targetDirName) || ev.path === join75(parentDir, targetDirName)) {
|
|
87238
87596
|
void ensureDirWatcher();
|
|
87239
87597
|
break;
|
|
87240
87598
|
}
|
|
@@ -87351,7 +87709,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
87351
87709
|
// src/services/task/cc-task-file-writer.ts
|
|
87352
87710
|
import { createHash as createHash13 } from "crypto";
|
|
87353
87711
|
import { mkdir as mkdir35, readdir as readdir22, rename as rename20, unlink as unlink14, writeFile as writeFile28 } from "fs/promises";
|
|
87354
|
-
import { join as
|
|
87712
|
+
import { join as join76 } from "path";
|
|
87355
87713
|
function contentHash2(data) {
|
|
87356
87714
|
return createHash13("sha256").update(data).digest("hex").slice(0, 16);
|
|
87357
87715
|
}
|
|
@@ -87367,7 +87725,7 @@ async function writeTasks(dir, tasks, hashes) {
|
|
|
87367
87725
|
const json = JSON.stringify(structuredTaskToCCFile(task), null, 2);
|
|
87368
87726
|
const hash = contentHash2(json);
|
|
87369
87727
|
if (hashes.get(id) === hash) continue;
|
|
87370
|
-
await atomicWriteFile2(
|
|
87728
|
+
await atomicWriteFile2(join76(dir, `${id}.json`), json);
|
|
87371
87729
|
hashes.set(id, hash);
|
|
87372
87730
|
}
|
|
87373
87731
|
return targetIds;
|
|
@@ -87383,7 +87741,7 @@ async function removeStaleFiles(dir, targetIds, hashes) {
|
|
|
87383
87741
|
if (!entry.endsWith(".json")) continue;
|
|
87384
87742
|
const id = entry.slice(0, -".json".length);
|
|
87385
87743
|
if (!targetIds.has(id) && hashes.has(id)) {
|
|
87386
|
-
await unlink14(
|
|
87744
|
+
await unlink14(join76(dir, entry)).catch(() => {
|
|
87387
87745
|
});
|
|
87388
87746
|
hashes.delete(id);
|
|
87389
87747
|
}
|
|
@@ -88015,10 +88373,10 @@ var StructuredTaskTracker = class {
|
|
|
88015
88373
|
import { unwatchFile, watchFile } from "fs";
|
|
88016
88374
|
import { open as open2 } from "fs/promises";
|
|
88017
88375
|
import { homedir as homedir12 } from "os";
|
|
88018
|
-
import { join as
|
|
88376
|
+
import { join as join77 } from "path";
|
|
88019
88377
|
function computeTranscriptPath(cwd, sessionId, taskId) {
|
|
88020
88378
|
const cwdSlug = cwd.replace(/[^a-zA-Z0-9]/g, "-").slice(0, 200);
|
|
88021
|
-
return
|
|
88379
|
+
return join77(
|
|
88022
88380
|
homedir12(),
|
|
88023
88381
|
".claude",
|
|
88024
88382
|
"projects",
|
|
@@ -88733,7 +89091,7 @@ import { createHash as createHash14 } from "crypto";
|
|
|
88733
89091
|
import { unlink as unlink15, writeFile as writeFile29 } from "fs/promises";
|
|
88734
89092
|
import { createRequire as createRequire4 } from "module";
|
|
88735
89093
|
import { tmpdir } from "os";
|
|
88736
|
-
import { join as
|
|
89094
|
+
import { join as join78 } from "path";
|
|
88737
89095
|
function spawnCollect(bin, args, opts) {
|
|
88738
89096
|
return new Promise((resolve12, reject) => {
|
|
88739
89097
|
const child = spawn12(bin, args, {
|
|
@@ -88791,7 +89149,7 @@ function resolveDaemonNodeModules(log) {
|
|
|
88791
89149
|
} catch {
|
|
88792
89150
|
}
|
|
88793
89151
|
try {
|
|
88794
|
-
const cwdRequire = createRequire4(
|
|
89152
|
+
const cwdRequire = createRequire4(join78(process.cwd(), "package.json"));
|
|
88795
89153
|
const sdkPath = cwdRequire.resolve("@modelcontextprotocol/sdk/server/mcp.js");
|
|
88796
89154
|
const idx = sdkPath.lastIndexOf("node_modules");
|
|
88797
89155
|
if (idx >= 0) return sdkPath.slice(0, idx + "node_modules".length);
|
|
@@ -88801,7 +89159,7 @@ function resolveDaemonNodeModules(log) {
|
|
|
88801
89159
|
error: err3 instanceof Error ? err3.message : String(err3)
|
|
88802
89160
|
});
|
|
88803
89161
|
}
|
|
88804
|
-
return
|
|
89162
|
+
return join78(process.cwd(), "node_modules");
|
|
88805
89163
|
}
|
|
88806
89164
|
function planTokenCount(systemPrompt, metadata, cwd, model, mcpServers) {
|
|
88807
89165
|
return {
|
|
@@ -89000,7 +89358,7 @@ async function serializeMcpConfig(servers, allToolNames, daemonNodeModules, rese
|
|
|
89000
89358
|
if (isReservedMcpServerName(serverName, reservedServerNames)) continue;
|
|
89001
89359
|
const script = buildStubServerScript(serverName, tools);
|
|
89002
89360
|
if (!script) continue;
|
|
89003
|
-
const stubPath =
|
|
89361
|
+
const stubPath = join78(tmpdir(), `shipyard-mcp-stub-${serverName}-${Date.now()}.cjs`);
|
|
89004
89362
|
await writeFile29(stubPath, script, "utf-8");
|
|
89005
89363
|
stubPaths.push(stubPath);
|
|
89006
89364
|
mcpServers[serverName] = {
|
|
@@ -89026,7 +89384,7 @@ async function prepareTokenCountArgs(plan, log, reservedServerNames = SDK_RESERV
|
|
|
89026
89384
|
reservedServerNames
|
|
89027
89385
|
);
|
|
89028
89386
|
if (!serialized.config) return { args, mcpConfigPath: null, stubPaths: serialized.stubPaths };
|
|
89029
|
-
const mcpConfigPath =
|
|
89387
|
+
const mcpConfigPath = join78(tmpdir(), `shipyard-mcp-${Date.now()}.json`);
|
|
89030
89388
|
await writeFile29(mcpConfigPath, JSON.stringify(serialized.config), "utf-8");
|
|
89031
89389
|
args.push("--mcp-config", mcpConfigPath);
|
|
89032
89390
|
return { args, mcpConfigPath, stubPaths: serialized.stubPaths };
|
|
@@ -95122,7 +95480,7 @@ async function createDaemon(deps) {
|
|
|
95122
95480
|
const annotationStore = buildAnnotationStore(deps.dataDir);
|
|
95123
95481
|
const deliverableStore = buildDeliverableStore(deps.dataDir);
|
|
95124
95482
|
const resourceRegistry = createResourceRegistry();
|
|
95125
|
-
const assetStore = buildAssetStore(
|
|
95483
|
+
const assetStore = buildAssetStore(join79(deps.dataDir, "assets"));
|
|
95126
95484
|
const shipyardResolver = createShipyardResolver();
|
|
95127
95485
|
shipyardResolver.addSubResolver("asset", createAssetResolver(assetStore));
|
|
95128
95486
|
const pluginResourceResolver = new PluginResourceResolver(deps.log);
|
|
@@ -95149,10 +95507,10 @@ async function createDaemon(deps) {
|
|
|
95149
95507
|
workspaceRoot: deps.workspaceRoot,
|
|
95150
95508
|
log: deps.log
|
|
95151
95509
|
});
|
|
95152
|
-
const userSettingsStore = buildUserSettingsStore(
|
|
95153
|
-
const projectsStore = buildProjectsStore(
|
|
95510
|
+
const userSettingsStore = buildUserSettingsStore(join79(deps.dataDir, "user-settings.json"));
|
|
95511
|
+
const projectsStore = buildProjectsStore(join79(deps.dataDir, "projects.json"));
|
|
95154
95512
|
const credentialsVaultStore = buildCredentialsVaultStore(
|
|
95155
|
-
|
|
95513
|
+
join79(deps.dataDir, "credentials-vault.json")
|
|
95156
95514
|
);
|
|
95157
95515
|
const cursorVaultKeyRef = { current: null };
|
|
95158
95516
|
let resolveCursorVaultKey;
|
|
@@ -95600,7 +95958,7 @@ async function createDaemon(deps) {
|
|
|
95600
95958
|
taskId: args.taskId,
|
|
95601
95959
|
agentSystem: args.agentSystem,
|
|
95602
95960
|
environmentKey: args.cwd,
|
|
95603
|
-
pluginsDir:
|
|
95961
|
+
pluginsDir: join79(deps.shipyardHome, "plugins"),
|
|
95604
95962
|
vizWatcher: getOrCreateVizWatcher(args.taskId).watcher,
|
|
95605
95963
|
mode: "task",
|
|
95606
95964
|
previewProxy: deps.previewProxy,
|
|
@@ -95738,7 +96096,7 @@ async function createDaemon(deps) {
|
|
|
95738
96096
|
/** Claude in-process harness — Codex uses the HTTP path with 'codex' above. */
|
|
95739
96097
|
agentSystem: AGENT_SYSTEM_CLAUDE_CODE,
|
|
95740
96098
|
environmentKey: args.cwd,
|
|
95741
|
-
pluginsDir:
|
|
96099
|
+
pluginsDir: join79(deps.shipyardHome, "plugins"),
|
|
95742
96100
|
vizWatcher: args.vizWatcher,
|
|
95743
96101
|
mode: args.mode ?? "task",
|
|
95744
96102
|
previewProxy: deps.previewProxy,
|
|
@@ -96772,7 +97130,7 @@ async function createDaemon(deps) {
|
|
|
96772
97130
|
deps.metricsCollector
|
|
96773
97131
|
);
|
|
96774
97132
|
const planDocGapReporter = startPlanDocPersistenceGapReporter({
|
|
96775
|
-
loroDir:
|
|
97133
|
+
loroDir: join79(deps.dataDir, "loro"),
|
|
96776
97134
|
listTasks: () => taskStateStore.listTasks(),
|
|
96777
97135
|
log: deps.log,
|
|
96778
97136
|
metricsCollector: deps.metricsCollector
|
|
@@ -96788,7 +97146,7 @@ async function createDaemon(deps) {
|
|
|
96788
97146
|
const stallProfilerConfig = getStallProfilerConfig();
|
|
96789
97147
|
const stallProfiler = new StallProfiler({
|
|
96790
97148
|
log: deps.log,
|
|
96791
|
-
outDir:
|
|
97149
|
+
outDir: join79(deps.dataDir, "stall-profiles"),
|
|
96792
97150
|
thresholdMs: stallProfilerConfig.thresholdMs,
|
|
96793
97151
|
captureMs: stallProfilerConfig.captureMs,
|
|
96794
97152
|
rateLimitMs: stallProfilerConfig.rateLimitMs
|
|
@@ -97426,7 +97784,7 @@ import { existsSync as existsSync13 } from "fs";
|
|
|
97426
97784
|
import { execFile as execFile11, spawn as spawn13 } from "child_process";
|
|
97427
97785
|
import { createHash as createHash15 } from "crypto";
|
|
97428
97786
|
import { chmod as chmod3, mkdir as mkdir37, readFile as readFile48, rename as rename21, unlink as unlink16, writeFile as writeFile30 } from "fs/promises";
|
|
97429
|
-
import { join as
|
|
97787
|
+
import { join as join80 } from "path";
|
|
97430
97788
|
|
|
97431
97789
|
// src/services/bootstrap/self-update-installer-scripts.ts
|
|
97432
97790
|
function buildPosixInstallerScript(params) {
|
|
@@ -97828,7 +98186,7 @@ async function downloadTarball(url, destPath, fetchFn) {
|
|
|
97828
98186
|
throw new Error(`download failed: HTTP ${response.status} ${response.statusText}`);
|
|
97829
98187
|
}
|
|
97830
98188
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
97831
|
-
await mkdir37(
|
|
98189
|
+
await mkdir37(join80(destPath, ".."), { recursive: true });
|
|
97832
98190
|
try {
|
|
97833
98191
|
await writeFile30(tmpPath, bytes);
|
|
97834
98192
|
await rename21(tmpPath, destPath);
|
|
@@ -97858,7 +98216,7 @@ async function verifyChecksum(path5, expectedHash) {
|
|
|
97858
98216
|
}
|
|
97859
98217
|
}
|
|
97860
98218
|
async function stageInstallerScript(scriptPath, params) {
|
|
97861
|
-
await mkdir37(
|
|
98219
|
+
await mkdir37(join80(scriptPath, ".."), { recursive: true });
|
|
97862
98220
|
const body = process.platform === "win32" ? buildWindowsInstallerScript(params) : buildPosixInstallerScript(params);
|
|
97863
98221
|
await writeFile30(scriptPath, body);
|
|
97864
98222
|
await chmod3(scriptPath, 493);
|
|
@@ -97908,16 +98266,16 @@ function buildInstallerParams(state, deps) {
|
|
|
97908
98266
|
targetVersion: state.resolved.version,
|
|
97909
98267
|
previousVersion: deps.currentVersion,
|
|
97910
98268
|
parentPid: deps.pid,
|
|
97911
|
-
statusFilePath:
|
|
97912
|
-
pidFilePath:
|
|
97913
|
-
logPath:
|
|
97914
|
-
snapshotPath:
|
|
98269
|
+
statusFilePath: join80(deps.shipyardHome, "update-status.json"),
|
|
98270
|
+
pidFilePath: join80(deps.shipyardHome, "daemon.pid"),
|
|
98271
|
+
logPath: join80(deps.shipyardHome, "updates", `install-${deps.pid}.log`),
|
|
98272
|
+
snapshotPath: join80(deps.shipyardHome, "updates", `rollback-${deps.currentVersion}`),
|
|
97915
98273
|
npmBin: "npm"
|
|
97916
98274
|
};
|
|
97917
98275
|
}
|
|
97918
98276
|
function tarballPathFor(shipyardHome, version, shasum) {
|
|
97919
98277
|
const shaPrefix = shasum.slice(0, 12);
|
|
97920
|
-
return
|
|
98278
|
+
return join80(shipyardHome, "updates", `${version}-${shaPrefix}.tgz`);
|
|
97921
98279
|
}
|
|
97922
98280
|
async function runResolveStep(step, state, deps) {
|
|
97923
98281
|
const resolver = deps.resolveVersion ?? defaultResolveVersion;
|
|
@@ -99005,7 +99363,7 @@ function buildLocalDirectChannelCallbacks(deps) {
|
|
|
99005
99363
|
|
|
99006
99364
|
// src/services/skills-cache/codex-skills-cache.ts
|
|
99007
99365
|
import { mkdir as mkdir38, readFile as readFile49, rename as rename22, writeFile as writeFile31 } from "fs/promises";
|
|
99008
|
-
import { dirname as dirname38, join as
|
|
99366
|
+
import { dirname as dirname38, join as join81 } from "path";
|
|
99009
99367
|
var CACHE_FILENAME = "codex-skills-cache.json";
|
|
99010
99368
|
var SkillInfoCacheEntrySchema = external_exports.object({
|
|
99011
99369
|
name: external_exports.string(),
|
|
@@ -99022,7 +99380,7 @@ var CodexSkillsCacheFileSchema = external_exports.object({
|
|
|
99022
99380
|
skills: external_exports.array(SkillInfoCacheEntrySchema)
|
|
99023
99381
|
});
|
|
99024
99382
|
function defaultCodexSkillsCachePath(shipyardHome) {
|
|
99025
|
-
return
|
|
99383
|
+
return join81(shipyardHome, "state", CACHE_FILENAME);
|
|
99026
99384
|
}
|
|
99027
99385
|
async function loadCodexSkillsCache(path5) {
|
|
99028
99386
|
let raw;
|
|
@@ -99086,11 +99444,11 @@ async function saveCodexSkillsCache(path5, cache2) {
|
|
|
99086
99444
|
// src/services/skills-mirror/manager.ts
|
|
99087
99445
|
import { lstat, mkdir as mkdir40, readlink, stat as stat18, symlink, unlink as unlink18 } from "fs/promises";
|
|
99088
99446
|
import { homedir as homedir14 } from "os";
|
|
99089
|
-
import { dirname as dirname40, join as
|
|
99447
|
+
import { dirname as dirname40, join as join83 } from "path";
|
|
99090
99448
|
|
|
99091
99449
|
// src/services/skills-mirror/manifest.ts
|
|
99092
99450
|
import { mkdir as mkdir39, readFile as readFile50, rename as rename23, unlink as unlink17, writeFile as writeFile32 } from "fs/promises";
|
|
99093
|
-
import { dirname as dirname39, join as
|
|
99451
|
+
import { dirname as dirname39, join as join82 } from "path";
|
|
99094
99452
|
var MANIFEST_VERSION = 1;
|
|
99095
99453
|
var ManifestEntrySchema = external_exports.object({
|
|
99096
99454
|
/** Display name of the skill (e.g. `qa`, or `plugin:name` when namespaced). */
|
|
@@ -99110,7 +99468,7 @@ function emptyManifest() {
|
|
|
99110
99468
|
return { version: MANIFEST_VERSION, entries: [] };
|
|
99111
99469
|
}
|
|
99112
99470
|
function defaultManifestPath(shipyardHome) {
|
|
99113
|
-
return
|
|
99471
|
+
return join82(shipyardHome, "state", "skill-mirror-manifest.json");
|
|
99114
99472
|
}
|
|
99115
99473
|
async function loadManifest(path5) {
|
|
99116
99474
|
try {
|
|
@@ -99141,15 +99499,15 @@ async function deleteManifest(path5) {
|
|
|
99141
99499
|
function defaultPaths() {
|
|
99142
99500
|
const home = homedir14();
|
|
99143
99501
|
return {
|
|
99144
|
-
claudeSkillsDir:
|
|
99145
|
-
codexSkillsDir:
|
|
99502
|
+
claudeSkillsDir: join83(home, ".claude", "skills"),
|
|
99503
|
+
codexSkillsDir: join83(home, ".agents", "skills"),
|
|
99146
99504
|
manifestPath: defaultManifestPath(getShipyardHome())
|
|
99147
99505
|
};
|
|
99148
99506
|
}
|
|
99149
99507
|
function plannedSymlinkPath(skill, paths) {
|
|
99150
99508
|
const linkName = skill.namespace ? `${skill.namespace}:${skill.name}` : skill.name;
|
|
99151
|
-
if (skill.sourceAgent === "claude-code") return
|
|
99152
|
-
if (skill.sourceAgent === "codex") return
|
|
99509
|
+
if (skill.sourceAgent === "claude-code") return join83(paths.codexSkillsDir, linkName);
|
|
99510
|
+
if (skill.sourceAgent === "codex") return join83(paths.claudeSkillsDir, linkName);
|
|
99153
99511
|
return null;
|
|
99154
99512
|
}
|
|
99155
99513
|
function isPermissionError(err3) {
|
|
@@ -99362,7 +99720,7 @@ async function ensureMirror(skills, config, paths = defaultPaths()) {
|
|
|
99362
99720
|
|
|
99363
99721
|
// src/services/skills-mirror/prefs.ts
|
|
99364
99722
|
import { mkdir as mkdir41, readFile as readFile51, rename as rename24, writeFile as writeFile33 } from "fs/promises";
|
|
99365
|
-
import { dirname as dirname41, join as
|
|
99723
|
+
import { dirname as dirname41, join as join84 } from "path";
|
|
99366
99724
|
var SkillsMirrorPrefsSchema = external_exports.object({
|
|
99367
99725
|
enabled: external_exports.boolean().default(false)
|
|
99368
99726
|
});
|
|
@@ -99371,7 +99729,7 @@ function defaultSkillsMirrorPrefs() {
|
|
|
99371
99729
|
return { enabled: false };
|
|
99372
99730
|
}
|
|
99373
99731
|
function prefsPath(dataDir) {
|
|
99374
|
-
return
|
|
99732
|
+
return join84(dataDir, FILENAME);
|
|
99375
99733
|
}
|
|
99376
99734
|
async function loadSkillsMirrorPrefs(dataDir) {
|
|
99377
99735
|
try {
|
|
@@ -99420,7 +99778,7 @@ async function reconcileSkillsMirror(args) {
|
|
|
99420
99778
|
// src/services/storage/daemon-settings-store.ts
|
|
99421
99779
|
import { createHash as createHash16 } from "crypto";
|
|
99422
99780
|
import { mkdir as mkdir42, readFile as readFile52 } from "fs/promises";
|
|
99423
|
-
import { join as
|
|
99781
|
+
import { join as join85 } from "path";
|
|
99424
99782
|
var ProjectSettingsSchema = external_exports.object({
|
|
99425
99783
|
disabledMcpServers: external_exports.array(external_exports.string()).optional(),
|
|
99426
99784
|
previewMigrationV36Done: external_exports.boolean().default(false)
|
|
@@ -99429,9 +99787,9 @@ function hashProjectPath(projectPath) {
|
|
|
99429
99787
|
return createHash16("sha256").update(projectPath).digest("hex").slice(0, 16);
|
|
99430
99788
|
}
|
|
99431
99789
|
function buildDaemonSettingsStore(dataDir) {
|
|
99432
|
-
const settingsDir =
|
|
99790
|
+
const settingsDir = join85(dataDir, "settings");
|
|
99433
99791
|
function settingsPath(projectPath) {
|
|
99434
|
-
return
|
|
99792
|
+
return join85(settingsDir, `${hashProjectPath(projectPath)}.json`);
|
|
99435
99793
|
}
|
|
99436
99794
|
async function ensureDir() {
|
|
99437
99795
|
await mkdir42(settingsDir, { recursive: true });
|
|
@@ -99456,12 +99814,12 @@ function buildDaemonSettingsStore(dataDir) {
|
|
|
99456
99814
|
|
|
99457
99815
|
// src/services/storage/plugin-config-store.ts
|
|
99458
99816
|
import { mkdir as mkdir43, readFile as readFile53 } from "fs/promises";
|
|
99459
|
-
import { join as
|
|
99817
|
+
import { join as join86 } from "path";
|
|
99460
99818
|
function buildPluginConfigStore(pluginsDir) {
|
|
99461
99819
|
const cache2 = /* @__PURE__ */ new Map();
|
|
99462
99820
|
const writeQueues = /* @__PURE__ */ new Map();
|
|
99463
99821
|
function configPath(pluginId) {
|
|
99464
|
-
return
|
|
99822
|
+
return join86(pluginsDir, pluginId, "config.json");
|
|
99465
99823
|
}
|
|
99466
99824
|
async function readConfigFromDisk(pluginId) {
|
|
99467
99825
|
const fp = configPath(pluginId);
|
|
@@ -99515,7 +99873,7 @@ function buildPluginConfigStore(pluginsDir) {
|
|
|
99515
99873
|
const fresh = await readConfigFromDisk(pluginId);
|
|
99516
99874
|
fresh[key] = value;
|
|
99517
99875
|
cache2.set(pluginId, fresh);
|
|
99518
|
-
await mkdir43(
|
|
99876
|
+
await mkdir43(join86(pluginsDir, pluginId), { recursive: true });
|
|
99519
99877
|
await atomicWriteFile(configPath(pluginId), JSON.stringify(fresh, null, 2));
|
|
99520
99878
|
})
|
|
99521
99879
|
);
|
|
@@ -99761,7 +100119,7 @@ async function serve(options = {}) {
|
|
|
99761
100119
|
}
|
|
99762
100120
|
async function runServeBody(options, captureRefs) {
|
|
99763
100121
|
const shipyardHome = options.shipyardHome ?? getShipyardHome();
|
|
99764
|
-
const dataDir =
|
|
100122
|
+
const dataDir = join87(shipyardHome, options.isDev ? "data-dev" : "data");
|
|
99765
100123
|
const log = createChildLogger({ mode: "serve" });
|
|
99766
100124
|
log.info(
|
|
99767
100125
|
{ event: "daemon_start", version: getDaemonVersion(), pid: process.pid },
|
|
@@ -99769,7 +100127,7 @@ async function runServeBody(options, captureRefs) {
|
|
|
99769
100127
|
);
|
|
99770
100128
|
const { workspaceRoot, unscopedWorkspace, unscopedReason } = await resolveWorkspaceScope();
|
|
99771
100129
|
registerBuiltinPlugins();
|
|
99772
|
-
const pluginConfigStore = buildPluginConfigStore(
|
|
100130
|
+
const pluginConfigStore = buildPluginConfigStore(join87(dataDir, "plugins"));
|
|
99773
100131
|
await mkdir44(dataDir, { recursive: true });
|
|
99774
100132
|
log.info(
|
|
99775
100133
|
{
|
|
@@ -99862,9 +100220,9 @@ async function runServeBody(options, captureRefs) {
|
|
|
99862
100220
|
await bootstrapPhase("pid_sweep_orphans", () => pidTracker.sweepOrphans(logAdapter));
|
|
99863
100221
|
await bootstrapPhase(
|
|
99864
100222
|
"legacy_epoch_prune",
|
|
99865
|
-
() => pruneOldEpochData(
|
|
100223
|
+
() => pruneOldEpochData(join87(dataDir, "loro"), logAdapter)
|
|
99866
100224
|
);
|
|
99867
|
-
const storage = new FileStorageAdapter(
|
|
100225
|
+
const storage = new FileStorageAdapter(join87(dataDir, "loro"));
|
|
99868
100226
|
const personalWebrtcAdapter = new WebRtcDataChannelAdapter();
|
|
99869
100227
|
const peerRoleRegistry = createPeerRoleRegistry();
|
|
99870
100228
|
const presencePoolRef = { pool: {} };
|
|
@@ -99973,14 +100331,14 @@ async function runServeBody(options, captureRefs) {
|
|
|
99973
100331
|
current: []
|
|
99974
100332
|
};
|
|
99975
100333
|
const terminalPtys = /* @__PURE__ */ new Map();
|
|
99976
|
-
const terminalLogsDir =
|
|
100334
|
+
const terminalLogsDir = join87(shipyardHome, "data", "terminals");
|
|
99977
100335
|
await mkdir44(terminalLogsDir, { recursive: true, mode: 448 });
|
|
99978
100336
|
const publishedArtifactStore = createPublishedArtifactStore({
|
|
99979
|
-
rootDir:
|
|
100337
|
+
rootDir: join87(dataDir, "published"),
|
|
99980
100338
|
log: logAdapter
|
|
99981
100339
|
});
|
|
99982
100340
|
const previewStateStore = createPreviewStateStore({
|
|
99983
|
-
rootDir:
|
|
100341
|
+
rootDir: join87(dataDir, "preview-state"),
|
|
99984
100342
|
logger: log
|
|
99985
100343
|
});
|
|
99986
100344
|
let codexSkillsApplyScopedPatchRef = null;
|
|
@@ -100065,7 +100423,7 @@ async function runServeBody(options, captureRefs) {
|
|
|
100065
100423
|
daemon.healthMetrics.start();
|
|
100066
100424
|
publishedArtifactStore.setSendControlMessage((msg) => daemon.taskManager.broadcastControl(msg));
|
|
100067
100425
|
previewStateStore.setSendControlMessage((msg) => daemon.taskManager.broadcastControl(msg));
|
|
100068
|
-
const pluginsDir =
|
|
100426
|
+
const pluginsDir = join87(shipyardHome, "plugins");
|
|
100069
100427
|
await mkdir44(pluginsDir, { recursive: true });
|
|
100070
100428
|
let loadedPlugins = [];
|
|
100071
100429
|
const loadedPluginsRef = {
|
|
@@ -100261,6 +100619,7 @@ async function runServeBody(options, captureRefs) {
|
|
|
100261
100619
|
}
|
|
100262
100620
|
});
|
|
100263
100621
|
const fileIOHandlers = /* @__PURE__ */ new Set();
|
|
100622
|
+
const collabHostingStore = buildCollabHostingStore(dataDir);
|
|
100264
100623
|
const collabRoomManager = signalingHandle ? buildCollabRoomManager({
|
|
100265
100624
|
peerRoleRegistry,
|
|
100266
100625
|
repo,
|
|
@@ -100281,13 +100640,22 @@ async function runServeBody(options, captureRefs) {
|
|
|
100281
100640
|
fileWatcherPool,
|
|
100282
100641
|
terminalPtys,
|
|
100283
100642
|
terminalLogsDir,
|
|
100284
|
-
housekeepingBarrier
|
|
100643
|
+
housekeepingBarrier,
|
|
100644
|
+
collabHostingStore
|
|
100285
100645
|
}) : null;
|
|
100286
100646
|
if (collabRoomManager) {
|
|
100287
100647
|
daemon.setCollabParticipantsProvider(
|
|
100288
100648
|
(taskId) => collabRoomManager.getParticipantsForTask(taskId)
|
|
100289
100649
|
);
|
|
100290
100650
|
daemon.setCollabActiveTaskIdsProvider(() => collabRoomManager.listActiveTaskIds());
|
|
100651
|
+
startCollabRehydrate({
|
|
100652
|
+
collabRoomManager,
|
|
100653
|
+
store: collabHostingStore,
|
|
100654
|
+
signalingBaseUrl: auth3.signalingUrl,
|
|
100655
|
+
userToken: auth3.token,
|
|
100656
|
+
machineId: daemonPeerId,
|
|
100657
|
+
log: logAdapter
|
|
100658
|
+
});
|
|
100291
100659
|
}
|
|
100292
100660
|
const portDetectorRef = { current: null };
|
|
100293
100661
|
const onAttachDataChannelFailed = createAttachFailureCanary(log, logAdapter);
|
|
@@ -100626,4 +100994,4 @@ export {
|
|
|
100626
100994
|
decideWorkspaceScope,
|
|
100627
100995
|
serve
|
|
100628
100996
|
};
|
|
100629
|
-
//# sourceMappingURL=serve-
|
|
100997
|
+
//# sourceMappingURL=serve-25I4ML7R.js.map
|