@rynx-ai/daemon 0.1.11-beta.2 → 0.1.11-beta.21
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/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +0 -12
- package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +3 -1
- package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +3 -3
- package/bundled-plugins/plugins/lark/dist/host-adapters.js +20 -18
- package/bundled-plugins/plugins/lark/dist/lark.js +18 -12
- package/bundled-plugins/plugins/lark/dist/runtime.js +6046 -6022
- package/bundled-plugins/plugins/lark/dist/settings.js +6006 -5988
- package/bundled-plugins/plugins/lark/package.json +1 -1
- package/bundled-plugins/plugins/lark/ui/assets/index-CGUVAMsv.css +1 -0
- package/bundled-plugins/plugins/lark/ui/index.html +2 -2
- package/dist/agent-file.js +0 -1
- package/dist/browser-artifact-management.d.ts +4 -1
- package/dist/browser-artifact-management.js +22 -11
- package/dist/browser-artifacts.d.ts +1 -0
- package/dist/browser-artifacts.js +1 -0
- package/dist/chrome-for-testing-runtime.d.ts +27 -0
- package/dist/chrome-for-testing-runtime.js +194 -0
- package/dist/chrome-for-testing-store.d.ts +2 -0
- package/dist/chrome-for-testing-store.js +37 -6
- package/dist/control-deps.d.ts +6 -2
- package/dist/control-deps.js +87 -4
- package/dist/control-link-store.js +4 -3
- package/dist/daemon-owner.js +4 -7
- package/dist/daemon-server.d.ts +4 -1
- package/dist/daemon-server.js +158 -45
- package/dist/db.d.ts +2 -2
- package/dist/db.js +55 -20
- package/dist/entry-path.d.ts +3 -0
- package/dist/entry-path.js +12 -0
- package/dist/index-daemon.js +3 -87
- package/dist/lifecycle.d.ts +13 -0
- package/dist/lifecycle.js +19 -0
- package/dist/maintenance-lease.d.ts +39 -0
- package/dist/maintenance-lease.js +250 -0
- package/dist/migrations/cleanup-legacy-sessions.js +4 -3
- package/dist/plugin-cli.d.ts +10 -0
- package/dist/plugin-cli.js +19 -2
- package/dist/plugin-host-rpc.d.ts +35 -6
- package/dist/plugin-host-rpc.js +222 -59
- package/dist/plugin-installer.js +62 -2
- package/dist/plugin-management-service.js +0 -2
- package/dist/plugin-marketplace.js +2 -1
- package/dist/plugin-store.js +2 -4
- package/dist/plugin-supervisor.d.ts +5 -0
- package/dist/plugin-supervisor.js +218 -33
- package/dist/pm2.d.ts +11 -2
- package/dist/pm2.js +116 -19
- package/dist/process.d.ts +12 -0
- package/dist/process.js +91 -0
- package/dist/remote-runtime-access-store.d.ts +2 -2
- package/dist/remote-runtime-access-store.js +6 -9
- package/dist/remote-runtime-target-store.d.ts +2 -2
- package/dist/remote-runtime-target-store.js +6 -6
- package/dist/session-emulator-binding-store.js +4 -6
- package/dist/session-fork-store.js +24 -8
- package/dist/session-launch-operations.d.ts +10 -1
- package/dist/session-launch-operations.js +21 -11
- package/dist/session-log-store.js +2 -1
- package/dist/session-meta-store.d.ts +5 -0
- package/dist/session-meta-store.js +25 -2
- package/dist/session-portal-grant-store.d.ts +29 -0
- package/dist/session-portal-grant-store.js +85 -0
- package/dist/session-resource-store.js +3 -2
- package/dist/setup-service.d.ts +21 -0
- package/dist/setup-service.js +39 -0
- package/dist/skills-catalog.d.ts +11 -2
- package/dist/skills-catalog.js +307 -10
- package/dist/sqlite.d.ts +22 -0
- package/dist/sqlite.js +57 -0
- package/dist/system-dependencies.d.ts +22 -0
- package/dist/system-dependencies.js +79 -0
- package/package.json +37 -21
- package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +0 -1
- package/dist/browser-cli-args.d.ts +0 -20
- package/dist/browser-cli-args.js +0 -100
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -550
- package/dist/control-client.d.ts +0 -122
- package/dist/control-client.js +0 -855
- package/dist/control-endpoint.d.ts +0 -14
- package/dist/control-endpoint.js +0 -124
- package/dist/desktop-browser-host-client.d.ts +0 -35
- package/dist/desktop-browser-host-client.js +0 -415
- package/dist/setup.d.ts +0 -20
- package/dist/setup.js +0 -386
- package/dist/update.d.ts +0 -25
- package/dist/update.js +0 -133
- /package/bundled-plugins/plugins/lark/ui/assets/{index-D9N-AkLX.js → index-D8Amoe6a.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { newSessionId } from "@rynx-ai/core";
|
|
2
2
|
import { db } from "./db.js";
|
|
3
|
+
import { scalar, transaction } from "./sqlite.js";
|
|
3
4
|
import { createSessionMeta } from "./session-meta-store.js";
|
|
4
5
|
const MAX_ID_LENGTH = 512;
|
|
5
6
|
const SHA256_PATTERN = /^[a-fA-F0-9]{64}$/;
|
|
@@ -48,14 +49,17 @@ export function replayPluginSessionLaunch(input) {
|
|
|
48
49
|
const existing = readOperation(pluginId, scopeId, operationId);
|
|
49
50
|
if (!existing)
|
|
50
51
|
return undefined;
|
|
51
|
-
if (existing.request_hash !== requestHash
|
|
52
|
+
if (existing.request_hash !== requestHash &&
|
|
53
|
+
existing.raw_request_hash !== requestHash) {
|
|
54
|
+
if (input.allowRequestHashMismatch)
|
|
55
|
+
return undefined;
|
|
52
56
|
throw new SessionLaunchOperationConflictError(pluginId, scopeId, operationId);
|
|
53
57
|
}
|
|
54
58
|
if (existing.state === "deleted") {
|
|
55
59
|
throw new SessionLaunchOperationTargetDeletedError(pluginId, scopeId, operationId, existing.session_id);
|
|
56
60
|
}
|
|
57
61
|
if (!sessionExists(existing.session_id)) {
|
|
58
|
-
db()
|
|
62
|
+
transaction(db(), () => {
|
|
59
63
|
tombstoneOperations(existing.session_id, new Date().toISOString());
|
|
60
64
|
}).immediate();
|
|
61
65
|
throw new SessionLaunchOperationTargetDeletedError(pluginId, scopeId, operationId, existing.session_id);
|
|
@@ -75,10 +79,16 @@ export function ensurePluginSessionLaunch(input) {
|
|
|
75
79
|
const scopeId = requiredId(input.scopeId, "scopeId");
|
|
76
80
|
const operationId = requiredId(input.operationId, "operationId");
|
|
77
81
|
const requestHash = requiredRequestHash(input.requestHash);
|
|
78
|
-
const
|
|
82
|
+
const rawRequestHash = input.rawRequestHash
|
|
83
|
+
? requiredRequestHash(input.rawRequestHash)
|
|
84
|
+
: requestHash;
|
|
85
|
+
const ensure = transaction(db(), () => {
|
|
79
86
|
const existing = readOperation(pluginId, scopeId, operationId);
|
|
80
87
|
if (existing) {
|
|
81
|
-
if (existing.request_hash !== requestHash
|
|
88
|
+
if (existing.request_hash !== requestHash &&
|
|
89
|
+
existing.raw_request_hash !== requestHash &&
|
|
90
|
+
existing.request_hash !== rawRequestHash &&
|
|
91
|
+
existing.raw_request_hash !== rawRequestHash) {
|
|
82
92
|
throw new SessionLaunchOperationConflictError(pluginId, scopeId, operationId);
|
|
83
93
|
}
|
|
84
94
|
if (existing.state === "deleted") {
|
|
@@ -110,10 +120,10 @@ export function ensurePluginSessionLaunch(input) {
|
|
|
110
120
|
}
|
|
111
121
|
db()
|
|
112
122
|
.prepare(`INSERT INTO plugin_session_launch_operations
|
|
113
|
-
(plugin_id, scope_id, operation_id, request_hash, session_id, state,
|
|
123
|
+
(plugin_id, scope_id, operation_id, request_hash, raw_request_hash, session_id, state,
|
|
114
124
|
created_at, updated_at, deleted_at)
|
|
115
|
-
VALUES (?, ?, ?, ?, ?, 'active', ?, ?, NULL)`)
|
|
116
|
-
.run(pluginId, scopeId, operationId, requestHash, sessionId, now, now);
|
|
125
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', ?, ?, NULL)`)
|
|
126
|
+
.run(pluginId, scopeId, operationId, requestHash, rawRequestHash, sessionId, now, now);
|
|
117
127
|
return { disposition: "created", sessionId };
|
|
118
128
|
});
|
|
119
129
|
const result = ensure.immediate();
|
|
@@ -124,20 +134,20 @@ export function ensurePluginSessionLaunch(input) {
|
|
|
124
134
|
}
|
|
125
135
|
function readOperation(pluginId, scopeId, operationId) {
|
|
126
136
|
return db()
|
|
127
|
-
.prepare(`SELECT request_hash, session_id, state
|
|
137
|
+
.prepare(`SELECT request_hash, raw_request_hash, session_id, state
|
|
128
138
|
FROM plugin_session_launch_operations
|
|
129
139
|
WHERE plugin_id = ? AND scope_id = ? AND operation_id = ?`)
|
|
130
140
|
.get(pluginId, scopeId, operationId);
|
|
131
141
|
}
|
|
132
142
|
function sessionExists(sessionId) {
|
|
133
|
-
return Boolean(db().prepare("SELECT 1 FROM sessions WHERE id = ?")
|
|
143
|
+
return Boolean(scalar(db().prepare("SELECT 1 FROM sessions WHERE id = ?")).get(sessionId));
|
|
134
144
|
}
|
|
135
145
|
function tombstoneOperations(sessionId, now) {
|
|
136
|
-
return db()
|
|
146
|
+
return Number(db()
|
|
137
147
|
.prepare(`UPDATE plugin_session_launch_operations
|
|
138
148
|
SET state = 'deleted', updated_at = ?, deleted_at = ?
|
|
139
149
|
WHERE session_id = ? AND state = 'active'`)
|
|
140
|
-
.run(now, now, sessionId).changes;
|
|
150
|
+
.run(now, now, sessionId).changes);
|
|
141
151
|
}
|
|
142
152
|
function requiredId(value, label) {
|
|
143
153
|
const normalized = value?.trim();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { db } from "./db.js";
|
|
2
|
+
import { transaction } from "./sqlite.js";
|
|
2
3
|
function toItem(row) {
|
|
3
4
|
return {
|
|
4
5
|
id: row.id,
|
|
@@ -21,7 +22,7 @@ export class SqliteSessionLogStore {
|
|
|
21
22
|
(id, session_id, position, response_id, type, status, data, created_by, created_at)
|
|
22
23
|
VALUES (@id, @session_id, @position, @response_id, @type, @status, @data, @created_by, @created_at)`);
|
|
23
24
|
const maxStmt = conn.prepare("SELECT MAX(position) AS max FROM session_items WHERE session_id = ?");
|
|
24
|
-
const tx =
|
|
25
|
+
const tx = transaction(conn, (batch) => {
|
|
25
26
|
const { max } = maxStmt.get(sessionId);
|
|
26
27
|
let next = (max ?? -1) + 1;
|
|
27
28
|
return batch.map((item) => {
|
|
@@ -18,6 +18,11 @@ export declare function listSessionMetas(): StoredSessionMeta[];
|
|
|
18
18
|
export declare function getSessionMeta(id: string): StoredSessionMeta | undefined;
|
|
19
19
|
export declare function createSessionMeta(meta: StoredSessionMeta): void;
|
|
20
20
|
export declare function setSessionTitle(id: string, title: string): void;
|
|
21
|
+
export declare function setSessionExecution(id: string, execution: MachineSessionRecord["execution"]): void;
|
|
21
22
|
export declare function removeSessionMeta(id: string): void;
|
|
22
23
|
/** {@link SessionRegistry} over the same `sessions` table. */
|
|
23
24
|
export declare const sessionRegistry: SessionRegistry;
|
|
25
|
+
/** Generic plugin-principal ownership, including descendants forked from a
|
|
26
|
+
* plugin-created Session. Portal authorization calls this on every request so
|
|
27
|
+
* a grant cannot cross into console or another plugin's Sessions. */
|
|
28
|
+
export declare function sessionBelongsToPlugin(pluginId: string, sessionId: string): boolean;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { normalizeSessionTitle, resolvedExecutionSnapshotSchema, sessionWorkspaceSnapshotSchema, } from "@rynx-ai/core";
|
|
15
15
|
import { db } from "./db.js";
|
|
16
|
+
import { transaction } from "./sqlite.js";
|
|
16
17
|
const COLS = "id, workspace, execution, title, forked_from_session_id, source, created_at, updated_at";
|
|
17
18
|
export function listSessionMetas() {
|
|
18
19
|
return db()
|
|
@@ -46,10 +47,15 @@ export function createSessionMeta(meta) {
|
|
|
46
47
|
export function setSessionTitle(id, title) {
|
|
47
48
|
db()
|
|
48
49
|
.prepare("UPDATE sessions SET title = ?, updated_at = ? WHERE id = ?")
|
|
49
|
-
.run(normalizeSessionTitle(title), new Date().toISOString(), id);
|
|
50
|
+
.run(normalizeSessionTitle(title) ?? null, new Date().toISOString(), id);
|
|
51
|
+
}
|
|
52
|
+
export function setSessionExecution(id, execution) {
|
|
53
|
+
db()
|
|
54
|
+
.prepare("UPDATE sessions SET execution = ?, updated_at = ? WHERE id = ?")
|
|
55
|
+
.run(JSON.stringify(resolvedExecutionSnapshotSchema.parse(execution)), new Date().toISOString(), id);
|
|
50
56
|
}
|
|
51
57
|
export function removeSessionMeta(id) {
|
|
52
|
-
db()
|
|
58
|
+
transaction(db(), () => {
|
|
53
59
|
const now = new Date().toISOString();
|
|
54
60
|
db()
|
|
55
61
|
.prepare(`UPDATE plugin_session_launch_operations
|
|
@@ -70,8 +76,25 @@ export const sessionRegistry = {
|
|
|
70
76
|
get: getSessionMeta,
|
|
71
77
|
list: listSessionMetas,
|
|
72
78
|
setTitle: setSessionTitle,
|
|
79
|
+
setExecution: setSessionExecution,
|
|
73
80
|
remove: removeSessionMeta,
|
|
74
81
|
};
|
|
82
|
+
/** Generic plugin-principal ownership, including descendants forked from a
|
|
83
|
+
* plugin-created Session. Portal authorization calls this on every request so
|
|
84
|
+
* a grant cannot cross into console or another plugin's Sessions. */
|
|
85
|
+
export function sessionBelongsToPlugin(pluginId, sessionId) {
|
|
86
|
+
const seen = new Set();
|
|
87
|
+
let current = sessionRegistry.get(sessionId);
|
|
88
|
+
while (current && !seen.has(current.id)) {
|
|
89
|
+
if (current.source === `plugin:${pluginId}`)
|
|
90
|
+
return true;
|
|
91
|
+
seen.add(current.id);
|
|
92
|
+
current = current.forkedFromSessionId
|
|
93
|
+
? sessionRegistry.get(current.forkedFromSessionId)
|
|
94
|
+
: undefined;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
75
98
|
function rowToMeta(row) {
|
|
76
99
|
return {
|
|
77
100
|
id: row.id,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type SessionPortalScope = "plugin_sessions" | "single_session";
|
|
2
|
+
export type SessionPortalAccess = "read_only" | "read_write";
|
|
3
|
+
export interface SessionPortalAuthorization {
|
|
4
|
+
portalId: string;
|
|
5
|
+
pluginId: string;
|
|
6
|
+
scope: SessionPortalScope;
|
|
7
|
+
access: SessionPortalAccess;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
expiresAt: string;
|
|
10
|
+
}
|
|
11
|
+
/** Issue a browser handoff into the native Session workbench. The Host knows
|
|
12
|
+
* only the generic Session scope and access level; product/user policy stays in
|
|
13
|
+
* the plugin that asks for the ticket. */
|
|
14
|
+
export declare function issueSessionPortalTicket(input: {
|
|
15
|
+
pluginId: string;
|
|
16
|
+
scope: SessionPortalScope;
|
|
17
|
+
access: SessionPortalAccess;
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
now?: Date;
|
|
20
|
+
}): {
|
|
21
|
+
path: string;
|
|
22
|
+
expiresAt: string;
|
|
23
|
+
};
|
|
24
|
+
/** Atomically consume a one-use ticket and mint a fixed-lifetime browser grant. */
|
|
25
|
+
export declare function redeemSessionPortalTicket(ticket: string, now?: Date): (SessionPortalAuthorization & {
|
|
26
|
+
secret: string;
|
|
27
|
+
}) | undefined;
|
|
28
|
+
export declare function authorizeSessionPortalGrant(portalId: string, secret: string, now?: Date): SessionPortalAuthorization | undefined;
|
|
29
|
+
export declare function pruneSessionPortalAuthorization(now?: Date): void;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { db } from "./db.js";
|
|
3
|
+
import { transaction } from "./sqlite.js";
|
|
4
|
+
const TICKET_TTL_MS = 5 * 60 * 1_000;
|
|
5
|
+
const GRANT_TTL_MS = 30 * 60 * 1_000;
|
|
6
|
+
/** Issue a browser handoff into the native Session workbench. The Host knows
|
|
7
|
+
* only the generic Session scope and access level; product/user policy stays in
|
|
8
|
+
* the plugin that asks for the ticket. */
|
|
9
|
+
export function issueSessionPortalTicket(input) {
|
|
10
|
+
if ((input.scope === "single_session") !== Boolean(input.sessionId)) {
|
|
11
|
+
throw new Error("single_session Portal tickets require exactly one Session id");
|
|
12
|
+
}
|
|
13
|
+
const now = input.now ?? new Date();
|
|
14
|
+
const expiresAt = new Date(now.getTime() + TICKET_TTL_MS).toISOString();
|
|
15
|
+
const ticket = randomToken();
|
|
16
|
+
pruneSessionPortalAuthorization(now);
|
|
17
|
+
db().prepare(`INSERT INTO session_portal_tickets
|
|
18
|
+
(ticket_hash, plugin_id, scope, access, session_id, created_at, expires_at, used_at)
|
|
19
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, NULL)`).run(hash(ticket), input.pluginId, input.scope, input.access, input.sessionId ?? null, now.toISOString(), expiresAt);
|
|
20
|
+
return {
|
|
21
|
+
path: `/session-portal/redeem#ticket=${encodeURIComponent(ticket)}`,
|
|
22
|
+
expiresAt,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Atomically consume a one-use ticket and mint a fixed-lifetime browser grant. */
|
|
26
|
+
export function redeemSessionPortalTicket(ticket, now = new Date()) {
|
|
27
|
+
const connection = db();
|
|
28
|
+
return transaction(connection, () => {
|
|
29
|
+
const ticketHash = hash(ticket);
|
|
30
|
+
const row = connection.prepare(`SELECT plugin_id, scope, access, session_id, expires_at, used_at
|
|
31
|
+
FROM session_portal_tickets
|
|
32
|
+
WHERE ticket_hash = ?`).get(ticketHash);
|
|
33
|
+
if (!row || row.used_at !== null || row.expires_at <= now.toISOString())
|
|
34
|
+
return undefined;
|
|
35
|
+
const consumed = connection.prepare(`UPDATE session_portal_tickets SET used_at = ?
|
|
36
|
+
WHERE ticket_hash = ? AND used_at IS NULL AND expires_at > ?`).run(now.toISOString(), ticketHash, now.toISOString());
|
|
37
|
+
if (consumed.changes !== 1)
|
|
38
|
+
return undefined;
|
|
39
|
+
const portalId = randomUUID();
|
|
40
|
+
const secret = randomToken();
|
|
41
|
+
const expiresAt = new Date(now.getTime() + GRANT_TTL_MS).toISOString();
|
|
42
|
+
connection.prepare(`INSERT INTO session_portal_grants
|
|
43
|
+
(portal_id, secret_hash, plugin_id, scope, access, session_id, created_at, expires_at, revoked_at)
|
|
44
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(portalId, hash(secret), row.plugin_id, row.scope, row.access, row.session_id, now.toISOString(), expiresAt);
|
|
45
|
+
return {
|
|
46
|
+
portalId,
|
|
47
|
+
secret,
|
|
48
|
+
pluginId: row.plugin_id,
|
|
49
|
+
scope: row.scope,
|
|
50
|
+
access: row.access,
|
|
51
|
+
...(row.session_id ? { sessionId: row.session_id } : {}),
|
|
52
|
+
expiresAt,
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
}
|
|
56
|
+
export function authorizeSessionPortalGrant(portalId, secret, now = new Date()) {
|
|
57
|
+
const row = db().prepare(`SELECT portal_id, secret_hash, plugin_id, scope, access, session_id, expires_at, revoked_at
|
|
58
|
+
FROM session_portal_grants
|
|
59
|
+
WHERE portal_id = ?`).get(portalId);
|
|
60
|
+
if (!row || row.revoked_at !== null || row.expires_at <= now.toISOString())
|
|
61
|
+
return undefined;
|
|
62
|
+
const actual = Buffer.from(hash(secret), "hex");
|
|
63
|
+
const expected = Buffer.from(row.secret_hash, "hex");
|
|
64
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected))
|
|
65
|
+
return undefined;
|
|
66
|
+
return {
|
|
67
|
+
portalId: row.portal_id,
|
|
68
|
+
pluginId: row.plugin_id,
|
|
69
|
+
scope: row.scope,
|
|
70
|
+
access: row.access,
|
|
71
|
+
...(row.session_id ? { sessionId: row.session_id } : {}),
|
|
72
|
+
expiresAt: row.expires_at,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function pruneSessionPortalAuthorization(now = new Date()) {
|
|
76
|
+
const connection = db();
|
|
77
|
+
connection.prepare("DELETE FROM session_portal_tickets WHERE expires_at <= ? OR used_at IS NOT NULL").run(now.toISOString());
|
|
78
|
+
connection.prepare("DELETE FROM session_portal_grants WHERE expires_at <= ? OR revoked_at IS NOT NULL").run(now.toISOString());
|
|
79
|
+
}
|
|
80
|
+
function randomToken() {
|
|
81
|
+
return randomBytes(32).toString("base64url");
|
|
82
|
+
}
|
|
83
|
+
function hash(value) {
|
|
84
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
85
|
+
}
|
|
@@ -5,6 +5,7 @@ import { rynxHome, } from "@rynx-ai/core";
|
|
|
5
5
|
import { defaultSessionResourcePolicy, SESSION_RESOURCE_MAX_IMAGE_PIXELS, SESSION_RESOURCE_MAX_IMAGE_SIDE, SESSION_RESOURCE_MAX_MESSAGE_BYTES, SESSION_RESOURCE_TRANSFER_CHUNK_BYTES, } from "@rynx-ai/protocol/remote-runtime-rpc";
|
|
6
6
|
import { MachineSessionServiceFailure, } from "@rynx-ai/server";
|
|
7
7
|
import { db } from "./db.js";
|
|
8
|
+
import { transaction } from "./sqlite.js";
|
|
8
9
|
const MAX_STAGED_RESOURCES_PER_SESSION = 16;
|
|
9
10
|
const STAGED_TTL_MS = 24 * 60 * 60 * 1_000;
|
|
10
11
|
export class SqliteSessionResourceStore {
|
|
@@ -244,7 +245,7 @@ export class SqliteSessionResourceStore {
|
|
|
244
245
|
rmSync(this.partialPath(row), { force: true });
|
|
245
246
|
rmSync(this.finalPath(row), { force: true });
|
|
246
247
|
}
|
|
247
|
-
db()
|
|
248
|
+
transaction(db(), () => {
|
|
248
249
|
db().prepare("DELETE FROM session_message_operations WHERE session_id = ?").run(sessionId);
|
|
249
250
|
db().prepare("DELETE FROM session_resources WHERE session_id = ?").run(sessionId);
|
|
250
251
|
})();
|
|
@@ -252,7 +253,7 @@ export class SqliteSessionResourceStore {
|
|
|
252
253
|
}
|
|
253
254
|
prepareMessageOperation(sessionId, clientMessageId, requestHash, resourceIds) {
|
|
254
255
|
const conn = db();
|
|
255
|
-
return
|
|
256
|
+
return transaction(conn, () => {
|
|
256
257
|
const existing = conn.prepare(`SELECT request_hash, state
|
|
257
258
|
FROM session_message_operations
|
|
258
259
|
WHERE session_id = ? AND client_message_id = ?`).get(sessionId, clientMessageId);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { inspectSystemDependencies, type SystemDependencySnapshot, } from "./system-dependencies.js";
|
|
2
|
+
export interface DaemonDiagnosticSnapshot {
|
|
3
|
+
databasePath: string;
|
|
4
|
+
databaseExists: boolean;
|
|
5
|
+
pluginCount: number;
|
|
6
|
+
agentCount: number;
|
|
7
|
+
browser: {
|
|
8
|
+
supported: false;
|
|
9
|
+
} | {
|
|
10
|
+
supported: true;
|
|
11
|
+
installedVersion?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function prepareBundledPlugins(): Promise<{
|
|
16
|
+
status: "ready" | "unavailable";
|
|
17
|
+
installed: string[];
|
|
18
|
+
updated: string[];
|
|
19
|
+
unchanged: string[];
|
|
20
|
+
}>;
|
|
21
|
+
export declare function inspectDaemonDiagnostics(): Promise<DaemonDiagnosticSnapshot>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { listAgentSpecs } from "@rynx-ai/core";
|
|
3
|
+
import { createChromeForTestingArtifactStore, isChromeForTestingHostSupported } from "./chrome-for-testing-store.js";
|
|
4
|
+
import { ensureBundledOfficialPlugins } from "./bundled-plugins.js";
|
|
5
|
+
import { dbPath } from "./db.js";
|
|
6
|
+
import { listPlugins } from "./plugin-store.js";
|
|
7
|
+
export { inspectSystemDependencies, } from "./system-dependencies.js";
|
|
8
|
+
export async function prepareBundledPlugins() {
|
|
9
|
+
return ensureBundledOfficialPlugins();
|
|
10
|
+
}
|
|
11
|
+
export async function inspectDaemonDiagnostics() {
|
|
12
|
+
const databasePath = dbPath();
|
|
13
|
+
let browser;
|
|
14
|
+
if (!isChromeForTestingHostSupported()) {
|
|
15
|
+
browser = { supported: false };
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
try {
|
|
19
|
+
const installed = createChromeForTestingArtifactStore().readActive();
|
|
20
|
+
browser = {
|
|
21
|
+
supported: true,
|
|
22
|
+
...(installed ? { installedVersion: installed.version } : {}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
browser = {
|
|
27
|
+
supported: true,
|
|
28
|
+
error: error instanceof Error ? error.message : String(error),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
databasePath,
|
|
34
|
+
databaseExists: existsSync(databasePath),
|
|
35
|
+
pluginCount: existsSync(databasePath) ? listPlugins().length : 0,
|
|
36
|
+
agentCount: (await listAgentSpecs()).length,
|
|
37
|
+
browser,
|
|
38
|
+
};
|
|
39
|
+
}
|
package/dist/skills-catalog.d.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { type SkillInstallRecipe, type SkillMeta } from "@rynx-ai/core";
|
|
2
|
-
import type { SkillDetail } from "@rynx-ai/protocol/control";
|
|
1
|
+
import { type LocalSkillRuntime, type SkillInstallRecipe, type SkillMeta } from "@rynx-ai/core";
|
|
2
|
+
import type { ControlLocalSkillCandidate, ControlLocalSkillImportResult, SkillDetail } from "@rynx-ai/protocol/control";
|
|
3
3
|
export declare function listCatalogSkills(): Promise<SkillMeta[]>;
|
|
4
4
|
/** Install a skill (or a repo's skills) into the catalog per a full install
|
|
5
5
|
* recipe; delegates to the core catalog layer (which also writes each skill's
|
|
6
6
|
* `.rynx-skill.json` install-info file — a catalog-only artifact). Returns
|
|
7
7
|
* the refreshed catalog. */
|
|
8
8
|
export declare function installCatalogSkill(recipe: SkillInstallRecipe): Promise<SkillMeta[]>;
|
|
9
|
+
export declare function scanLocalCatalogSkillCandidates(runtime: LocalSkillRuntime): Promise<ControlLocalSkillCandidate[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Register complete local runtime Skill bundles into the owner catalog. Every
|
|
12
|
+
* content hash gets a durable immutable artifact; the mutable catalog entry's
|
|
13
|
+
* receipt points at that artifact so old Agent specs keep their exact bytes
|
|
14
|
+
* after a later catalog update.
|
|
15
|
+
*/
|
|
16
|
+
export declare function importLocalCatalogSkills(runtime: LocalSkillRuntime, keys: string[]): Promise<ControlLocalSkillImportResult[]>;
|
|
17
|
+
export declare function importCatalogSkillsFromArchive(filename: string, contentBase64: string): Promise<ControlLocalSkillImportResult[]>;
|
|
9
18
|
/** Detail for one catalog skill: its metadata + the file tree under its dir. */
|
|
10
19
|
export declare function getCatalogSkill(name: string): Promise<SkillDetail | null>;
|
|
11
20
|
/** Read one text file inside a catalog skill. Path-confined to the skill dir,
|