@rynx-ai/daemon 0.1.11-beta.11 → 0.1.11-beta.12
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/package.json +1 -1
- package/dist/chrome-for-testing-store.js +4 -4
- package/dist/control-link-store.js +4 -3
- package/dist/daemon-owner.js +4 -7
- package/dist/db.d.ts +2 -2
- package/dist/db.js +4 -5
- package/dist/migrations/cleanup-legacy-sessions.js +4 -3
- package/dist/plugin-cli.js +2 -1
- package/dist/plugin-installer.js +3 -2
- package/dist/plugin-marketplace.js +2 -1
- 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.js +6 -5
- package/dist/session-log-store.js +2 -1
- package/dist/session-meta-store.js +3 -2
- package/dist/session-portal-grant-store.js +2 -1
- package/dist/session-resource-store.js +3 -2
- package/dist/sqlite.d.ts +22 -0
- package/dist/sqlite.js +57 -0
- package/package.json +10 -11
|
@@ -9,9 +9,10 @@ import { createHash } from "node:crypto";
|
|
|
9
9
|
import { accessSync, chmodSync, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync, writeSync, } from "node:fs";
|
|
10
10
|
import { constants as fsConstants } from "node:fs";
|
|
11
11
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
12
|
+
import { DatabaseSync } from "node:sqlite";
|
|
12
13
|
import { rynxHome } from "@rynx-ai/core";
|
|
13
|
-
import Database from "better-sqlite3";
|
|
14
14
|
import extractZipArchive from "extract-zip";
|
|
15
|
+
import { hasSqlitePrimaryCode, SQLITE_BUSY, SQLITE_LOCKED } from "./sqlite.js";
|
|
15
16
|
const BUILD_OWNER_FILE = ".rynx-cft-owner.json";
|
|
16
17
|
const STAGING_OWNER_FILE = ".rynx-cft-staging-owner.json";
|
|
17
18
|
const ACTIVE_MANIFEST_FILE = "active.json";
|
|
@@ -360,7 +361,7 @@ function acquireStoreLock(rootDir) {
|
|
|
360
361
|
throw new Error("Chrome for Testing store lock is not a direct regular file");
|
|
361
362
|
}
|
|
362
363
|
}
|
|
363
|
-
const connection = new
|
|
364
|
+
const connection = new DatabaseSync(lockPath, { timeout: 0 });
|
|
364
365
|
try {
|
|
365
366
|
const stat = lstatSync(lockPath);
|
|
366
367
|
if (!stat.isFile() ||
|
|
@@ -369,14 +370,13 @@ function acquireStoreLock(rootDir) {
|
|
|
369
370
|
throw new Error("Chrome for Testing store lock escapes its owned root");
|
|
370
371
|
}
|
|
371
372
|
chmodSync(lockPath, 0o600);
|
|
372
|
-
connection.pragma("busy_timeout = 0");
|
|
373
373
|
connection.exec("BEGIN EXCLUSIVE");
|
|
374
374
|
connection.exec("CREATE TABLE IF NOT EXISTS cft_store_guard (id INTEGER PRIMARY KEY)");
|
|
375
375
|
return connection;
|
|
376
376
|
}
|
|
377
377
|
catch (error) {
|
|
378
378
|
connection.close();
|
|
379
|
-
if (
|
|
379
|
+
if (hasSqlitePrimaryCode(error, SQLITE_BUSY) || hasSqlitePrimaryCode(error, SQLITE_LOCKED)) {
|
|
380
380
|
throw new Error("Chrome for Testing store is busy in another process", { cause: error });
|
|
381
381
|
}
|
|
382
382
|
throw error;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { db } from "./db.js";
|
|
3
|
+
import { transaction } from "./sqlite.js";
|
|
3
4
|
const DEFAULT_TTL_MS = 5 * 60_000;
|
|
4
5
|
const MAX_TTL_MS = 15 * 60_000;
|
|
5
6
|
/** Create a short-lived, one-use link into the generic session console. */
|
|
@@ -23,7 +24,7 @@ export function consumeControlLink(tokenInput) {
|
|
|
23
24
|
if (!token || token.length > 512)
|
|
24
25
|
return null;
|
|
25
26
|
const now = new Date().toISOString();
|
|
26
|
-
const consume = db()
|
|
27
|
+
const consume = transaction(db(), () => {
|
|
27
28
|
const row = db()
|
|
28
29
|
.prepare(`SELECT session_id
|
|
29
30
|
FROM control_links
|
|
@@ -39,9 +40,9 @@ export function consumeControlLink(tokenInput) {
|
|
|
39
40
|
return consume.immediate();
|
|
40
41
|
}
|
|
41
42
|
export function pruneControlLinks(now = new Date().toISOString()) {
|
|
42
|
-
return db()
|
|
43
|
+
return Number(db()
|
|
43
44
|
.prepare("DELETE FROM control_links WHERE expires_at <= ? OR used_at IS NOT NULL")
|
|
44
|
-
.run(now).changes;
|
|
45
|
+
.run(now).changes);
|
|
45
46
|
}
|
|
46
47
|
function required(value, label) {
|
|
47
48
|
const normalized = value?.trim();
|
package/dist/daemon-owner.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { DatabaseSync } from "node:sqlite";
|
|
4
5
|
import { rynxHome } from "@rynx-ai/core";
|
|
5
|
-
import
|
|
6
|
+
import { hasSqlitePrimaryCode, SQLITE_BUSY, SQLITE_LOCKED } from "./sqlite.js";
|
|
6
7
|
const LOCK_FILE = "daemon-owner.sqlite";
|
|
7
8
|
const OWNER_FILE = "daemon-owner.json";
|
|
8
9
|
/**
|
|
@@ -18,16 +19,15 @@ export async function acquireDaemonOwner(env = process.env) {
|
|
|
18
19
|
const lockPath = path.join(home, LOCK_FILE);
|
|
19
20
|
const ownerFile = path.join(home, OWNER_FILE);
|
|
20
21
|
await mkdir(home, { recursive: true, mode: 0o700 });
|
|
21
|
-
const connection = new
|
|
22
|
+
const connection = new DatabaseSync(lockPath, { timeout: 0 });
|
|
22
23
|
try {
|
|
23
|
-
connection.pragma("busy_timeout = 0");
|
|
24
24
|
connection.exec("BEGIN EXCLUSIVE");
|
|
25
25
|
// Force this transaction to hold a write lock even on a brand-new file.
|
|
26
26
|
connection.exec("CREATE TABLE IF NOT EXISTS daemon_owner_guard (id INTEGER PRIMARY KEY)");
|
|
27
27
|
}
|
|
28
28
|
catch (error) {
|
|
29
29
|
connection.close();
|
|
30
|
-
if (
|
|
30
|
+
if (hasSqlitePrimaryCode(error, SQLITE_BUSY) || hasSqlitePrimaryCode(error, SQLITE_LOCKED)) {
|
|
31
31
|
throw await ownerConflictError(home, ownerFile);
|
|
32
32
|
}
|
|
33
33
|
throw error;
|
|
@@ -99,6 +99,3 @@ function rollbackAndClose(connection) {
|
|
|
99
99
|
connection.close();
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
function hasCode(error, code) {
|
|
103
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
104
|
-
}
|
package/dist/db.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
2
|
/** Absolute path to the SQLite file (override via `RYNX_DB`, else `~/.rynx/rynx.db`). */
|
|
3
3
|
export declare function dbPath(): string;
|
|
4
4
|
/** Open (or reuse) the database at the current path, migrating + importing on first open. */
|
|
5
|
-
export declare function db():
|
|
5
|
+
export declare function db(): DatabaseSync;
|
package/dist/db.js
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { chmodSync, mkdirSync } from "node:fs";
|
|
20
20
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
21
|
+
import { DatabaseSync } from "node:sqlite";
|
|
21
22
|
import { rynxHome } from "@rynx-ai/core";
|
|
22
|
-
import
|
|
23
|
+
import { transaction } from "./sqlite.js";
|
|
23
24
|
const connections = new Map();
|
|
24
25
|
/** Absolute path to the SQLite file (override via `RYNX_DB`, else `~/.rynx/rynx.db`). */
|
|
25
26
|
export function dbPath() {
|
|
@@ -36,9 +37,7 @@ export function db() {
|
|
|
36
37
|
if (cached)
|
|
37
38
|
return cached;
|
|
38
39
|
mkdirSync(dirname(path), { recursive: true });
|
|
39
|
-
const conn = new
|
|
40
|
-
conn.pragma("busy_timeout = 5000");
|
|
41
|
-
conn.pragma("foreign_keys = ON");
|
|
40
|
+
const conn = new DatabaseSync(path, { timeout: 5000, enableForeignKeyConstraints: true });
|
|
42
41
|
migrate(conn);
|
|
43
42
|
// Plugin state and daemon credentials are private.
|
|
44
43
|
try {
|
|
@@ -376,7 +375,7 @@ function migrateSessionSnapshots(conn) {
|
|
|
376
375
|
const names = new Set(columns.map((column) => column.name));
|
|
377
376
|
if (names.has("workspace") && names.has("execution"))
|
|
378
377
|
return;
|
|
379
|
-
|
|
378
|
+
transaction(conn, () => {
|
|
380
379
|
conn.exec(`
|
|
381
380
|
INSERT OR IGNORE INTO session_snapshot_cleanup (session_id)
|
|
382
381
|
SELECT id FROM sessions;
|
|
@@ -20,6 +20,7 @@ import { mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
|
20
20
|
import path from "node:path";
|
|
21
21
|
import { rynxHome } from "@rynx-ai/core";
|
|
22
22
|
import { db } from "../db.js";
|
|
23
|
+
import { transaction } from "../sqlite.js";
|
|
23
24
|
/** Mirrors the daemon runtime store's default resolution. */
|
|
24
25
|
function defaultProxyDir() {
|
|
25
26
|
return path.join(rynxHome(), ".codex-proxy");
|
|
@@ -46,9 +47,9 @@ export function cleanupLegacySessions(opts = {}) {
|
|
|
46
47
|
let deletedLogRows = 0;
|
|
47
48
|
let deletedSessions = 0;
|
|
48
49
|
let prunedStoreEntries = 0;
|
|
49
|
-
|
|
50
|
-
deletedLogRows = conn.prepare(`DELETE FROM session_items WHERE ${LEGACY_LOG}`).run().changes;
|
|
51
|
-
deletedSessions = conn.prepare(`DELETE FROM sessions WHERE ${LEGACY_META}`).run().changes;
|
|
50
|
+
transaction(conn, () => {
|
|
51
|
+
deletedLogRows = Number(conn.prepare(`DELETE FROM session_items WHERE ${LEGACY_LOG}`).run().changes);
|
|
52
|
+
deletedSessions = Number(conn.prepare(`DELETE FROM sessions WHERE ${LEGACY_META}`).run().changes);
|
|
52
53
|
})();
|
|
53
54
|
// The runtime store is daemon-owned. Plugin routing stores remain private to
|
|
54
55
|
// their plugins and are never inspected by this migration.
|
package/dist/plugin-cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { mkdirSync, realpathSync, statSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createPluginCliInvocationLeaseId, removePluginRuntimeLease, writePluginRuntimeLease, } from "@rynx-ai/plugin-sdk";
|
|
5
5
|
import { db } from "./db.js";
|
|
6
|
+
import { transaction } from "./sqlite.js";
|
|
6
7
|
import { pluginDataDir, pluginRuntimeCacheDir } from "./plugin-installer.js";
|
|
7
8
|
import { validatePluginPackageSnapshot } from "./plugin-package.js";
|
|
8
9
|
import { getPlugin } from "./plugin-store.js";
|
|
@@ -74,7 +75,7 @@ export async function runPluginCliCommand(pluginId, args, options = {}) {
|
|
|
74
75
|
removePluginRuntimeLease(dataDir, leaseId);
|
|
75
76
|
};
|
|
76
77
|
try {
|
|
77
|
-
db()
|
|
78
|
+
transaction(db(), () => {
|
|
78
79
|
const record = getPlugin(pluginId);
|
|
79
80
|
if (!record)
|
|
80
81
|
throw new Error(`plugin "${pluginId}" is not installed`);
|
package/dist/plugin-installer.js
CHANGED
|
@@ -4,6 +4,7 @@ import { join, resolve } from "node:path";
|
|
|
4
4
|
import { MARKETPLACE_ID_PATTERN, pluginCliInvocationLeasePid, readPluginRuntimeLeases, removePluginRuntimeLease, } from "@rynx-ai/plugin-sdk";
|
|
5
5
|
import { rynxHome } from "@rynx-ai/core";
|
|
6
6
|
import { db } from "./db.js";
|
|
7
|
+
import { transaction } from "./sqlite.js";
|
|
7
8
|
import { isBuiltInPluginMarketplace } from "./plugin-marketplace.js";
|
|
8
9
|
import { detectPluginSource, inspectPluginPackage, materializePluginPackage, } from "./plugin-package.js";
|
|
9
10
|
import { canonicalPluginId, getPlugin, removePlugin, upsertPlugin, } from "./plugin-store.js";
|
|
@@ -117,7 +118,7 @@ export async function preparePluginInstallation(spec, onProgress = () => { }, op
|
|
|
117
118
|
promotedByThisTransaction = true;
|
|
118
119
|
}
|
|
119
120
|
promotedPath = realpathSync(target);
|
|
120
|
-
db()
|
|
121
|
+
transaction(db(), () => {
|
|
121
122
|
const current = getPlugin(canonicalId);
|
|
122
123
|
if (!samePluginGeneration(existing, current)) {
|
|
123
124
|
throw new Error(`plugin "${canonicalId}" changed concurrently; retry`);
|
|
@@ -172,7 +173,7 @@ export async function preparePluginInstallation(spec, onProgress = () => { }, op
|
|
|
172
173
|
}
|
|
173
174
|
export async function uninstallPlugin(identity, onProgress = () => { }, options = {}) {
|
|
174
175
|
let record;
|
|
175
|
-
const removed = db()
|
|
176
|
+
const removed = transaction(db(), () => {
|
|
176
177
|
record = getPlugin(identity);
|
|
177
178
|
if (!record)
|
|
178
179
|
return false;
|
|
@@ -3,6 +3,7 @@ import { isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
|
3
3
|
import { MARKETPLACE_ID_PATTERN, parsePluginMarketplace, } from "@rynx-ai/plugin-sdk";
|
|
4
4
|
import { rynxHome } from "@rynx-ai/core";
|
|
5
5
|
import { db } from "./db.js";
|
|
6
|
+
import { transaction } from "./sqlite.js";
|
|
6
7
|
import { detectPluginSource, materializePluginPackage, parsePluginSourceSpec, pluginSourceFingerprint, } from "./plugin-package.js";
|
|
7
8
|
export function listPluginMarketplaces() {
|
|
8
9
|
return db().prepare("SELECT * FROM plugin_marketplaces ORDER BY marketplace_id").all().map(toRecord);
|
|
@@ -27,7 +28,7 @@ export async function addPluginMarketplace(spec, options = {}) {
|
|
|
27
28
|
throw new Error(`built-in marketplace "${marketplaceId}" is managed by Rynx`);
|
|
28
29
|
}
|
|
29
30
|
const now = new Date().toISOString();
|
|
30
|
-
db()
|
|
31
|
+
transaction(db(), () => {
|
|
31
32
|
const binding = db().prepare(`SELECT source_fingerprint AS sourceFingerprint
|
|
32
33
|
FROM plugin_marketplace_bindings
|
|
33
34
|
WHERE marketplace_id = ?`).get(marketplaceId);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type DatabaseSync } from "node:sqlite";
|
|
2
2
|
declare const DEFAULT_AUTHORITIES: readonly ["daemon.full"];
|
|
3
3
|
export type RuntimeAuthority = (typeof DEFAULT_AUTHORITIES)[number];
|
|
4
4
|
export type RemoteRuntimeAccessErrorCode = "invalid_claim" | "claim_expired" | "claim_used" | "grant_not_found" | "grant_revoked" | "grant_identity_mismatch" | "conflict" | "corrupt_state";
|
|
@@ -46,7 +46,7 @@ type Clock = () => Date;
|
|
|
46
46
|
export declare class RemoteRuntimeAccessStore {
|
|
47
47
|
private readonly connection;
|
|
48
48
|
private readonly clock;
|
|
49
|
-
constructor(connection?:
|
|
49
|
+
constructor(connection?: DatabaseSync, clock?: Clock);
|
|
50
50
|
createPairingClaim(input: CreatePairingClaimInput): RuntimePairingClaim;
|
|
51
51
|
redeemPairingClaim(input: RedeemPairingClaimInput): RuntimeClientGrant;
|
|
52
52
|
resolveActiveGrant(input: ResolveActiveGrantInput): RuntimeClientGrant;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, createPublicKey, randomBytes, timingSafeEqual, } from "node:crypto";
|
|
2
2
|
import { db } from "./db.js";
|
|
3
|
+
import { hasSqlitePrimaryCode, SQLITE_CONSTRAINT, transaction } from "./sqlite.js";
|
|
3
4
|
const CLAIM_SECRET_HASH_DOMAIN = "rynx-runtime-pairing-claim-secret-v1\0";
|
|
4
5
|
const DEFAULT_AUTHORITIES = Object.freeze(["daemon.full"]);
|
|
5
6
|
const MIN_TTL_MS = 1_000;
|
|
@@ -35,7 +36,7 @@ export class RemoteRuntimeAccessStore {
|
|
|
35
36
|
const created = this.now();
|
|
36
37
|
const createdAt = created.toISOString();
|
|
37
38
|
const expiresAt = new Date(created.getTime() + ttlMs).toISOString();
|
|
38
|
-
const create = this.connection
|
|
39
|
+
const create = transaction(this.connection, () => {
|
|
39
40
|
this.deleteRetainedExpiredClaims(created);
|
|
40
41
|
this.connection
|
|
41
42
|
.prepare(`INSERT INTO runtime_pairing_claims
|
|
@@ -60,7 +61,7 @@ export class RemoteRuntimeAccessStore {
|
|
|
60
61
|
const clientId = validId(input.clientId, "clientId", "invalid_claim");
|
|
61
62
|
const clientPublicKey = canonicalEd25519PublicKey(input.clientPublicKey, "invalid_claim");
|
|
62
63
|
const requestedLabel = optionalLabel(input.label, "invalid_claim");
|
|
63
|
-
const redeem = this.connection
|
|
64
|
+
const redeem = transaction(this.connection, () => {
|
|
64
65
|
const now = this.now().toISOString();
|
|
65
66
|
const claim = this.connection
|
|
66
67
|
.prepare(`SELECT claim_id, secret_hash, authorities, client_label, created_at, expires_at,
|
|
@@ -124,7 +125,7 @@ export class RemoteRuntimeAccessStore {
|
|
|
124
125
|
const grantId = validId(input.grantId, "grantId", "grant_not_found");
|
|
125
126
|
const clientId = validId(input.clientId, "clientId", "grant_identity_mismatch");
|
|
126
127
|
const clientPublicKey = canonicalEd25519PublicKey(input.clientPublicKey, "grant_identity_mismatch");
|
|
127
|
-
const resolve = this.connection
|
|
128
|
+
const resolve = transaction(this.connection, () => {
|
|
128
129
|
const row = this.connection
|
|
129
130
|
.prepare(`SELECT grant_id, client_id, client_public_key, client_label, authorities,
|
|
130
131
|
created_at, revoked_at, last_used_at
|
|
@@ -180,7 +181,7 @@ export class RemoteRuntimeAccessStore {
|
|
|
180
181
|
revokeClientGrant(grantIdInput) {
|
|
181
182
|
const grantId = validId(grantIdInput, "grantId", "grant_not_found");
|
|
182
183
|
const revokedAt = this.now().toISOString();
|
|
183
|
-
const revoke = this.connection
|
|
184
|
+
const revoke = transaction(this.connection, () => {
|
|
184
185
|
const revoked = this.connection
|
|
185
186
|
.prepare(`UPDATE runtime_client_grants
|
|
186
187
|
SET revoked_at = ?
|
|
@@ -382,11 +383,7 @@ function grantFromRow(row) {
|
|
|
382
383
|
};
|
|
383
384
|
}
|
|
384
385
|
function isSqliteConstraint(error) {
|
|
385
|
-
return (
|
|
386
|
-
error !== null &&
|
|
387
|
-
"code" in error &&
|
|
388
|
-
typeof error.code === "string" &&
|
|
389
|
-
error.code.startsWith("SQLITE_CONSTRAINT"));
|
|
386
|
+
return hasSqlitePrimaryCode(error, SQLITE_CONSTRAINT);
|
|
390
387
|
}
|
|
391
388
|
function accessError(code, message, cause) {
|
|
392
389
|
return new RemoteRuntimeAccessError(code, message, cause === undefined ? undefined : { cause });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type DatabaseSync } from "node:sqlite";
|
|
2
2
|
export interface RemoteRuntimeTarget {
|
|
3
3
|
daemonId: string;
|
|
4
4
|
displayName: string;
|
|
@@ -40,7 +40,7 @@ export interface CommitRemoteRuntimePendingEnrollmentOptions {
|
|
|
40
40
|
export declare class RemoteRuntimeTargetStore {
|
|
41
41
|
private readonly connection;
|
|
42
42
|
private readonly clock;
|
|
43
|
-
constructor(connection?:
|
|
43
|
+
constructor(connection?: DatabaseSync, clock?: () => Date);
|
|
44
44
|
add(input: AddRemoteRuntimeTargetInput): RemoteRuntimeTarget;
|
|
45
45
|
addPendingEnrollment(input: AddRemoteRuntimePendingEnrollmentInput): RemoteRuntimePendingEnrollment;
|
|
46
46
|
getPendingEnrollment(claimIdInput: string): RemoteRuntimePendingEnrollment | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createPublicKey } from "node:crypto";
|
|
2
2
|
import { db } from "./db.js";
|
|
3
|
+
import { hasSqlitePrimaryCode, SQLITE_CONSTRAINT, transaction } from "./sqlite.js";
|
|
3
4
|
const OPAQUE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,255}$/;
|
|
4
5
|
const CREDENTIAL_REF_PATTERN = /^credential_[A-Za-z0-9_-]{24}$/;
|
|
5
6
|
const MAX_DISPLAY_NAME_CHARS = 128;
|
|
@@ -13,7 +14,7 @@ export class RemoteRuntimeTargetStore {
|
|
|
13
14
|
add(input) {
|
|
14
15
|
const normalized = normalizeTargetInput(input);
|
|
15
16
|
const now = validNow(this.clock()).toISOString();
|
|
16
|
-
const insert = this.connection
|
|
17
|
+
const insert = transaction(this.connection, () => {
|
|
17
18
|
this.connection.prepare(`INSERT INTO runtime_targets
|
|
18
19
|
(daemon_id, display_name, identity_public_key, credential_ref, created_at, updated_at)
|
|
19
20
|
VALUES (?, ?, ?, ?, ?, ?)`).run(normalized.daemonId, normalized.displayName, normalized.identityPublicKey, normalized.credentialRef, now, now);
|
|
@@ -67,7 +68,7 @@ export class RemoteRuntimeTargetStore {
|
|
|
67
68
|
}
|
|
68
69
|
removePendingEnrollment(claimIdInput) {
|
|
69
70
|
const claimId = validId(claimIdInput, "claimId");
|
|
70
|
-
const remove = this.connection
|
|
71
|
+
const remove = transaction(this.connection, () => {
|
|
71
72
|
const pending = this.getPendingEnrollment(claimId);
|
|
72
73
|
if (!pending)
|
|
73
74
|
return undefined;
|
|
@@ -82,7 +83,7 @@ export class RemoteRuntimeTargetStore {
|
|
|
82
83
|
const claimId = validId(claimIdInput, "claimId");
|
|
83
84
|
const normalized = normalizeTargetInput(input);
|
|
84
85
|
const now = validNow(this.clock()).toISOString();
|
|
85
|
-
const commit = this.connection
|
|
86
|
+
const commit = transaction(this.connection, () => {
|
|
86
87
|
const pending = this.getPendingEnrollment(claimId);
|
|
87
88
|
if (!pending)
|
|
88
89
|
throw new Error("pending Runtime enrollment was not found");
|
|
@@ -134,7 +135,7 @@ export class RemoteRuntimeTargetStore {
|
|
|
134
135
|
}
|
|
135
136
|
remove(daemonIdInput) {
|
|
136
137
|
const daemonId = validId(daemonIdInput, "daemonId");
|
|
137
|
-
const remove = this.connection
|
|
138
|
+
const remove = transaction(this.connection, () => {
|
|
138
139
|
const target = this.get(daemonId);
|
|
139
140
|
if (!target)
|
|
140
141
|
return undefined;
|
|
@@ -321,6 +322,5 @@ function validNow(value) {
|
|
|
321
322
|
return value;
|
|
322
323
|
}
|
|
323
324
|
function isSqliteConstraint(error) {
|
|
324
|
-
return
|
|
325
|
-
typeof error.code === "string" && error.code.startsWith("SQLITE_CONSTRAINT");
|
|
325
|
+
return hasSqlitePrimaryCode(error, SQLITE_CONSTRAINT);
|
|
326
326
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SessionEmulatorBindingConflictError, } from "@rynx-ai/server";
|
|
2
2
|
import { db } from "./db.js";
|
|
3
|
+
import { hasSqlitePrimaryCode, SQLITE_CONSTRAINT, transaction } from "./sqlite.js";
|
|
3
4
|
export class SqliteSessionEmulatorBindingStore {
|
|
4
5
|
get(sessionId) {
|
|
5
6
|
const row = db().prepare(`SELECT session_id, binding_id, backend, device_id, device_name, attached_at
|
|
@@ -13,7 +14,7 @@ export class SqliteSessionEmulatorBindingStore {
|
|
|
13
14
|
}
|
|
14
15
|
bind(record) {
|
|
15
16
|
const connection = db();
|
|
16
|
-
const replace =
|
|
17
|
+
const replace = transaction(connection, () => {
|
|
17
18
|
connection.prepare("DELETE FROM session_emulator_bindings WHERE session_id = ?")
|
|
18
19
|
.run(record.sessionId);
|
|
19
20
|
connection.prepare(`INSERT INTO session_emulator_bindings
|
|
@@ -40,7 +41,7 @@ export class SqliteSessionEmulatorBindingStore {
|
|
|
40
41
|
return db().prepare("DELETE FROM session_emulator_bindings WHERE session_id = ?").run(sessionId).changes > 0;
|
|
41
42
|
}
|
|
42
43
|
releaseDevice(backend, deviceId) {
|
|
43
|
-
return db().prepare("DELETE FROM session_emulator_bindings WHERE backend = ? AND device_id = ?").run(backend, deviceId).changes;
|
|
44
|
+
return Number(db().prepare("DELETE FROM session_emulator_bindings WHERE backend = ? AND device_id = ?").run(backend, deviceId).changes);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
function bindingRecord(row) {
|
|
@@ -54,8 +55,5 @@ function bindingRecord(row) {
|
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
function isSqliteConstraint(error) {
|
|
57
|
-
|
|
58
|
-
return false;
|
|
59
|
-
const code = error.code;
|
|
60
|
-
return typeof code === "string" && code.startsWith("SQLITE_CONSTRAINT");
|
|
58
|
+
return hasSqlitePrimaryCode(error, SQLITE_CONSTRAINT);
|
|
61
59
|
}
|
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { newSessionId, newSessionItemId, normalizeSessionTitle, resolvedExecutionSnapshotSchema, rynxHome, sessionWorkspaceSnapshotSchema, } from "@rynx-ai/core";
|
|
5
5
|
import { MachineSessionServiceFailure, } from "@rynx-ai/server";
|
|
6
6
|
import { db } from "./db.js";
|
|
7
|
+
import { scalar, transaction } from "./sqlite.js";
|
|
7
8
|
const FORK_COLS = [
|
|
8
9
|
"operation_id",
|
|
9
10
|
"request_hash",
|
|
@@ -39,9 +40,8 @@ export class SqliteSessionForkStore {
|
|
|
39
40
|
}
|
|
40
41
|
const now = new Date().toISOString();
|
|
41
42
|
let targetSessionId = newSessionId();
|
|
42
|
-
while (db().prepare("SELECT 1 FROM sessions WHERE id = ?")
|
|
43
|
-
db().prepare("SELECT 1 FROM session_fork_operations WHERE target_session_id = ?")
|
|
44
|
-
.pluck().get(targetSessionId)) {
|
|
43
|
+
while (scalar(db().prepare("SELECT 1 FROM sessions WHERE id = ?")).get(targetSessionId) ||
|
|
44
|
+
scalar(db().prepare("SELECT 1 FROM session_fork_operations WHERE target_session_id = ?")).get(targetSessionId)) {
|
|
45
45
|
targetSessionId = newSessionId();
|
|
46
46
|
}
|
|
47
47
|
try {
|
|
@@ -130,7 +130,7 @@ export class SqliteSessionForkStore {
|
|
|
130
130
|
};
|
|
131
131
|
});
|
|
132
132
|
const conn = db();
|
|
133
|
-
|
|
133
|
+
transaction(conn, () => {
|
|
134
134
|
if (input.operationId) {
|
|
135
135
|
const current = this.read(input.operationId);
|
|
136
136
|
if (current?.state === "completed")
|
|
@@ -160,8 +160,24 @@ export class SqliteSessionForkStore {
|
|
|
160
160
|
@byte_length, @uploaded_bytes, @sha256, @width, @height, 'committed',
|
|
161
161
|
@message_item_id, @created_at, @committed_at
|
|
162
162
|
)`);
|
|
163
|
-
for (const resource of resources)
|
|
164
|
-
insertResource.run(
|
|
163
|
+
for (const resource of resources) {
|
|
164
|
+
insertResource.run({
|
|
165
|
+
id: resource.id,
|
|
166
|
+
session_id: resource.session_id,
|
|
167
|
+
client_upload_id: resource.client_upload_id,
|
|
168
|
+
upload_id: resource.upload_id,
|
|
169
|
+
filename: resource.filename,
|
|
170
|
+
media_type: resource.media_type,
|
|
171
|
+
byte_length: resource.byte_length,
|
|
172
|
+
uploaded_bytes: resource.uploaded_bytes,
|
|
173
|
+
sha256: resource.sha256,
|
|
174
|
+
width: resource.width,
|
|
175
|
+
height: resource.height,
|
|
176
|
+
message_item_id: resource.message_item_id,
|
|
177
|
+
created_at: resource.created_at,
|
|
178
|
+
committed_at: resource.committed_at,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
165
181
|
const insertItem = conn.prepare(`INSERT INTO session_items (
|
|
166
182
|
id, session_id, position, response_id, type, status,
|
|
167
183
|
data, created_by, created_at
|
|
@@ -203,9 +219,9 @@ export class SqliteSessionForkStore {
|
|
|
203
219
|
ORDER BY created_at`).all().map(toOperation);
|
|
204
220
|
}
|
|
205
221
|
hasReservedSource(sessionId) {
|
|
206
|
-
return Boolean(db().prepare(`SELECT 1
|
|
222
|
+
return Boolean(scalar(db().prepare(`SELECT 1
|
|
207
223
|
FROM session_fork_operations
|
|
208
|
-
WHERE source_session_id = ? AND state = 'reserved'`)
|
|
224
|
+
WHERE source_session_id = ? AND state = 'reserved'`)).get(sessionId));
|
|
209
225
|
}
|
|
210
226
|
markTargetDeleted(sessionId) {
|
|
211
227
|
const now = new Date().toISOString();
|
|
@@ -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}$/;
|
|
@@ -58,7 +59,7 @@ export function replayPluginSessionLaunch(input) {
|
|
|
58
59
|
throw new SessionLaunchOperationTargetDeletedError(pluginId, scopeId, operationId, existing.session_id);
|
|
59
60
|
}
|
|
60
61
|
if (!sessionExists(existing.session_id)) {
|
|
61
|
-
db()
|
|
62
|
+
transaction(db(), () => {
|
|
62
63
|
tombstoneOperations(existing.session_id, new Date().toISOString());
|
|
63
64
|
}).immediate();
|
|
64
65
|
throw new SessionLaunchOperationTargetDeletedError(pluginId, scopeId, operationId, existing.session_id);
|
|
@@ -81,7 +82,7 @@ export function ensurePluginSessionLaunch(input) {
|
|
|
81
82
|
const rawRequestHash = input.rawRequestHash
|
|
82
83
|
? requiredRequestHash(input.rawRequestHash)
|
|
83
84
|
: requestHash;
|
|
84
|
-
const ensure = db()
|
|
85
|
+
const ensure = transaction(db(), () => {
|
|
85
86
|
const existing = readOperation(pluginId, scopeId, operationId);
|
|
86
87
|
if (existing) {
|
|
87
88
|
if (existing.request_hash !== requestHash &&
|
|
@@ -139,14 +140,14 @@ function readOperation(pluginId, scopeId, operationId) {
|
|
|
139
140
|
.get(pluginId, scopeId, operationId);
|
|
140
141
|
}
|
|
141
142
|
function sessionExists(sessionId) {
|
|
142
|
-
return Boolean(db().prepare("SELECT 1 FROM sessions WHERE id = ?")
|
|
143
|
+
return Boolean(scalar(db().prepare("SELECT 1 FROM sessions WHERE id = ?")).get(sessionId));
|
|
143
144
|
}
|
|
144
145
|
function tombstoneOperations(sessionId, now) {
|
|
145
|
-
return db()
|
|
146
|
+
return Number(db()
|
|
146
147
|
.prepare(`UPDATE plugin_session_launch_operations
|
|
147
148
|
SET state = 'deleted', updated_at = ?, deleted_at = ?
|
|
148
149
|
WHERE session_id = ? AND state = 'active'`)
|
|
149
|
-
.run(now, now, sessionId).changes;
|
|
150
|
+
.run(now, now, sessionId).changes);
|
|
150
151
|
}
|
|
151
152
|
function requiredId(value, label) {
|
|
152
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) => {
|
|
@@ -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,7 +47,7 @@ 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);
|
|
50
51
|
}
|
|
51
52
|
export function setSessionExecution(id, execution) {
|
|
52
53
|
db()
|
|
@@ -54,7 +55,7 @@ export function setSessionExecution(id, execution) {
|
|
|
54
55
|
.run(JSON.stringify(resolvedExecutionSnapshotSchema.parse(execution)), new Date().toISOString(), id);
|
|
55
56
|
}
|
|
56
57
|
export function removeSessionMeta(id) {
|
|
57
|
-
db()
|
|
58
|
+
transaction(db(), () => {
|
|
58
59
|
const now = new Date().toISOString();
|
|
59
60
|
db()
|
|
60
61
|
.prepare(`UPDATE plugin_session_launch_operations
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { db } from "./db.js";
|
|
3
|
+
import { transaction } from "./sqlite.js";
|
|
3
4
|
const TICKET_TTL_MS = 5 * 60 * 1_000;
|
|
4
5
|
const GRANT_TTL_MS = 30 * 60 * 1_000;
|
|
5
6
|
/** Issue a browser handoff into the native Session workbench. The Host knows
|
|
@@ -24,7 +25,7 @@ export function issueSessionPortalTicket(input) {
|
|
|
24
25
|
/** Atomically consume a one-use ticket and mint a fixed-lifetime browser grant. */
|
|
25
26
|
export function redeemSessionPortalTicket(ticket, now = new Date()) {
|
|
26
27
|
const connection = db();
|
|
27
|
-
return
|
|
28
|
+
return transaction(connection, () => {
|
|
28
29
|
const ticketHash = hash(ticket);
|
|
29
30
|
const row = connection.prepare(`SELECT plugin_id, scope, access, session_id, expires_at, used_at
|
|
30
31
|
FROM session_portal_tickets
|
|
@@ -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);
|
package/dist/sqlite.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type DatabaseSync, type StatementSync } from "node:sqlite";
|
|
2
|
+
type SQLiteInput = null | number | bigint | string | NodeJS.ArrayBufferView;
|
|
3
|
+
type SQLiteParameter = SQLiteInput | Record<string, SQLiteInput>;
|
|
4
|
+
type SQLiteOutput = null | number | bigint | string | Uint8Array;
|
|
5
|
+
export type SQLiteTransaction<Args extends unknown[], Result> = ((...args: Args) => Result) & {
|
|
6
|
+
deferred: (...args: Args) => Result;
|
|
7
|
+
immediate: (...args: Args) => Result;
|
|
8
|
+
exclusive: (...args: Args) => Result;
|
|
9
|
+
};
|
|
10
|
+
/** Wrap a synchronous operation in a SQLite transaction. Nested calls use savepoints. */
|
|
11
|
+
export declare function transaction<Args extends unknown[], Result>(connection: DatabaseSync, operation: (...args: Args) => Result): SQLiteTransaction<Args, Result>;
|
|
12
|
+
/** Return only the first column from a native statement, matching scalar-query intent. */
|
|
13
|
+
export declare function scalar(statement: StatementSync): {
|
|
14
|
+
get(...parameters: SQLiteParameter[]): SQLiteOutput | undefined;
|
|
15
|
+
all(...parameters: SQLiteParameter[]): SQLiteOutput[];
|
|
16
|
+
};
|
|
17
|
+
/** Match a primary SQLite result code, including extended result codes. */
|
|
18
|
+
export declare function hasSqlitePrimaryCode(error: unknown, primaryCode: number): boolean;
|
|
19
|
+
export declare const SQLITE_BUSY = 5;
|
|
20
|
+
export declare const SQLITE_LOCKED = 6;
|
|
21
|
+
export declare const SQLITE_CONSTRAINT = 19;
|
|
22
|
+
export {};
|
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
let nextSavepointId = 0;
|
|
2
|
+
/** Wrap a synchronous operation in a SQLite transaction. Nested calls use savepoints. */
|
|
3
|
+
export function transaction(connection, operation) {
|
|
4
|
+
const run = (mode, args) => {
|
|
5
|
+
if (connection.isTransaction) {
|
|
6
|
+
const savepoint = `rynx_transaction_${++nextSavepointId}`;
|
|
7
|
+
connection.exec(`SAVEPOINT ${savepoint}`);
|
|
8
|
+
try {
|
|
9
|
+
const result = operation(...args);
|
|
10
|
+
connection.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (connection.isTransaction) {
|
|
15
|
+
connection.exec(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
16
|
+
connection.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
17
|
+
}
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
connection.exec(`BEGIN ${mode}`);
|
|
22
|
+
try {
|
|
23
|
+
const result = operation(...args);
|
|
24
|
+
connection.exec("COMMIT");
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (connection.isTransaction)
|
|
29
|
+
connection.exec("ROLLBACK");
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const wrapped = ((...args) => run("DEFERRED", args));
|
|
34
|
+
wrapped.deferred = (...args) => run("DEFERRED", args);
|
|
35
|
+
wrapped.immediate = (...args) => run("IMMEDIATE", args);
|
|
36
|
+
wrapped.exclusive = (...args) => run("EXCLUSIVE", args);
|
|
37
|
+
return wrapped;
|
|
38
|
+
}
|
|
39
|
+
/** Return only the first column from a native statement, matching scalar-query intent. */
|
|
40
|
+
export function scalar(statement) {
|
|
41
|
+
statement.setReturnArrays(true);
|
|
42
|
+
const arrayStatement = statement;
|
|
43
|
+
return {
|
|
44
|
+
get: (...parameters) => arrayStatement.get(...parameters)?.[0],
|
|
45
|
+
all: (...parameters) => arrayStatement.all(...parameters).map((row) => row[0]),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Match a primary SQLite result code, including extended result codes. */
|
|
49
|
+
export function hasSqlitePrimaryCode(error, primaryCode) {
|
|
50
|
+
if (!error || typeof error !== "object" || !("errcode" in error))
|
|
51
|
+
return false;
|
|
52
|
+
const errcode = error.errcode;
|
|
53
|
+
return typeof errcode === "number" && (errcode & 0xff) === primaryCode;
|
|
54
|
+
}
|
|
55
|
+
export const SQLITE_BUSY = 5;
|
|
56
|
+
export const SQLITE_LOCKED = 6;
|
|
57
|
+
export const SQLITE_CONSTRAINT = 19;
|
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.12",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"description": "Rynx resident control-plane service and standalone lifecycle implementation.",
|
|
10
10
|
"type": "module",
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=22"
|
|
12
|
+
"node": ">=22.16"
|
|
13
13
|
},
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -42,22 +42,21 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"better-sqlite3": "^12.11.1",
|
|
46
45
|
"extract-zip": "2.0.1",
|
|
47
46
|
"pm2": "^6.0.0",
|
|
48
47
|
"tar": "^7.5.19",
|
|
49
48
|
"ws": "^8.21.0",
|
|
50
|
-
"@rynx-ai/core": "0.1.11-beta.
|
|
51
|
-
"@rynx-ai/
|
|
52
|
-
"@rynx-ai/plugin-
|
|
53
|
-
"@rynx-ai/
|
|
54
|
-
"@rynx-ai/
|
|
55
|
-
"@rynx-ai/
|
|
56
|
-
"@rynx-ai/server": "0.1.11-beta.
|
|
49
|
+
"@rynx-ai/core": "0.1.11-beta.12",
|
|
50
|
+
"@rynx-ai/plugin-runner": "0.1.11-beta.12",
|
|
51
|
+
"@rynx-ai/plugin-sdk": "0.1.11-beta.12",
|
|
52
|
+
"@rynx-ai/protocol": "0.1.11-beta.12",
|
|
53
|
+
"@rynx-ai/remote-runtime-client": "0.1.11-beta.12",
|
|
54
|
+
"@rynx-ai/emulator": "0.1.11-beta.12",
|
|
55
|
+
"@rynx-ai/server": "0.1.11-beta.12"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
58
|
"@types/ws": "^8.18.1",
|
|
60
|
-
"@rynx-ai/plugin-channel-lark": "0.1.11-beta.
|
|
59
|
+
"@rynx-ai/plugin-channel-lark": "0.1.11-beta.12"
|
|
61
60
|
},
|
|
62
61
|
"scripts": {
|
|
63
62
|
"build": "rm -rf dist bundled-plugins && tsc -p tsconfig.json && chmod +x dist/index-daemon.js && node ../../scripts/stage-bundled-plugins.mjs",
|