@chrysb/alphaclaw 0.8.3-beta.2 → 0.8.3-beta.4
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.
|
@@ -2,6 +2,7 @@ import { h } from "preact";
|
|
|
2
2
|
import {
|
|
3
3
|
useCallback,
|
|
4
4
|
useEffect,
|
|
5
|
+
useLayoutEffect,
|
|
5
6
|
useMemo,
|
|
6
7
|
useRef,
|
|
7
8
|
useState,
|
|
@@ -17,6 +18,20 @@ const kNewChatEventName = "alphaclaw:chat-new";
|
|
|
17
18
|
const kWsReconnectMaxAttempts = 8;
|
|
18
19
|
const kAutoscrollBottomThresholdPx = 40;
|
|
19
20
|
const kChatDebugQueryFlag = "chatDebug";
|
|
21
|
+
const kComposerMaxLines = 5;
|
|
22
|
+
const kComposerFontSizePx = 12;
|
|
23
|
+
const kComposerLineHeight = 1.4;
|
|
24
|
+
const kComposerPaddingYPx = 20;
|
|
25
|
+
|
|
26
|
+
const resizeComposerTextarea = (element) => {
|
|
27
|
+
if (!element) return;
|
|
28
|
+
const linePx = kComposerFontSizePx * kComposerLineHeight;
|
|
29
|
+
const minH = linePx + kComposerPaddingYPx;
|
|
30
|
+
const maxH = linePx * kComposerMaxLines + kComposerPaddingYPx;
|
|
31
|
+
element.style.height = "auto";
|
|
32
|
+
const next = Math.min(Math.max(element.scrollHeight, minH), maxH);
|
|
33
|
+
element.style.height = `${next}px`;
|
|
34
|
+
};
|
|
20
35
|
|
|
21
36
|
const buildMessage = ({
|
|
22
37
|
role = "assistant",
|
|
@@ -174,8 +189,10 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
174
189
|
const [activeRunBySession, setActiveRunBySession] = useState({});
|
|
175
190
|
const [connectionError, setConnectionError] = useState("");
|
|
176
191
|
const [historyLoading, setHistoryLoading] = useState(false);
|
|
192
|
+
const [assistantStreamStarted, setAssistantStreamStarted] = useState(false);
|
|
177
193
|
const wsRef = useRef(null);
|
|
178
194
|
const threadRef = useRef(null);
|
|
195
|
+
const composerRef = useRef(null);
|
|
179
196
|
const reconnectTimerRef = useRef(null);
|
|
180
197
|
const reconnectAttemptsRef = useRef(0);
|
|
181
198
|
const selectedSessionKeyRef = useRef(selectedSessionKey);
|
|
@@ -206,6 +223,14 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
206
223
|
selectedSessionKeyRef.current = selectedSessionKey;
|
|
207
224
|
}, [selectedSessionKey]);
|
|
208
225
|
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
setAssistantStreamStarted(false);
|
|
228
|
+
}, [selectedSessionKey]);
|
|
229
|
+
|
|
230
|
+
useLayoutEffect(() => {
|
|
231
|
+
resizeComposerTextarea(composerRef.current);
|
|
232
|
+
}, [draft, selectedSessionKey]);
|
|
233
|
+
|
|
209
234
|
useEffect(() => {
|
|
210
235
|
if (!selectedSessionKey) return;
|
|
211
236
|
setDraft(String(draftBySession[selectedSessionKey] || ""));
|
|
@@ -294,6 +319,7 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
294
319
|
setIsConnected(false);
|
|
295
320
|
setStreaming(false);
|
|
296
321
|
setSending(false);
|
|
322
|
+
setAssistantStreamStarted(false);
|
|
297
323
|
setHistoryLoading(false);
|
|
298
324
|
if (realtimeDisabledRef.current) return;
|
|
299
325
|
if (reconnectAttemptsRef.current >= kWsReconnectMaxAttempts) return;
|
|
@@ -368,6 +394,7 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
368
394
|
if (!chunkSessionKey || !messageId) return;
|
|
369
395
|
setSending(false);
|
|
370
396
|
setStreaming(true);
|
|
397
|
+
setAssistantStreamStarted(true);
|
|
371
398
|
setMessagesBySession((currentMap) => {
|
|
372
399
|
const currentMessages = currentMap[chunkSessionKey] || [];
|
|
373
400
|
const lastMessage = currentMessages[currentMessages.length - 1];
|
|
@@ -424,6 +451,8 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
424
451
|
payload.sessionKey || selectedSessionKeyRef.current || "",
|
|
425
452
|
);
|
|
426
453
|
if (!toolSessionKey) return;
|
|
454
|
+
setSending(false);
|
|
455
|
+
setAssistantStreamStarted(true);
|
|
427
456
|
const toolPhase = String(payload.phase || "").toLowerCase();
|
|
428
457
|
const toolCall =
|
|
429
458
|
payload?.toolCall && typeof payload.toolCall === "object"
|
|
@@ -552,6 +581,7 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
552
581
|
);
|
|
553
582
|
const runId = String(payload.runId || "");
|
|
554
583
|
if (!nextSessionKey || !runId) return;
|
|
584
|
+
setSending(false);
|
|
555
585
|
setActiveRunBySession((currentMap) => ({
|
|
556
586
|
...currentMap,
|
|
557
587
|
[nextSessionKey]: runId,
|
|
@@ -572,6 +602,7 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
572
602
|
}
|
|
573
603
|
setSending(false);
|
|
574
604
|
setStreaming(false);
|
|
605
|
+
setAssistantStreamStarted(false);
|
|
575
606
|
setHistoryLoading(false);
|
|
576
607
|
if (doneSessionKey && ws && ws.readyState === 1) {
|
|
577
608
|
setHistoryLoading(true);
|
|
@@ -592,6 +623,7 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
592
623
|
if (payload.type === "error") {
|
|
593
624
|
setSending(false);
|
|
594
625
|
setStreaming(false);
|
|
626
|
+
setAssistantStreamStarted(false);
|
|
595
627
|
setHistoryLoading(false);
|
|
596
628
|
const errorSessionKey = String(
|
|
597
629
|
payload.sessionKey || selectedSessionKeyRef.current || "",
|
|
@@ -791,6 +823,7 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
791
823
|
...currentMap,
|
|
792
824
|
[selectedSessionKey]: "",
|
|
793
825
|
}));
|
|
826
|
+
setAssistantStreamStarted(false);
|
|
794
827
|
setSending(true);
|
|
795
828
|
setMessagesBySession((currentMap) => ({
|
|
796
829
|
...currentMap,
|
|
@@ -829,8 +862,20 @@ export const ChatRoute = ({ sessions = [], selectedSessionKey = "" }) => {
|
|
|
829
862
|
});
|
|
830
863
|
setStreaming(false);
|
|
831
864
|
setSending(false);
|
|
865
|
+
setAssistantStreamStarted(false);
|
|
832
866
|
}, [appendDebugEvent, selectedSessionKey]);
|
|
833
867
|
|
|
868
|
+
const handleComposerKeyDown = useCallback(
|
|
869
|
+
(event) => {
|
|
870
|
+
if (event.key !== "Enter") return;
|
|
871
|
+
if (event.shiftKey) return;
|
|
872
|
+
if (event.isComposing) return;
|
|
873
|
+
event.preventDefault();
|
|
874
|
+
handleSend();
|
|
875
|
+
},
|
|
876
|
+
[handleSend],
|
|
877
|
+
);
|
|
878
|
+
|
|
834
879
|
const rawHistory = selectedSessionKey
|
|
835
880
|
? rawHistoryBySession[selectedSessionKey]
|
|
836
881
|
: null;
|
|
@@ -1024,14 +1069,12 @@ ${JSON.stringify(
|
|
|
1024
1069
|
})()}
|
|
1025
1070
|
`,
|
|
1026
1071
|
)}
|
|
1027
|
-
${selectedSessionKey &&
|
|
1072
|
+
${selectedSessionKey &&
|
|
1073
|
+
(sending || streaming) &&
|
|
1074
|
+
!assistantStreamStarted
|
|
1028
1075
|
? html`
|
|
1029
1076
|
<div class="chat-bubble is-assistant chat-typing-indicator">
|
|
1030
|
-
<div class="chat-
|
|
1031
|
-
<span>Agent</span>
|
|
1032
|
-
<span>${isConnected ? "typing..." : "reconnecting..."}</span>
|
|
1033
|
-
</div>
|
|
1034
|
-
<div class="chat-typing-dots">
|
|
1077
|
+
<div class="chat-typing-dots" aria-hidden="true">
|
|
1035
1078
|
<span></span><span></span><span></span>
|
|
1036
1079
|
</div>
|
|
1037
1080
|
</div>
|
|
@@ -1055,12 +1098,15 @@ ${JSON.stringify(
|
|
|
1055
1098
|
<div class="chat-composer">
|
|
1056
1099
|
<textarea
|
|
1057
1100
|
class="chat-composer-input"
|
|
1101
|
+
ref=${composerRef}
|
|
1102
|
+
rows=${1}
|
|
1058
1103
|
placeholder=${selectedSessionKey
|
|
1059
|
-
? "
|
|
1104
|
+
? "Message… (Enter to send, Shift+Enter for newline)"
|
|
1060
1105
|
: "Select a session to start"}
|
|
1061
1106
|
value=${draft}
|
|
1062
1107
|
disabled=${!selectedSessionKey || sending || !isConnected}
|
|
1063
1108
|
oninput=${handleDraftInput}
|
|
1109
|
+
onkeydown=${handleComposerKeyDown}
|
|
1064
1110
|
></textarea>
|
|
1065
1111
|
<div class="chat-composer-actions">
|
|
1066
1112
|
${streaming
|
|
@@ -1085,7 +1131,7 @@ ${JSON.stringify(
|
|
|
1085
1131
|
!String(draft || "").trim()}
|
|
1086
1132
|
onclick=${handleSend}
|
|
1087
1133
|
>
|
|
1088
|
-
${sending
|
|
1134
|
+
${sending ? "Sending..." : "Send"}
|
|
1089
1135
|
</button>
|
|
1090
1136
|
</div>
|
|
1091
1137
|
</div>
|
package/lib/server/chat-ws.js
CHANGED
|
@@ -354,6 +354,10 @@ const createChatWsService = ({
|
|
|
354
354
|
let gatewayConnectPromise = null;
|
|
355
355
|
const pendingGatewayRequests = new Map();
|
|
356
356
|
const runTargets = new Map();
|
|
357
|
+
/** While `chat.send` is in flight, agent events can arrive before we have `runId` + runTargets — match by session. */
|
|
358
|
+
const pendingSessionBySessionKey = new Map();
|
|
359
|
+
/** Agent events keyed by runId when the event references a run not yet in runTargets (race with chat.send response). */
|
|
360
|
+
const pendingAgentEventsByRunId = new Map();
|
|
357
361
|
const browserRuns = new WeakMap();
|
|
358
362
|
|
|
359
363
|
const sendJson = (ws, payload = {}) => {
|
|
@@ -382,6 +386,9 @@ const createChatWsService = ({
|
|
|
382
386
|
};
|
|
383
387
|
|
|
384
388
|
const clearRunTargetsForBrowser = (ws) => {
|
|
389
|
+
for (const [sk, pending] of pendingSessionBySessionKey.entries()) {
|
|
390
|
+
if (pending.ws === ws) pendingSessionBySessionKey.delete(sk);
|
|
391
|
+
}
|
|
385
392
|
const runs = browserRuns.get(ws);
|
|
386
393
|
if (!runs) return;
|
|
387
394
|
for (const runId of runs) runTargets.delete(runId);
|
|
@@ -416,6 +423,7 @@ const createChatWsService = ({
|
|
|
416
423
|
const markGatewayDisconnected = (reason = "Gateway disconnected") => {
|
|
417
424
|
gatewaySocket = null;
|
|
418
425
|
gatewayConnectPromise = null;
|
|
426
|
+
pendingAgentEventsByRunId.clear();
|
|
419
427
|
rejectAllGatewayRequests(reason);
|
|
420
428
|
};
|
|
421
429
|
|
|
@@ -436,6 +444,17 @@ const createChatWsService = ({
|
|
|
436
444
|
sessionTarget = targetRow;
|
|
437
445
|
}
|
|
438
446
|
if (sessionTarget) return { runId: "", target: sessionTarget };
|
|
447
|
+
const pending = pendingSessionBySessionKey.get(sessionKey);
|
|
448
|
+
if (pending) {
|
|
449
|
+
return {
|
|
450
|
+
runId: resolveRunIdFromPayload(payload),
|
|
451
|
+
target: {
|
|
452
|
+
ws: pending.ws,
|
|
453
|
+
messageId: pending.messageId,
|
|
454
|
+
sessionKey,
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
}
|
|
439
458
|
}
|
|
440
459
|
if (runTargets.size === 1) {
|
|
441
460
|
for (const [singleRunId, singleTarget] of runTargets.entries()) {
|
|
@@ -446,9 +465,60 @@ const createChatWsService = ({
|
|
|
446
465
|
};
|
|
447
466
|
if (eventName === "agent") {
|
|
448
467
|
const { runId, target } = resolveTargetForPayload();
|
|
449
|
-
if (!target)
|
|
468
|
+
if (!target) {
|
|
469
|
+
const runIdEarly = resolveRunIdFromPayload(payload);
|
|
470
|
+
if (runIdEarly && !runTargets.get(runIdEarly)) {
|
|
471
|
+
const list = pendingAgentEventsByRunId.get(runIdEarly) || [];
|
|
472
|
+
list.push(eventPayload);
|
|
473
|
+
pendingAgentEventsByRunId.set(runIdEarly, list);
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
450
477
|
const stream = String(payload?.stream || "");
|
|
451
478
|
const data = payload?.data || {};
|
|
479
|
+
if (stream === "tool") {
|
|
480
|
+
const toolPhase = String(data?.phase || "");
|
|
481
|
+
const toolName = String(data?.name || "unknown");
|
|
482
|
+
const toolCallId = String(data?.toolCallId || "");
|
|
483
|
+
if (toolPhase === "start") {
|
|
484
|
+
sendJson(target.ws, {
|
|
485
|
+
type: "tool",
|
|
486
|
+
phase: "call",
|
|
487
|
+
messageId: target.messageId,
|
|
488
|
+
sessionKey: target.sessionKey,
|
|
489
|
+
timestamp: Number(payload?.ts) || Date.now(),
|
|
490
|
+
toolCall: {
|
|
491
|
+
id: toolCallId,
|
|
492
|
+
name: toolName,
|
|
493
|
+
arguments: data?.args || null,
|
|
494
|
+
partialJson: "",
|
|
495
|
+
},
|
|
496
|
+
toolResult: null,
|
|
497
|
+
rawEvent: eventPayload || null,
|
|
498
|
+
});
|
|
499
|
+
} else if (toolPhase === "result") {
|
|
500
|
+
const resultText = collectTextFromUnknownShape(data?.result);
|
|
501
|
+
sendJson(target.ws, {
|
|
502
|
+
type: "tool",
|
|
503
|
+
phase: "result",
|
|
504
|
+
messageId: target.messageId,
|
|
505
|
+
sessionKey: target.sessionKey,
|
|
506
|
+
timestamp: Number(payload?.ts) || Date.now(),
|
|
507
|
+
toolCall: null,
|
|
508
|
+
toolResult: {
|
|
509
|
+
role: "toolResult",
|
|
510
|
+
toolCallId,
|
|
511
|
+
toolName,
|
|
512
|
+
content: resultText
|
|
513
|
+
? [{ type: "text", text: resultText }]
|
|
514
|
+
: [],
|
|
515
|
+
isError: data?.isError === true,
|
|
516
|
+
},
|
|
517
|
+
rawEvent: eventPayload || null,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
452
522
|
const toolCall =
|
|
453
523
|
extractToolCallFromUnknownShape(payload) ||
|
|
454
524
|
extractToolCallFromUnknownShape(data);
|
|
@@ -557,7 +627,7 @@ const createChatWsService = ({
|
|
|
557
627
|
},
|
|
558
628
|
role: "operator",
|
|
559
629
|
scopes: kGatewayChatBridgeScopes,
|
|
560
|
-
caps: [],
|
|
630
|
+
caps: ["tool-events"],
|
|
561
631
|
commands: [],
|
|
562
632
|
permissions: {},
|
|
563
633
|
auth: { token: getGatewayToken() },
|
|
@@ -689,13 +759,21 @@ const createChatWsService = ({
|
|
|
689
759
|
});
|
|
690
760
|
return;
|
|
691
761
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
762
|
+
pendingSessionBySessionKey.set(sessionKey, { ws, messageId });
|
|
763
|
+
let result;
|
|
764
|
+
try {
|
|
765
|
+
result = await requestGateway("chat.send", {
|
|
766
|
+
sessionKey,
|
|
767
|
+
message: content,
|
|
768
|
+
idempotencyKey: crypto.randomUUID(),
|
|
769
|
+
});
|
|
770
|
+
} catch (err) {
|
|
771
|
+
pendingSessionBySessionKey.delete(sessionKey);
|
|
772
|
+
throw err;
|
|
773
|
+
}
|
|
697
774
|
const runId = String(result?.runId || "").trim();
|
|
698
775
|
if (!runId) {
|
|
776
|
+
pendingSessionBySessionKey.delete(sessionKey);
|
|
699
777
|
sendJson(ws, {
|
|
700
778
|
type: "error",
|
|
701
779
|
message: "Something went wrong connecting to the agent.",
|
|
@@ -706,12 +784,20 @@ const createChatWsService = ({
|
|
|
706
784
|
}
|
|
707
785
|
runTargets.set(runId, { ws, messageId, sessionKey });
|
|
708
786
|
registerRunForBrowser(ws, runId);
|
|
787
|
+
pendingSessionBySessionKey.delete(sessionKey);
|
|
709
788
|
sendJson(ws, {
|
|
710
789
|
type: "started",
|
|
711
790
|
sessionKey,
|
|
712
791
|
runId,
|
|
713
792
|
messageId,
|
|
714
793
|
});
|
|
794
|
+
const buffered = pendingAgentEventsByRunId.get(runId);
|
|
795
|
+
if (buffered && buffered.length) {
|
|
796
|
+
pendingAgentEventsByRunId.delete(runId);
|
|
797
|
+
for (const stored of buffered) {
|
|
798
|
+
handleGatewayEvent(stored);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
715
801
|
};
|
|
716
802
|
|
|
717
803
|
const handleStop = async ({ ws, payload }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrysb/alphaclaw",
|
|
3
|
-
"version": "0.8.3-beta.
|
|
3
|
+
"version": "0.8.3-beta.4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"bin/",
|
|
22
22
|
"lib/",
|
|
23
|
-
"patches/"
|
|
23
|
+
"patches/",
|
|
24
|
+
"scripts/apply-openclaw-patches.js"
|
|
24
25
|
],
|
|
25
26
|
"scripts": {
|
|
26
27
|
"start": "node bin/alphaclaw.js start",
|
|
27
28
|
"build:ui": "node scripts/build-ui.mjs",
|
|
28
|
-
"postinstall": "
|
|
29
|
+
"postinstall": "node ./scripts/apply-openclaw-patches.js",
|
|
29
30
|
"test": "vitest run",
|
|
30
31
|
"test:watch": "vitest",
|
|
31
32
|
"test:watchdog": "vitest run tests/server/watchdog.test.js tests/server/watchdog-db.test.js tests/server/routes-watchdog.test.js",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* patch-package resolves paths relative to the npm/yarn project root (where the
|
|
3
|
+
* lockfile lives). When this package's postinstall runs, process.cwd() is often
|
|
4
|
+
* this package directory, so a plain `patch-package` call treats that as the
|
|
5
|
+
* app root and looks for ./node_modules/openclaw under it — but openclaw is
|
|
6
|
+
* usually hoisted to the consumer's top-level node_modules.
|
|
7
|
+
*
|
|
8
|
+
* This script finds the real install root (directory containing a lockfile) and
|
|
9
|
+
* runs patch-package there with --patch-dir pointing at our bundled patches/.
|
|
10
|
+
*/
|
|
11
|
+
const { spawnSync } = require("child_process");
|
|
12
|
+
const fs = require("fs");
|
|
13
|
+
const path = require("path");
|
|
14
|
+
|
|
15
|
+
const kAlphaclawRoot = path.join(__dirname, "..");
|
|
16
|
+
|
|
17
|
+
const findProjectRootFromOpenclawDir = (openclawDir) => {
|
|
18
|
+
let dir = path.resolve(openclawDir);
|
|
19
|
+
for (let i = 0; i < 30; i += 1) {
|
|
20
|
+
if (
|
|
21
|
+
fs.existsSync(path.join(dir, "package-lock.json")) ||
|
|
22
|
+
fs.existsSync(path.join(dir, "yarn.lock")) ||
|
|
23
|
+
fs.existsSync(path.join(dir, "pnpm-lock.yaml"))
|
|
24
|
+
) {
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
const parent = path.dirname(dir);
|
|
28
|
+
if (parent === dir) break;
|
|
29
|
+
dir = parent;
|
|
30
|
+
}
|
|
31
|
+
return path.dirname(path.dirname(openclawDir));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const main = () => {
|
|
35
|
+
const patchesDir = path.join(kAlphaclawRoot, "patches");
|
|
36
|
+
if (!fs.existsSync(patchesDir)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const hasPatch = fs
|
|
40
|
+
.readdirSync(patchesDir)
|
|
41
|
+
.some((name) => name.endsWith(".patch"));
|
|
42
|
+
if (!hasPatch) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let openclawMainPath;
|
|
47
|
+
try {
|
|
48
|
+
openclawMainPath = require.resolve("openclaw", { paths: [kAlphaclawRoot] });
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const openclawDir = (() => {
|
|
54
|
+
let dir = path.dirname(openclawMainPath);
|
|
55
|
+
for (let i = 0; i < 8; i += 1) {
|
|
56
|
+
const pkgPath = path.join(dir, "package.json");
|
|
57
|
+
if (fs.existsSync(pkgPath)) {
|
|
58
|
+
try {
|
|
59
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
60
|
+
if (pkg.name === "openclaw") return dir;
|
|
61
|
+
} catch {
|
|
62
|
+
/* continue */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const parent = path.dirname(dir);
|
|
66
|
+
if (parent === dir) break;
|
|
67
|
+
dir = parent;
|
|
68
|
+
}
|
|
69
|
+
return path.dirname(path.dirname(openclawMainPath));
|
|
70
|
+
})();
|
|
71
|
+
const projectRoot = findProjectRootFromOpenclawDir(openclawDir);
|
|
72
|
+
|
|
73
|
+
let relPatchDir = path.relative(projectRoot, patchesDir);
|
|
74
|
+
if (relPatchDir.startsWith("..") || path.isAbsolute(relPatchDir)) {
|
|
75
|
+
console.error(
|
|
76
|
+
"[@chrysb/alphaclaw] patch-package: could not resolve patch dir relative to project root",
|
|
77
|
+
);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
relPatchDir = relPatchDir.split(path.sep).join("/");
|
|
81
|
+
|
|
82
|
+
const patchPackageMain = require.resolve("patch-package/dist/index.js", {
|
|
83
|
+
paths: [kAlphaclawRoot],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const result = spawnSync(
|
|
87
|
+
process.execPath,
|
|
88
|
+
[patchPackageMain, "--patch-dir", relPatchDir],
|
|
89
|
+
{ cwd: projectRoot, stdio: "inherit", env: process.env },
|
|
90
|
+
);
|
|
91
|
+
if (result.error) {
|
|
92
|
+
throw result.error;
|
|
93
|
+
}
|
|
94
|
+
if (result.status !== 0 && result.status !== null) {
|
|
95
|
+
process.exit(result.status);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
main();
|