@rynx-ai/daemon 0.1.11-beta.37 → 0.1.11-beta.39
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/db.js
CHANGED
|
@@ -100,6 +100,7 @@ function migrate(conn) {
|
|
|
100
100
|
status TEXT NOT NULL,
|
|
101
101
|
data TEXT NOT NULL,
|
|
102
102
|
created_by TEXT,
|
|
103
|
+
parent_tool_call_id TEXT,
|
|
103
104
|
created_at INTEGER NOT NULL,
|
|
104
105
|
UNIQUE(session_id, position)
|
|
105
106
|
);
|
|
@@ -370,6 +371,7 @@ function migrate(conn) {
|
|
|
370
371
|
addColumnIfMissing(conn, "session_message_operations", "response_id", "TEXT");
|
|
371
372
|
addColumnIfMissing(conn, "session_message_operations", "message_item_id", "TEXT");
|
|
372
373
|
addColumnIfMissing(conn, "session_message_operations", "execution_snapshot", "TEXT");
|
|
374
|
+
addColumnIfMissing(conn, "session_items", "parent_tool_call_id", "TEXT");
|
|
373
375
|
addColumnIfMissing(conn, "session_portal_tickets", "grant_handle", "TEXT");
|
|
374
376
|
addColumnIfMissing(conn, "session_portal_tickets", "embed_origin", "TEXT");
|
|
375
377
|
addColumnIfMissing(conn, "session_portal_grants", "grant_handle", "TEXT");
|
|
@@ -42,6 +42,14 @@ const NETWORK_AUTO_ATTACH_FILTER = [
|
|
|
42
42
|
{ type: "shared_worker" },
|
|
43
43
|
{ exclude: true },
|
|
44
44
|
];
|
|
45
|
+
const NETWORK_AUTO_ATTACH_PARAMS = {
|
|
46
|
+
autoAttach: true,
|
|
47
|
+
// Pause new targets until Fetch interception is installed. This is required
|
|
48
|
+
// for Session Headers to cover a popup's very first navigation request.
|
|
49
|
+
waitForDebuggerOnStart: true,
|
|
50
|
+
flatten: true,
|
|
51
|
+
filter: NETWORK_AUTO_ATTACH_FILTER,
|
|
52
|
+
};
|
|
45
53
|
export function createHeadlessBrowserHost(options = {}) {
|
|
46
54
|
return new HeadlessBrowserHost(options);
|
|
47
55
|
}
|
|
@@ -235,12 +243,7 @@ class HeadlessBrowserHandle {
|
|
|
235
243
|
}
|
|
236
244
|
async initialize() {
|
|
237
245
|
await this.host.connection.command("Target.setDiscoverTargets", { discover: true });
|
|
238
|
-
await this.host.connection.command("Target.setAutoAttach",
|
|
239
|
-
autoAttach: true,
|
|
240
|
-
waitForDebuggerOnStart: true,
|
|
241
|
-
flatten: true,
|
|
242
|
-
filter: NETWORK_AUTO_ATTACH_FILTER,
|
|
243
|
-
});
|
|
246
|
+
await this.host.connection.command("Target.setAutoAttach", NETWORK_AUTO_ATTACH_PARAMS);
|
|
244
247
|
await this.createFreshStartupPage();
|
|
245
248
|
await this.refreshTargets();
|
|
246
249
|
const requestedUrl = this.host.input.url;
|
|
@@ -433,14 +436,28 @@ class HeadlessBrowserHandle {
|
|
|
433
436
|
this.closing = true;
|
|
434
437
|
this.closePromise = (async () => {
|
|
435
438
|
await Promise.allSettled([...this.surfaceSources.values()].map((source) => source.close()));
|
|
436
|
-
this.host.connection.close();
|
|
437
439
|
let terminated = false;
|
|
438
440
|
try {
|
|
439
|
-
|
|
441
|
+
let exited = false;
|
|
442
|
+
if (this.host.connection.connected) {
|
|
443
|
+
const closeCommand = this.host.connection.command("Browser.close")
|
|
444
|
+
.catch(() => undefined);
|
|
445
|
+
exited = await Promise.race([
|
|
446
|
+
this.host.exit.then(() => true),
|
|
447
|
+
delay(this.host.terminationGraceMs).then(() => false),
|
|
448
|
+
]);
|
|
449
|
+
if (!exited)
|
|
450
|
+
this.host.connection.close();
|
|
451
|
+
await closeCommand;
|
|
452
|
+
}
|
|
453
|
+
if (!exited) {
|
|
454
|
+
await terminateProcessGroup(this.host.child, this.host.exit, this.host.terminationGraceMs);
|
|
455
|
+
}
|
|
440
456
|
removeMatchingProcessLease(this.host.profileDir, this.host.ownerToken);
|
|
441
457
|
terminated = true;
|
|
442
458
|
}
|
|
443
459
|
finally {
|
|
460
|
+
this.host.connection.close();
|
|
444
461
|
if (terminated)
|
|
445
462
|
this.host.release();
|
|
446
463
|
this.eventQueue.end();
|
|
@@ -501,11 +518,19 @@ class HeadlessBrowserHandle {
|
|
|
501
518
|
try {
|
|
502
519
|
const targets = await this.readPageTargets();
|
|
503
520
|
const next = new Map();
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
521
|
+
const orderedTargets = [
|
|
522
|
+
...targets.filter((target) => this.targets.has(target.targetId)),
|
|
523
|
+
...targets.filter((target) => !this.targets.has(target.targetId)),
|
|
524
|
+
];
|
|
525
|
+
for (const target of orderedTargets) {
|
|
526
|
+
if (this.closingTargetIds.has(target.targetId))
|
|
527
|
+
continue;
|
|
528
|
+
if (next.size < RUNTIME_BROWSER_MAX_PAGES) {
|
|
529
|
+
next.set(target.targetId, target);
|
|
530
|
+
continue;
|
|
507
531
|
}
|
|
508
|
-
|
|
532
|
+
this.closingTargetIds.add(target.targetId);
|
|
533
|
+
await this.networkExclusive(() => this.closeRejectedTarget(undefined, target, false));
|
|
509
534
|
}
|
|
510
535
|
for (const targetId of this.targets.keys()) {
|
|
511
536
|
if (!next.has(targetId))
|
|
@@ -700,6 +725,13 @@ class HeadlessBrowserHandle {
|
|
|
700
725
|
this.targetBySession.set(sessionId, target.targetId);
|
|
701
726
|
this.networkTargetTypeBySession.set(sessionId, target.type);
|
|
702
727
|
if (target.type === "page") {
|
|
728
|
+
if (this.closingTargetIds.has(target.targetId) ||
|
|
729
|
+
(!this.targets.has(target.targetId) &&
|
|
730
|
+
this.targets.size >= RUNTIME_BROWSER_MAX_PAGES)) {
|
|
731
|
+
this.closingTargetIds.add(target.targetId);
|
|
732
|
+
void this.networkExclusive(() => this.closeRejectedTarget(sessionId, target, waitingForDebugger)).catch(() => undefined);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
703
735
|
this.targets.set(target.targetId, target);
|
|
704
736
|
let pending;
|
|
705
737
|
pending = this.networkExclusive(() => this.initializeAttachedTarget(sessionId, target, waitingForDebugger))
|
|
@@ -744,6 +776,16 @@ class HeadlessBrowserHandle {
|
|
|
744
776
|
const target = parseTargetInfo(event.params.targetInfo);
|
|
745
777
|
if (target.type !== "page")
|
|
746
778
|
return;
|
|
779
|
+
if (event.method === "Target.targetCreated" &&
|
|
780
|
+
!this.targets.has(target.targetId) &&
|
|
781
|
+
this.targets.size >= RUNTIME_BROWSER_MAX_PAGES) {
|
|
782
|
+
this.closingTargetIds.add(target.targetId);
|
|
783
|
+
void this.networkExclusive(() => this.closeRejectedTarget(undefined, target, false))
|
|
784
|
+
.catch(() => undefined);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (this.closingTargetIds.has(target.targetId))
|
|
788
|
+
return;
|
|
747
789
|
this.targets.set(target.targetId, target);
|
|
748
790
|
if (event.method === "Target.targetCreated")
|
|
749
791
|
this.activeTargetId = target.targetId;
|
|
@@ -765,7 +807,12 @@ class HeadlessBrowserHandle {
|
|
|
765
807
|
if (event.method === "Target.targetCrashed") {
|
|
766
808
|
const targetId = optionalCdpId(event.params.targetId);
|
|
767
809
|
if (targetId && this.targets.has(targetId)) {
|
|
768
|
-
this.
|
|
810
|
+
const target = this.targets.get(targetId);
|
|
811
|
+
this.closingTargetIds.add(targetId);
|
|
812
|
+
this.dropTarget(targetId);
|
|
813
|
+
void this.networkExclusive(() => this.closeRejectedTarget(undefined, target, false))
|
|
814
|
+
.catch(() => undefined);
|
|
815
|
+
this.eventQueue.changed(this.browserGeneration);
|
|
769
816
|
}
|
|
770
817
|
return;
|
|
771
818
|
}
|
|
@@ -778,6 +825,10 @@ class HeadlessBrowserHandle {
|
|
|
778
825
|
const targetId = event.sessionId ? this.targetBySession.get(event.sessionId) : undefined;
|
|
779
826
|
if (!targetId)
|
|
780
827
|
return;
|
|
828
|
+
if (event.method === "Page.javascriptDialogOpening") {
|
|
829
|
+
void this.dismissJavaScriptDialog(event.sessionId, targetId);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
781
832
|
if (event.method === "Inspector.detached") {
|
|
782
833
|
this.dropPageSession(event.sessionId);
|
|
783
834
|
return;
|
|
@@ -856,17 +907,14 @@ class HeadlessBrowserHandle {
|
|
|
856
907
|
}
|
|
857
908
|
async initializeAttachedTarget(sessionId, target, waitingForDebugger) {
|
|
858
909
|
try {
|
|
859
|
-
|
|
910
|
+
// Fetch.enable succeeds while a waitForDebugger target is paused and must
|
|
911
|
+
// precede its first request. Network.enable does not: current Chrome for
|
|
912
|
+
// Testing builds leave that command pending until the target is resumed.
|
|
860
913
|
await this.host.connection.command("Fetch.enable", {
|
|
861
914
|
patterns: [{ urlPattern: "*", requestStage: "Request" }],
|
|
862
915
|
}, sessionId);
|
|
863
916
|
if (target.type === "page" || target.type === "iframe") {
|
|
864
|
-
await this.host.connection.command("Target.setAutoAttach",
|
|
865
|
-
autoAttach: true,
|
|
866
|
-
waitForDebuggerOnStart: true,
|
|
867
|
-
flatten: true,
|
|
868
|
-
filter: NETWORK_AUTO_ATTACH_FILTER,
|
|
869
|
-
}, sessionId);
|
|
917
|
+
await this.host.connection.command("Target.setAutoAttach", NETWORK_AUTO_ATTACH_PARAMS, sessionId);
|
|
870
918
|
}
|
|
871
919
|
if (target.type === "page") {
|
|
872
920
|
await this.host.connection.command("Page.enable", {}, sessionId);
|
|
@@ -874,14 +922,52 @@ class HeadlessBrowserHandle {
|
|
|
874
922
|
if (waitingForDebugger) {
|
|
875
923
|
await this.host.connection.command("Runtime.runIfWaitingForDebugger", {}, sessionId);
|
|
876
924
|
}
|
|
925
|
+
await this.host.connection.command("Network.enable", {}, sessionId);
|
|
877
926
|
}
|
|
878
927
|
catch (error) {
|
|
879
928
|
if (isMissingTargetError(error) || isTransientPageTransitionError(error)) {
|
|
880
929
|
this.dropAttachedSession(sessionId);
|
|
881
930
|
throw missingPage(error);
|
|
882
931
|
}
|
|
883
|
-
this.
|
|
884
|
-
|
|
932
|
+
if (!this.host.connection.connected || error instanceof CdpDisconnectedError) {
|
|
933
|
+
this.markUnavailable("The native CDP observer disconnected");
|
|
934
|
+
throw this.mapOperationError(error);
|
|
935
|
+
}
|
|
936
|
+
await this.closeRejectedTarget(sessionId, target, waitingForDebugger);
|
|
937
|
+
if (target.type === "page")
|
|
938
|
+
throw missingPage(error);
|
|
939
|
+
throw unavailable("Headless Browser isolated a network target that failed setup", error);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
async closeRejectedTarget(sessionId, target, waitingForDebugger) {
|
|
943
|
+
if (target.type === "page") {
|
|
944
|
+
this.closingTargetIds.add(target.targetId);
|
|
945
|
+
this.dropTarget(target.targetId);
|
|
946
|
+
}
|
|
947
|
+
else if (sessionId) {
|
|
948
|
+
this.dropAttachedSession(sessionId);
|
|
949
|
+
}
|
|
950
|
+
let closed = false;
|
|
951
|
+
try {
|
|
952
|
+
if (this.host.connection.connected) {
|
|
953
|
+
const result = await this.host.connection.command("Target.closeTarget", {
|
|
954
|
+
targetId: target.targetId,
|
|
955
|
+
});
|
|
956
|
+
closed = result.success === true;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
catch (error) {
|
|
960
|
+
if (!isMissingTargetError(error) && !isTransientPageTransitionError(error)) {
|
|
961
|
+
if (!this.host.connection.connected || error instanceof CdpDisconnectedError) {
|
|
962
|
+
this.markUnavailable("The native CDP observer disconnected");
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (!closed && waitingForDebugger && sessionId && this.host.connection.connected) {
|
|
967
|
+
await this.host.connection.command("Runtime.runIfWaitingForDebugger", {}, sessionId).catch(() => undefined);
|
|
968
|
+
}
|
|
969
|
+
if (!closed && sessionId && this.host.connection.connected) {
|
|
970
|
+
await this.host.connection.command("Target.detachFromTarget", { sessionId }).catch(() => undefined);
|
|
885
971
|
}
|
|
886
972
|
}
|
|
887
973
|
async detachAuxiliaryPageSession(sessionId) {
|
|
@@ -898,6 +984,27 @@ class HeadlessBrowserHandle {
|
|
|
898
984
|
this.dropAttachedSession(sessionId);
|
|
899
985
|
}
|
|
900
986
|
}
|
|
987
|
+
async dismissJavaScriptDialog(sessionId, targetId) {
|
|
988
|
+
try {
|
|
989
|
+
// Rynx has no native dialog presentation surface. Dismiss every modal
|
|
990
|
+
// deterministically so alert/confirm/prompt/beforeunload cannot block all
|
|
991
|
+
// later CDP commands for this Page.
|
|
992
|
+
await this.host.connection.command("Page.handleJavaScriptDialog", { accept: false }, sessionId);
|
|
993
|
+
}
|
|
994
|
+
catch (error) {
|
|
995
|
+
if (isMissingTargetError(error) || isTransientPageTransitionError(error)) {
|
|
996
|
+
this.dropAttachedSession(sessionId);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
if (!this.host.connection.connected || error instanceof CdpDisconnectedError) {
|
|
1000
|
+
this.markUnavailable("The native CDP observer disconnected");
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const target = this.targets.get(targetId);
|
|
1004
|
+
if (target)
|
|
1005
|
+
await this.closeRejectedTarget(sessionId, target, false);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
901
1008
|
async continuePausedRequest(sessionId, params) {
|
|
902
1009
|
const requestId = requiredCdpId(params.requestId, "Fetch.requestPaused requestId");
|
|
903
1010
|
const config = this.requestHeaders;
|
|
@@ -194,10 +194,10 @@ export class SqliteSessionForkStore {
|
|
|
194
194
|
}
|
|
195
195
|
const insertItem = conn.prepare(`INSERT INTO session_items (
|
|
196
196
|
id, session_id, position, response_id, type, status,
|
|
197
|
-
data, created_by, created_at
|
|
197
|
+
data, created_by, parent_tool_call_id, created_at
|
|
198
198
|
) VALUES (
|
|
199
199
|
@id, @session_id, @position, @response_id, @type, @status,
|
|
200
|
-
@data, @created_by, @created_at
|
|
200
|
+
@data, @created_by, @parent_tool_call_id, @created_at
|
|
201
201
|
)`);
|
|
202
202
|
for (const item of clonedItems) {
|
|
203
203
|
insertItem.run({
|
|
@@ -209,6 +209,7 @@ export class SqliteSessionForkStore {
|
|
|
209
209
|
status: item.status,
|
|
210
210
|
data: JSON.stringify(item.data),
|
|
211
211
|
created_by: item.createdBy ?? null,
|
|
212
|
+
parent_tool_call_id: item.parentToolCallId ?? null,
|
|
212
213
|
created_at: item.createdAt,
|
|
213
214
|
});
|
|
214
215
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { SessionLogAppendError } from "@rynx-ai/core";
|
|
1
2
|
import { db } from "./db.js";
|
|
2
|
-
import { transaction } from "./sqlite.js";
|
|
3
|
+
import { hasSqlitePrimaryCode, SQLITE_CONSTRAINT, transaction } from "./sqlite.js";
|
|
3
4
|
function toItem(row) {
|
|
4
5
|
return {
|
|
5
6
|
id: row.id,
|
|
@@ -9,6 +10,9 @@ function toItem(row) {
|
|
|
9
10
|
status: row.status,
|
|
10
11
|
createdAt: row.created_at,
|
|
11
12
|
...(row.created_by ? { createdBy: row.created_by } : {}),
|
|
13
|
+
...(row.parent_tool_call_id
|
|
14
|
+
? { parentToolCallId: row.parent_tool_call_id }
|
|
15
|
+
: {}),
|
|
12
16
|
type: row.type,
|
|
13
17
|
data: JSON.parse(row.data),
|
|
14
18
|
};
|
|
@@ -19,8 +23,10 @@ export class SqliteSessionLogStore {
|
|
|
19
23
|
return [];
|
|
20
24
|
const conn = db();
|
|
21
25
|
const insert = conn.prepare(`INSERT INTO session_items
|
|
22
|
-
(id, session_id, position, response_id, type, status, data, created_by,
|
|
23
|
-
|
|
26
|
+
(id, session_id, position, response_id, type, status, data, created_by,
|
|
27
|
+
parent_tool_call_id, created_at)
|
|
28
|
+
VALUES (@id, @session_id, @position, @response_id, @type, @status, @data,
|
|
29
|
+
@created_by, @parent_tool_call_id, @created_at)`);
|
|
24
30
|
const maxStmt = conn.prepare("SELECT MAX(position) AS max FROM session_items WHERE session_id = ?");
|
|
25
31
|
const tx = transaction(conn, (batch) => {
|
|
26
32
|
const { max } = maxStmt.get(sessionId);
|
|
@@ -36,6 +42,7 @@ export class SqliteSessionLogStore {
|
|
|
36
42
|
status: stored.status,
|
|
37
43
|
data: JSON.stringify(stored.data),
|
|
38
44
|
created_by: stored.createdBy ?? null,
|
|
45
|
+
parent_tool_call_id: stored.parentToolCallId ?? null,
|
|
39
46
|
created_at: stored.createdAt,
|
|
40
47
|
});
|
|
41
48
|
const resourceIds = stored.type === "message" && stored.data.role === "user"
|
|
@@ -54,10 +61,14 @@ export class SqliteSessionLogStore {
|
|
|
54
61
|
const resource = conn.prepare(`SELECT state, message_item_id
|
|
55
62
|
FROM session_resources
|
|
56
63
|
WHERE id = ? AND session_id = ?`).get(resourceId, sessionId);
|
|
57
|
-
if (!resource
|
|
58
|
-
resource
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
if (!resource) {
|
|
65
|
+
throw new SessionLogAppendError(`Session resource ${resourceId} does not exist`, 422, "session_resource_missing");
|
|
66
|
+
}
|
|
67
|
+
if (resource.state === "uploading") {
|
|
68
|
+
throw new SessionLogAppendError(`Session resource ${resourceId} is still uploading`, 409, "session_resource_not_ready");
|
|
69
|
+
}
|
|
70
|
+
if (resource.message_item_id && resource.message_item_id !== stored.id) {
|
|
71
|
+
throw new SessionLogAppendError(`Session resource ${resourceId} is already committed to another item`, 422, "session_resource_already_committed");
|
|
61
72
|
}
|
|
62
73
|
conn.prepare(`UPDATE session_resources
|
|
63
74
|
SET state = 'committed', message_item_id = ?,
|
|
@@ -68,7 +79,17 @@ export class SqliteSessionLogStore {
|
|
|
68
79
|
return stored;
|
|
69
80
|
});
|
|
70
81
|
});
|
|
71
|
-
|
|
82
|
+
try {
|
|
83
|
+
return tx(items);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error instanceof SessionLogAppendError)
|
|
87
|
+
throw error;
|
|
88
|
+
if (hasSqlitePrimaryCode(error, SQLITE_CONSTRAINT)) {
|
|
89
|
+
throw new SessionLogAppendError(error instanceof Error ? error.message : "Session log constraint rejected the item", 422, "session_log_constraint", { cause: error });
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
async list(sessionId, opts = {}) {
|
|
74
95
|
const conn = db();
|
|
@@ -1008,7 +1008,7 @@ function invalid(message) {
|
|
|
1008
1008
|
return new MachineSessionServiceFailure("failed_precondition", message);
|
|
1009
1009
|
}
|
|
1010
1010
|
function unavailable(message) {
|
|
1011
|
-
return new MachineSessionServiceFailure("failed_precondition", message);
|
|
1011
|
+
return new MachineSessionServiceFailure("failed_precondition", message, undefined, undefined, true);
|
|
1012
1012
|
}
|
|
1013
1013
|
function conflict(message) {
|
|
1014
1014
|
return new MachineSessionServiceFailure("conflict", message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/daemon",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.39",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -51,17 +51,18 @@
|
|
|
51
51
|
"tar": "^7.5.19",
|
|
52
52
|
"undici": "^7.28.0",
|
|
53
53
|
"ws": "^8.21.0",
|
|
54
|
-
"@rynx-ai/
|
|
55
|
-
"@rynx-ai/emulator": "0.1.11-beta.
|
|
56
|
-
"@rynx-ai/
|
|
57
|
-
"@rynx-ai/plugin-sdk": "0.1.11-beta.
|
|
58
|
-
"@rynx-ai/protocol": "0.1.11-beta.
|
|
59
|
-
"@rynx-ai/remote-runtime-client": "0.1.11-beta.
|
|
60
|
-
"@rynx-ai/server": "0.1.11-beta.
|
|
54
|
+
"@rynx-ai/core": "0.1.11-beta.39",
|
|
55
|
+
"@rynx-ai/emulator": "0.1.11-beta.39",
|
|
56
|
+
"@rynx-ai/plugin-runner": "0.1.11-beta.39",
|
|
57
|
+
"@rynx-ai/plugin-sdk": "0.1.11-beta.39",
|
|
58
|
+
"@rynx-ai/protocol": "0.1.11-beta.39",
|
|
59
|
+
"@rynx-ai/remote-runtime-client": "0.1.11-beta.39",
|
|
60
|
+
"@rynx-ai/server": "0.1.11-beta.39"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/ws": "^8.18.1",
|
|
64
|
-
"@rynx-ai/
|
|
64
|
+
"@rynx-ai/browser-cdp": "0.1.11-beta.39",
|
|
65
|
+
"@rynx-ai/plugin-channel-lark": "0.1.11-beta.39"
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
68
|
"build": "rm -rf dist bundled-plugins && tsc -p tsconfig.json && chmod +x dist/index-daemon.js && node ../../scripts/stage-bundled-plugins.mjs",
|