@rynx-ai/daemon 0.1.10 → 0.1.11-beta.10
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/.rynx-plugin/marketplace.json +10 -0
- package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +25 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.d.ts +48 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +56 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.d.ts +85 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.js +333 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.d.ts +109 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.js +347 -0
- package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +95 -0
- package/bundled-plugins/plugins/lark/dist/host-adapters.js +506 -0
- package/bundled-plugins/plugins/lark/dist/index.d.ts +7 -0
- package/bundled-plugins/plugins/lark/dist/index.js +7 -0
- package/bundled-plugins/plugins/lark/dist/lark-card-stream.d.ts +363 -0
- package/bundled-plugins/plugins/lark/dist/lark-card-stream.js +1335 -0
- package/bundled-plugins/plugins/lark/dist/lark-config-store.d.ts +30 -0
- package/bundled-plugins/plugins/lark/dist/lark-config-store.js +135 -0
- package/bundled-plugins/plugins/lark/dist/lark-content.d.ts +30 -0
- package/bundled-plugins/plugins/lark/dist/lark-content.js +138 -0
- package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.d.ts +65 -0
- package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.js +106 -0
- package/bundled-plugins/plugins/lark/dist/lark-diff-card.d.ts +25 -0
- package/bundled-plugins/plugins/lark/dist/lark-diff-card.js +58 -0
- package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.d.ts +16 -0
- package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.js +141 -0
- package/bundled-plugins/plugins/lark/dist/lark-resume-card.d.ts +31 -0
- package/bundled-plugins/plugins/lark/dist/lark-resume-card.js +105 -0
- package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.d.ts +18 -0
- package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.js +95 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/client.d.ts +8 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/client.js +32 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/index.d.ts +1 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/index.js +1 -0
- package/bundled-plugins/plugins/lark/dist/lark-sender.d.ts +114 -0
- package/bundled-plugins/plugins/lark/dist/lark-sender.js +323 -0
- package/bundled-plugins/plugins/lark/dist/lark-session-store.d.ts +13 -0
- package/bundled-plugins/plugins/lark/dist/lark-session-store.js +14 -0
- package/bundled-plugins/plugins/lark/dist/lark-settings-card.d.ts +55 -0
- package/bundled-plugins/plugins/lark/dist/lark-settings-card.js +158 -0
- package/bundled-plugins/plugins/lark/dist/lark-setup.d.ts +39 -0
- package/bundled-plugins/plugins/lark/dist/lark-setup.js +103 -0
- package/bundled-plugins/plugins/lark/dist/lark.d.ts +507 -0
- package/bundled-plugins/plugins/lark/dist/lark.js +1975 -0
- package/bundled-plugins/plugins/lark/dist/runtime.d.ts +13 -0
- package/bundled-plugins/plugins/lark/dist/runtime.js +131328 -0
- package/bundled-plugins/plugins/lark/dist/settings.d.ts +7 -0
- package/bundled-plugins/plugins/lark/dist/settings.js +126971 -0
- package/bundled-plugins/plugins/lark/package.json +57 -0
- package/bundled-plugins/plugins/lark/ui/assets/index-CGUVAMsv.css +1 -0
- package/bundled-plugins/plugins/lark/ui/assets/index-D8Amoe6a.js +201 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/index.html +13 -0
- package/dist/agent-file.js +0 -1
- package/dist/browser-artifact-management.d.ts +4 -1
- package/dist/browser-artifact-management.js +18 -10
- package/dist/browser-artifacts.d.ts +1 -0
- package/dist/browser-artifacts.js +1 -0
- package/dist/bundled-plugins.d.ts +15 -0
- package/dist/bundled-plugins.js +134 -0
- package/dist/chrome-for-testing-runtime.d.ts +27 -0
- package/dist/chrome-for-testing-runtime.js +193 -0
- package/dist/chrome-for-testing-store.d.ts +2 -0
- package/dist/chrome-for-testing-store.js +9 -0
- package/dist/control-deps.d.ts +6 -2
- package/dist/control-deps.js +88 -82
- package/dist/daemon-server.d.ts +7 -1
- package/dist/daemon-server.js +310 -87
- package/dist/db.js +181 -124
- package/dist/entry-path.d.ts +3 -0
- package/dist/entry-path.js +12 -0
- package/dist/index-daemon.js +3 -66
- package/dist/lifecycle.d.ts +12 -0
- package/dist/lifecycle.js +16 -0
- package/dist/maintenance-lease.d.ts +39 -0
- package/dist/maintenance-lease.js +250 -0
- package/dist/migrations/cleanup-legacy-sessions.d.ts +1 -1
- package/dist/migrations/cleanup-legacy-sessions.js +33 -15
- package/dist/official-plugins.d.ts +2 -0
- package/dist/official-plugins.js +2 -0
- package/dist/plugin-cli.d.ts +1 -3
- package/dist/plugin-cli.js +56 -45
- package/dist/plugin-host-leases.d.ts +4 -0
- package/dist/plugin-host-leases.js +7 -0
- package/dist/plugin-host-rpc.d.ts +73 -6
- package/dist/plugin-host-rpc.js +354 -81
- package/dist/plugin-installer.d.ts +24 -55
- package/dist/plugin-installer.js +258 -412
- package/dist/plugin-management-service.d.ts +30 -23
- package/dist/plugin-management-service.js +288 -105
- package/dist/plugin-marketplace.d.ts +35 -0
- package/dist/plugin-marketplace.js +299 -0
- package/dist/plugin-package.d.ts +22 -24
- package/dist/plugin-package.js +520 -571
- package/dist/plugin-skill-context.d.ts +4 -0
- package/dist/plugin-skill-context.js +35 -0
- package/dist/plugin-store.d.ts +36 -37
- package/dist/plugin-store.js +138 -92
- package/dist/plugin-supervisor.d.ts +50 -125
- package/dist/plugin-supervisor.js +678 -1030
- package/dist/pm2.d.ts +11 -2
- package/dist/pm2.js +130 -28
- package/dist/process.d.ts +12 -0
- package/dist/process.js +91 -0
- package/dist/runtime-project-store.d.ts +9 -0
- package/dist/runtime-project-store.js +83 -0
- package/dist/session-fallback-workspace.d.ts +22 -0
- package/dist/session-fallback-workspace.js +120 -0
- package/dist/session-fork-store.d.ts +41 -0
- package/dist/session-fork-store.js +288 -0
- package/dist/session-launch-operations.d.ts +23 -2
- package/dist/session-launch-operations.js +48 -7
- package/dist/session-meta-store.d.ts +12 -9
- package/dist/session-meta-store.js +50 -49
- package/dist/session-portal-grant-store.d.ts +29 -0
- package/dist/session-portal-grant-store.js +84 -0
- 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 -7
- package/dist/system-dependencies.d.ts +26 -0
- package/dist/system-dependencies.js +149 -0
- package/package.json +36 -20
- package/dist/browser-cli-args.d.ts +0 -20
- package/dist/browser-cli-args.js +0 -100
- package/dist/channel-store.d.ts +0 -22
- package/dist/channel-store.js +0 -65
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -732
- 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/instance-store.d.ts +0 -38
- package/dist/instance-store.js +0 -85
- package/dist/plugin-install-restart.d.ts +0 -13
- package/dist/plugin-install-restart.js +0 -24
- package/dist/plugin-runtime-control.d.ts +0 -2
- package/dist/plugin-runtime-control.js +0 -37
- package/dist/registry.d.ts +0 -10
- package/dist/registry.js +0 -52
- package/dist/setup.d.ts +0 -20
- package/dist/setup.js +0 -374
- package/dist/update.d.ts +0 -25
- package/dist/update.js +0 -133
package/dist/db.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `~/.rynx/rynx.db` — the daemon's SQLite store. Concerns kept apart:
|
|
3
|
-
* - `
|
|
4
|
-
* - `
|
|
5
|
-
* enforced by a UNIQUE channel_id). This is what the daemon mounts.
|
|
6
|
-
* - `plugins` — static manifests, grants and immutable install locations.
|
|
3
|
+
* - `plugin_installations` — source-qualified manifests and immutable packages.
|
|
4
|
+
* - `plugin_marketplaces` — catalog registrations; catalog data is never executable.
|
|
7
5
|
* - `plugin_session_launch_operations` — durable idempotency/tombstones.
|
|
8
6
|
* - `control_links` — generic, one-use links to the local session console.
|
|
7
|
+
* - `session_portal_tickets` / `session_portal_grants` — hashed, scoped,
|
|
8
|
+
* browser Session Portal authorization.
|
|
9
9
|
* - `runtime_pairing_claims` / `runtime_client_grants` — one-use enrollment
|
|
10
10
|
* claims and the durable client identities they issue for direct runtimes.
|
|
11
11
|
* - `runtime_targets` / `runtime_direct_routes` / `runtime_pending_enrollments`
|
|
@@ -16,15 +16,18 @@
|
|
|
16
16
|
* Connections are cached per resolved path so tests can point `RYNX_DB` /
|
|
17
17
|
* `RYNX_HOME` at a temp dir. Override with `RYNX_DB` (full path) or `RYNX_HOME`.
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { dirname, join } from "node:path";
|
|
19
|
+
import { chmodSync, mkdirSync } from "node:fs";
|
|
20
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
21
|
+
import { rynxHome } from "@rynx-ai/core";
|
|
23
22
|
import Database from "better-sqlite3";
|
|
24
23
|
const connections = new Map();
|
|
25
24
|
/** Absolute path to the SQLite file (override via `RYNX_DB`, else `~/.rynx/rynx.db`). */
|
|
26
25
|
export function dbPath() {
|
|
27
|
-
|
|
26
|
+
const configured = process.env.RYNX_DB?.trim();
|
|
27
|
+
if (configured && !isAbsolute(configured)) {
|
|
28
|
+
throw new Error("RYNX_DB must be an absolute path");
|
|
29
|
+
}
|
|
30
|
+
return configured ? resolve(configured) : join(rynxHome(), "rynx.db");
|
|
28
31
|
}
|
|
29
32
|
/** Open (or reuse) the database at the current path, migrating + importing on first open. */
|
|
30
33
|
export function db() {
|
|
@@ -37,7 +40,7 @@ export function db() {
|
|
|
37
40
|
conn.pragma("busy_timeout = 5000");
|
|
38
41
|
conn.pragma("foreign_keys = ON");
|
|
39
42
|
migrate(conn);
|
|
40
|
-
//
|
|
43
|
+
// Plugin state and daemon credentials are private.
|
|
41
44
|
try {
|
|
42
45
|
chmodSync(path, 0o600);
|
|
43
46
|
}
|
|
@@ -49,34 +52,45 @@ export function db() {
|
|
|
49
52
|
}
|
|
50
53
|
function migrate(conn) {
|
|
51
54
|
conn.exec(`
|
|
52
|
-
CREATE TABLE IF NOT EXISTS
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
integrity TEXT,
|
|
63
|
-
grants TEXT
|
|
55
|
+
CREATE TABLE IF NOT EXISTS plugin_marketplaces (
|
|
56
|
+
marketplace_id TEXT PRIMARY KEY,
|
|
57
|
+
source_type TEXT NOT NULL CHECK(source_type IN ('git', 'local')),
|
|
58
|
+
requested_source TEXT NOT NULL,
|
|
59
|
+
resolved_revision TEXT NOT NULL,
|
|
60
|
+
source_fingerprint TEXT NOT NULL,
|
|
61
|
+
built_in INTEGER NOT NULL DEFAULT 0,
|
|
62
|
+
catalog TEXT NOT NULL,
|
|
63
|
+
created_at TEXT NOT NULL,
|
|
64
|
+
refreshed_at TEXT NOT NULL
|
|
64
65
|
);
|
|
65
|
-
CREATE TABLE IF NOT EXISTS
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
options TEXT,
|
|
70
|
-
created_at TEXT NOT NULL,
|
|
71
|
-
updated_at TEXT NOT NULL
|
|
66
|
+
CREATE TABLE IF NOT EXISTS plugin_marketplace_bindings (
|
|
67
|
+
marketplace_id TEXT PRIMARY KEY,
|
|
68
|
+
source_fingerprint TEXT NOT NULL,
|
|
69
|
+
created_at TEXT NOT NULL
|
|
72
70
|
);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
INSERT OR IGNORE INTO plugin_marketplace_bindings (
|
|
72
|
+
marketplace_id, source_fingerprint, created_at
|
|
73
|
+
)
|
|
74
|
+
SELECT marketplace_id, source_fingerprint, created_at
|
|
75
|
+
FROM plugin_marketplaces;
|
|
76
|
+
CREATE TABLE IF NOT EXISTS plugin_installations (
|
|
77
|
+
canonical_id TEXT PRIMARY KEY,
|
|
78
|
+
marketplace_id TEXT NOT NULL,
|
|
79
|
+
plugin_id TEXT NOT NULL,
|
|
80
|
+
source_type TEXT NOT NULL CHECK(source_type IN ('git', 'local')),
|
|
81
|
+
requested_source TEXT NOT NULL,
|
|
82
|
+
resolved_revision TEXT NOT NULL,
|
|
83
|
+
source_fingerprint TEXT NOT NULL,
|
|
84
|
+
version TEXT NOT NULL,
|
|
85
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
86
|
+
installed_at TEXT NOT NULL,
|
|
87
|
+
updated_at TEXT NOT NULL,
|
|
88
|
+
install_path TEXT NOT NULL,
|
|
89
|
+
manifest TEXT NOT NULL,
|
|
90
|
+
integrity TEXT NOT NULL,
|
|
91
|
+
runtime_state TEXT,
|
|
92
|
+
runtime_error TEXT,
|
|
93
|
+
UNIQUE(marketplace_id, plugin_id)
|
|
80
94
|
);
|
|
81
95
|
CREATE TABLE IF NOT EXISTS session_items (
|
|
82
96
|
id TEXT PRIMARY KEY,
|
|
@@ -142,16 +156,25 @@ function migrate(conn) {
|
|
|
142
156
|
ON DELETE CASCADE
|
|
143
157
|
);
|
|
144
158
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
159
|
+
id TEXT PRIMARY KEY,
|
|
160
|
+
workspace TEXT NOT NULL,
|
|
161
|
+
execution TEXT NOT NULL,
|
|
162
|
+
title TEXT,
|
|
163
|
+
forked_from_session_id TEXT,
|
|
164
|
+
source TEXT NOT NULL,
|
|
165
|
+
created_at TEXT NOT NULL,
|
|
166
|
+
updated_at TEXT
|
|
167
|
+
);
|
|
168
|
+
CREATE TABLE IF NOT EXISTS session_snapshot_cleanup (
|
|
169
|
+
session_id TEXT PRIMARY KEY
|
|
170
|
+
);
|
|
171
|
+
CREATE TABLE IF NOT EXISTS runtime_projects (
|
|
145
172
|
id TEXT PRIMARY KEY,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
model TEXT,
|
|
150
|
-
reasoning_effort TEXT,
|
|
151
|
-
title TEXT,
|
|
152
|
-
source TEXT,
|
|
173
|
+
name TEXT NOT NULL,
|
|
174
|
+
directories_json TEXT NOT NULL,
|
|
175
|
+
primary_dir TEXT NOT NULL,
|
|
153
176
|
created_at TEXT NOT NULL,
|
|
154
|
-
updated_at TEXT
|
|
177
|
+
updated_at TEXT NOT NULL
|
|
155
178
|
);
|
|
156
179
|
CREATE TABLE IF NOT EXISTS session_pending_messages (
|
|
157
180
|
session_id TEXT PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE,
|
|
@@ -173,6 +196,7 @@ function migrate(conn) {
|
|
|
173
196
|
scope_id TEXT NOT NULL,
|
|
174
197
|
operation_id TEXT NOT NULL,
|
|
175
198
|
request_hash TEXT NOT NULL,
|
|
199
|
+
raw_request_hash TEXT,
|
|
176
200
|
session_id TEXT NOT NULL,
|
|
177
201
|
state TEXT NOT NULL DEFAULT 'active' CHECK(state IN ('active', 'deleted')),
|
|
178
202
|
created_at TEXT NOT NULL,
|
|
@@ -187,6 +211,31 @@ function migrate(conn) {
|
|
|
187
211
|
);
|
|
188
212
|
CREATE INDEX IF NOT EXISTS idx_plugin_session_launch_operations_session
|
|
189
213
|
ON plugin_session_launch_operations(session_id);
|
|
214
|
+
CREATE TABLE IF NOT EXISTS session_fork_operations (
|
|
215
|
+
operation_id TEXT PRIMARY KEY,
|
|
216
|
+
request_hash TEXT NOT NULL,
|
|
217
|
+
source_session_id TEXT NOT NULL,
|
|
218
|
+
target_session_id TEXT NOT NULL UNIQUE,
|
|
219
|
+
title TEXT,
|
|
220
|
+
fork_position INTEGER,
|
|
221
|
+
state TEXT NOT NULL DEFAULT 'reserved'
|
|
222
|
+
CHECK(state IN ('reserved', 'completed', 'target_deleted')),
|
|
223
|
+
error TEXT,
|
|
224
|
+
created_at TEXT NOT NULL,
|
|
225
|
+
updated_at TEXT NOT NULL,
|
|
226
|
+
completed_at TEXT,
|
|
227
|
+
deleted_at TEXT,
|
|
228
|
+
CHECK(
|
|
229
|
+
(state = 'reserved' AND completed_at IS NULL AND deleted_at IS NULL)
|
|
230
|
+
OR (state = 'completed' AND completed_at IS NOT NULL AND deleted_at IS NULL)
|
|
231
|
+
OR (state = 'target_deleted' AND deleted_at IS NOT NULL)
|
|
232
|
+
)
|
|
233
|
+
);
|
|
234
|
+
CREATE INDEX IF NOT EXISTS idx_session_fork_operations_source
|
|
235
|
+
ON session_fork_operations(source_session_id, state);
|
|
236
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_fork_operations_reserved_source
|
|
237
|
+
ON session_fork_operations(source_session_id)
|
|
238
|
+
WHERE state = 'reserved';
|
|
190
239
|
CREATE TABLE IF NOT EXISTS control_links (
|
|
191
240
|
token TEXT PRIMARY KEY,
|
|
192
241
|
plugin_id TEXT NOT NULL,
|
|
@@ -197,6 +246,39 @@ function migrate(conn) {
|
|
|
197
246
|
);
|
|
198
247
|
CREATE INDEX IF NOT EXISTS idx_control_links_expires
|
|
199
248
|
ON control_links(expires_at);
|
|
249
|
+
CREATE TABLE IF NOT EXISTS session_portal_tickets (
|
|
250
|
+
ticket_hash TEXT PRIMARY KEY,
|
|
251
|
+
plugin_id TEXT NOT NULL,
|
|
252
|
+
scope TEXT NOT NULL CHECK(scope IN ('plugin_sessions', 'single_session')),
|
|
253
|
+
access TEXT NOT NULL CHECK(access IN ('read_only', 'read_write')),
|
|
254
|
+
session_id TEXT,
|
|
255
|
+
created_at TEXT NOT NULL,
|
|
256
|
+
expires_at TEXT NOT NULL,
|
|
257
|
+
used_at TEXT,
|
|
258
|
+
CHECK(
|
|
259
|
+
(scope = 'plugin_sessions' AND session_id IS NULL)
|
|
260
|
+
OR (scope = 'single_session' AND session_id IS NOT NULL)
|
|
261
|
+
)
|
|
262
|
+
);
|
|
263
|
+
CREATE INDEX IF NOT EXISTS idx_session_portal_tickets_expires
|
|
264
|
+
ON session_portal_tickets(expires_at);
|
|
265
|
+
CREATE TABLE IF NOT EXISTS session_portal_grants (
|
|
266
|
+
portal_id TEXT PRIMARY KEY,
|
|
267
|
+
secret_hash TEXT NOT NULL,
|
|
268
|
+
plugin_id TEXT NOT NULL,
|
|
269
|
+
scope TEXT NOT NULL CHECK(scope IN ('plugin_sessions', 'single_session')),
|
|
270
|
+
access TEXT NOT NULL CHECK(access IN ('read_only', 'read_write')),
|
|
271
|
+
session_id TEXT,
|
|
272
|
+
created_at TEXT NOT NULL,
|
|
273
|
+
expires_at TEXT NOT NULL,
|
|
274
|
+
revoked_at TEXT,
|
|
275
|
+
CHECK(
|
|
276
|
+
(scope = 'plugin_sessions' AND session_id IS NULL)
|
|
277
|
+
OR (scope = 'single_session' AND session_id IS NOT NULL)
|
|
278
|
+
)
|
|
279
|
+
);
|
|
280
|
+
CREATE INDEX IF NOT EXISTS idx_session_portal_grants_expires
|
|
281
|
+
ON session_portal_grants(expires_at);
|
|
200
282
|
CREATE TABLE IF NOT EXISTS runtime_pairing_claims (
|
|
201
283
|
claim_id TEXT PRIMARY KEY,
|
|
202
284
|
secret_hash TEXT NOT NULL,
|
|
@@ -260,30 +342,67 @@ function migrate(conn) {
|
|
|
260
342
|
CREATE INDEX IF NOT EXISTS idx_runtime_pending_enrollments_expires
|
|
261
343
|
ON runtime_pending_enrollments(expires_at);
|
|
262
344
|
`);
|
|
345
|
+
migratePluginCapabilityDeclarations(conn);
|
|
346
|
+
migrateSessionSnapshots(conn);
|
|
263
347
|
// Breaking plugin-runtime cutover: remove obsolete core-owned state.
|
|
264
348
|
conn.exec(`
|
|
265
349
|
DROP TABLE IF EXISTS control_sessions;
|
|
266
350
|
DROP TABLE IF EXISTS plugin_daemon_worker_settings;
|
|
267
351
|
`);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
352
|
+
addColumnIfMissing(conn, "plugin_marketplaces", "built_in", "INTEGER NOT NULL DEFAULT 0");
|
|
353
|
+
addColumnIfMissing(conn, "sessions", "forked_from_session_id", "TEXT");
|
|
354
|
+
addColumnIfMissing(conn, "plugin_session_launch_operations", "raw_request_hash", "TEXT");
|
|
355
|
+
}
|
|
356
|
+
/** Remove the inert permission-declaration columns left by older installations. */
|
|
357
|
+
function migratePluginCapabilityDeclarations(conn) {
|
|
358
|
+
const columns = conn
|
|
359
|
+
.prepare("PRAGMA table_info(plugin_installations)")
|
|
360
|
+
.all();
|
|
361
|
+
const names = new Set(columns.map((column) => column.name));
|
|
362
|
+
for (const legacyColumn of ["capabilities", "grants"]) {
|
|
363
|
+
if (names.has(legacyColumn)) {
|
|
364
|
+
conn.exec(`ALTER TABLE plugin_installations DROP COLUMN ${legacyColumn}`);
|
|
365
|
+
}
|
|
278
366
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Breaking Session cutover: the previous rows contain mutable Agent/Provider
|
|
370
|
+
* references and no immutable workspace/execution snapshots, so they cannot be
|
|
371
|
+
* migrated faithfully. Remove every database-owned Session artifact and create
|
|
372
|
+
* the new snapshot-only table. User workspace directories are never touched.
|
|
373
|
+
*/
|
|
374
|
+
function migrateSessionSnapshots(conn) {
|
|
375
|
+
const columns = conn.prepare("PRAGMA table_info(sessions)").all();
|
|
376
|
+
const names = new Set(columns.map((column) => column.name));
|
|
377
|
+
if (names.has("workspace") && names.has("execution"))
|
|
378
|
+
return;
|
|
379
|
+
conn.transaction(() => {
|
|
380
|
+
conn.exec(`
|
|
381
|
+
INSERT OR IGNORE INTO session_snapshot_cleanup (session_id)
|
|
382
|
+
SELECT id FROM sessions;
|
|
383
|
+
DELETE FROM session_message_operation_resources;
|
|
384
|
+
DELETE FROM session_message_operations;
|
|
385
|
+
DELETE FROM session_resources;
|
|
386
|
+
DELETE FROM session_items;
|
|
387
|
+
DELETE FROM session_pending_messages;
|
|
388
|
+
DELETE FROM session_emulator_bindings;
|
|
389
|
+
DELETE FROM plugin_session_launch_operations;
|
|
390
|
+
DELETE FROM session_fork_operations;
|
|
391
|
+
DELETE FROM control_links;
|
|
392
|
+
DELETE FROM sessions;
|
|
393
|
+
DROP TABLE sessions;
|
|
394
|
+
CREATE TABLE sessions (
|
|
395
|
+
id TEXT PRIMARY KEY,
|
|
396
|
+
workspace TEXT NOT NULL,
|
|
397
|
+
execution TEXT NOT NULL,
|
|
398
|
+
title TEXT,
|
|
399
|
+
forked_from_session_id TEXT,
|
|
400
|
+
source TEXT NOT NULL,
|
|
401
|
+
created_at TEXT NOT NULL,
|
|
402
|
+
updated_at TEXT
|
|
403
|
+
);
|
|
404
|
+
`);
|
|
405
|
+
}).immediate();
|
|
287
406
|
}
|
|
288
407
|
/** Idempotently add a column to an existing table (no-op if already present). */
|
|
289
408
|
function addColumnIfMissing(conn, table, column, ddl) {
|
|
@@ -292,65 +411,3 @@ function addColumnIfMissing(conn, table, column, ddl) {
|
|
|
292
411
|
return;
|
|
293
412
|
conn.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${ddl}`);
|
|
294
413
|
}
|
|
295
|
-
/**
|
|
296
|
-
* One-time import of a pre-existing `~/.rynx/config.json` (the old hand-edited
|
|
297
|
-
* format, where each entry mixed channel config + agent binding). Each entry
|
|
298
|
-
* becomes a `channels` row (the config) plus an `instances` row (the binding).
|
|
299
|
-
* The instance keeps the entry's key as its `id` so per-instance on-disk state
|
|
300
|
-
* (keyed by instanceId) stays intact. Runs only when empty; the file is renamed
|
|
301
|
-
* afterwards so it's never read — or hand-edited — again.
|
|
302
|
-
*/
|
|
303
|
-
function importLegacyConfig(conn) {
|
|
304
|
-
const { n } = conn.prepare("SELECT COUNT(*) AS n FROM channels").get();
|
|
305
|
-
if (n > 0)
|
|
306
|
-
return;
|
|
307
|
-
const legacy = process.env.RYNX_CONFIG?.trim() || join(daemonHome(), "config.json");
|
|
308
|
-
if (!existsSync(legacy))
|
|
309
|
-
return;
|
|
310
|
-
let channels;
|
|
311
|
-
try {
|
|
312
|
-
const parsed = JSON.parse(readFileSync(legacy, "utf8"));
|
|
313
|
-
if (parsed.channels && typeof parsed.channels === "object") {
|
|
314
|
-
channels = parsed.channels;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
catch {
|
|
318
|
-
return; // malformed legacy file — leave it alone, start empty
|
|
319
|
-
}
|
|
320
|
-
if (!channels)
|
|
321
|
-
return;
|
|
322
|
-
const now = new Date().toISOString();
|
|
323
|
-
const insertChannel = conn.prepare(`INSERT OR IGNORE INTO channels (id, name, type, options, created_at, updated_at)
|
|
324
|
-
VALUES (@id, @name, @type, @options, @now, @now)`);
|
|
325
|
-
const insertInstance = conn.prepare(`INSERT OR IGNORE INTO instances (id, channel_id, agent, enabled, created_at, updated_at)
|
|
326
|
-
VALUES (@id, @channelId, @agent, @enabled, @now, @now)`);
|
|
327
|
-
const tx = conn.transaction(() => {
|
|
328
|
-
for (const [key, entry] of Object.entries(channels)) {
|
|
329
|
-
const channelId = randomUUID();
|
|
330
|
-
insertChannel.run({
|
|
331
|
-
id: channelId,
|
|
332
|
-
name: key,
|
|
333
|
-
type: entry.type ?? key,
|
|
334
|
-
options: entry.options ? JSON.stringify(entry.options) : null,
|
|
335
|
-
now,
|
|
336
|
-
});
|
|
337
|
-
insertInstance.run({
|
|
338
|
-
id: key, // preserve the old key as the runtime instanceId
|
|
339
|
-
channelId,
|
|
340
|
-
agent: entry.agent ?? null,
|
|
341
|
-
enabled: entry.enabled === false ? 0 : 1,
|
|
342
|
-
now,
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
tx();
|
|
347
|
-
try {
|
|
348
|
-
renameSync(legacy, `${legacy}.imported`);
|
|
349
|
-
}
|
|
350
|
-
catch {
|
|
351
|
-
/* leave the source in place if it can't be renamed */
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
function daemonHome() {
|
|
355
|
-
return process.env.RYNX_HOME?.trim() || join(homedir(), ".rynx");
|
|
356
|
-
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
/** Resolve the executable daemon module without importing its side-effectful entry. */
|
|
6
|
+
export function resolveDaemonEntry(moduleUrl = import.meta.url) {
|
|
7
|
+
return join(dirname(fileURLToPath(moduleUrl)), "index-daemon.js");
|
|
8
|
+
}
|
|
9
|
+
export function installedDaemonVersion() {
|
|
10
|
+
const manifest = require("../package.json");
|
|
11
|
+
return typeof manifest.version === "string" ? manifest.version : "0.0.0";
|
|
12
|
+
}
|
package/dist/index-daemon.js
CHANGED
|
@@ -1,70 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Boot order matters: install the EPIPE guard first (we're a pm2 child).
|
|
6
|
-
* The composition root reads static plugin manifests, projects chat instances
|
|
7
|
-
* onto remote runner handles, and supervises non-chat plugin workers. Installed
|
|
8
|
-
* plugin code is loaded only in child processes.
|
|
9
|
-
*/
|
|
10
|
-
import { startRynxDaemonServer } from "./daemon-server.js";
|
|
11
|
-
import { installStdioEpipeGuard } from "./stdio-epipe-guard.js";
|
|
12
|
-
installStdioEpipeGuard();
|
|
13
|
-
async function main() {
|
|
14
|
-
const followsApp = process.env.RYNX_DAEMON_LIFECYCLE === "follow-app";
|
|
15
|
-
let parentDisconnected = followsApp && !process.connected;
|
|
16
|
-
const recordParentDisconnect = () => {
|
|
17
|
-
parentDisconnected = true;
|
|
18
|
-
};
|
|
19
|
-
if (followsApp)
|
|
20
|
-
process.once("disconnect", recordParentDisconnect);
|
|
21
|
-
let server;
|
|
22
|
-
try {
|
|
23
|
-
server = await startRynxDaemonServer();
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
process.off("disconnect", recordParentDisconnect);
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
let shutdownPromise;
|
|
30
|
-
const shutdown = (signal) => {
|
|
31
|
-
if (shutdownPromise)
|
|
32
|
-
return shutdownPromise;
|
|
33
|
-
process.off("SIGINT", onSigint);
|
|
34
|
-
process.off("SIGTERM", onSigterm);
|
|
35
|
-
process.off("disconnect", onDisconnect);
|
|
36
|
-
shutdownPromise = (async () => {
|
|
37
|
-
console.log(JSON.stringify({ level: "info", type: "shutdown", signal }));
|
|
38
|
-
await server.shutdown();
|
|
39
|
-
})();
|
|
40
|
-
shutdownPromise.catch((error) => {
|
|
41
|
-
process.exitCode = 1;
|
|
42
|
-
console.error(`Shutdown failed: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
|
|
43
|
-
});
|
|
44
|
-
return shutdownPromise;
|
|
45
|
-
};
|
|
46
|
-
const onSigint = () => {
|
|
47
|
-
void shutdown("SIGINT");
|
|
48
|
-
};
|
|
49
|
-
const onSigterm = () => {
|
|
50
|
-
void shutdown("SIGTERM");
|
|
51
|
-
};
|
|
52
|
-
const onDisconnect = () => {
|
|
53
|
-
if (process.env.RYNX_DAEMON_LIFECYCLE === "follow-app") {
|
|
54
|
-
void shutdown("APP_DISCONNECT");
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
process.once("SIGINT", onSigint);
|
|
58
|
-
process.once("SIGTERM", onSigterm);
|
|
59
|
-
if (followsApp) {
|
|
60
|
-
process.once("disconnect", onDisconnect);
|
|
61
|
-
process.off("disconnect", recordParentDisconnect);
|
|
62
|
-
if (parentDisconnected || !process.connected) {
|
|
63
|
-
void shutdown("APP_DISCONNECT");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
main().catch((err) => {
|
|
2
|
+
/** Direct daemon executable retained only for standalone PM2 supervision. */
|
|
3
|
+
import { runDaemonProcess } from "./process.js";
|
|
4
|
+
runDaemonProcess().catch((err) => {
|
|
68
5
|
console.error(`Fatal error: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`);
|
|
69
6
|
process.exit(1);
|
|
70
7
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { formatControlUrl, isPm2DaemonOnline, statusDaemon, stopDaemon, streamLogs, } from "./pm2.js";
|
|
2
|
+
export { resolveDaemonEntry } from "./entry-path.js";
|
|
3
|
+
export interface StandaloneDaemonLaunchContext {
|
|
4
|
+
builtinSkillsDir: string;
|
|
5
|
+
cliVersion: string;
|
|
6
|
+
productVersion: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function startDaemon(options: {
|
|
9
|
+
force: boolean;
|
|
10
|
+
quiet?: boolean;
|
|
11
|
+
context: StandaloneDaemonLaunchContext;
|
|
12
|
+
}): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { startDaemon as startPm2Daemon, } from "./pm2.js";
|
|
2
|
+
export { formatControlUrl, isPm2DaemonOnline, statusDaemon, stopDaemon, streamLogs, } from "./pm2.js";
|
|
3
|
+
export { resolveDaemonEntry } from "./entry-path.js";
|
|
4
|
+
export function startDaemon(options) {
|
|
5
|
+
return startPm2Daemon({
|
|
6
|
+
force: options.force,
|
|
7
|
+
quiet: options.quiet,
|
|
8
|
+
launchEnvironment: {
|
|
9
|
+
RYNX_BUILTIN_SKILLS_DIR: options.context.builtinSkillsDir,
|
|
10
|
+
RYNX_CLI_VERSION: options.context.cliVersion,
|
|
11
|
+
RYNX_DISTRIBUTION: "standalone",
|
|
12
|
+
RYNX_PRODUCT_VERSION: options.context.productVersion,
|
|
13
|
+
RYNX_DAEMON_LIFECYCLE: "standalone",
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type MaintenanceLeaseReleaseOutcome = "released" | "not_active" | "denied";
|
|
2
|
+
export interface MaintenanceLeaseStoreOptions {
|
|
3
|
+
filePath?: string;
|
|
4
|
+
now?: () => number;
|
|
5
|
+
ttlMs?: number;
|
|
6
|
+
/** Test seam; production fsyncs the lease directory after rename/remove. */
|
|
7
|
+
syncDirectory?: (directory: string) => void;
|
|
8
|
+
/** Process-local recovery hook, invoked once when a live lease is observed expired. */
|
|
9
|
+
onExpired?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function maintenanceLeaseFile(env?: NodeJS.ProcessEnv): string;
|
|
12
|
+
/**
|
|
13
|
+
* Cross-process admission fence used only during daemon replacement.
|
|
14
|
+
*
|
|
15
|
+
* The raw bearer token is never persisted. The bounded expiry means a crashed
|
|
16
|
+
* upgrader cannot leave the daemon permanently unavailable, while atomic rename
|
|
17
|
+
* ensures a replacement daemon sees either the complete lease or no lease.
|
|
18
|
+
*/
|
|
19
|
+
export declare class MaintenanceLeaseStore {
|
|
20
|
+
private readonly filePath;
|
|
21
|
+
private readonly now;
|
|
22
|
+
private readonly ttlMs;
|
|
23
|
+
private readonly syncDirectory;
|
|
24
|
+
private readonly onExpired;
|
|
25
|
+
private lease;
|
|
26
|
+
private expiryTimer;
|
|
27
|
+
constructor(options?: MaintenanceLeaseStoreOptions);
|
|
28
|
+
isActive(): boolean;
|
|
29
|
+
isHeldBy(token: string): boolean;
|
|
30
|
+
acquire(token: string): boolean;
|
|
31
|
+
release(token: string): MaintenanceLeaseReleaseOutcome;
|
|
32
|
+
private readPersistedLease;
|
|
33
|
+
private writePersistedLease;
|
|
34
|
+
private clear;
|
|
35
|
+
private clearFile;
|
|
36
|
+
private expire;
|
|
37
|
+
private scheduleExpiry;
|
|
38
|
+
private cancelExpiry;
|
|
39
|
+
}
|