@rynx-ai/daemon 0.1.10 → 0.1.11-beta.2

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.
Files changed (117) hide show
  1. package/bundled-plugins/.rynx-plugin/marketplace.json +10 -0
  2. package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +37 -0
  3. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.d.ts +48 -0
  4. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +54 -0
  5. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.d.ts +85 -0
  6. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.js +333 -0
  7. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.d.ts +109 -0
  8. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.js +347 -0
  9. package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +95 -0
  10. package/bundled-plugins/plugins/lark/dist/host-adapters.js +504 -0
  11. package/bundled-plugins/plugins/lark/dist/index.d.ts +7 -0
  12. package/bundled-plugins/plugins/lark/dist/index.js +7 -0
  13. package/bundled-plugins/plugins/lark/dist/lark-card-stream.d.ts +363 -0
  14. package/bundled-plugins/plugins/lark/dist/lark-card-stream.js +1335 -0
  15. package/bundled-plugins/plugins/lark/dist/lark-config-store.d.ts +30 -0
  16. package/bundled-plugins/plugins/lark/dist/lark-config-store.js +135 -0
  17. package/bundled-plugins/plugins/lark/dist/lark-content.d.ts +30 -0
  18. package/bundled-plugins/plugins/lark/dist/lark-content.js +138 -0
  19. package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.d.ts +65 -0
  20. package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.js +106 -0
  21. package/bundled-plugins/plugins/lark/dist/lark-diff-card.d.ts +25 -0
  22. package/bundled-plugins/plugins/lark/dist/lark-diff-card.js +58 -0
  23. package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.d.ts +16 -0
  24. package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.js +141 -0
  25. package/bundled-plugins/plugins/lark/dist/lark-resume-card.d.ts +31 -0
  26. package/bundled-plugins/plugins/lark/dist/lark-resume-card.js +105 -0
  27. package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.d.ts +18 -0
  28. package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.js +95 -0
  29. package/bundled-plugins/plugins/lark/dist/lark-sdk/client.d.ts +8 -0
  30. package/bundled-plugins/plugins/lark/dist/lark-sdk/client.js +32 -0
  31. package/bundled-plugins/plugins/lark/dist/lark-sdk/index.d.ts +1 -0
  32. package/bundled-plugins/plugins/lark/dist/lark-sdk/index.js +1 -0
  33. package/bundled-plugins/plugins/lark/dist/lark-sender.d.ts +114 -0
  34. package/bundled-plugins/plugins/lark/dist/lark-sender.js +323 -0
  35. package/bundled-plugins/plugins/lark/dist/lark-session-store.d.ts +13 -0
  36. package/bundled-plugins/plugins/lark/dist/lark-session-store.js +14 -0
  37. package/bundled-plugins/plugins/lark/dist/lark-settings-card.d.ts +55 -0
  38. package/bundled-plugins/plugins/lark/dist/lark-settings-card.js +158 -0
  39. package/bundled-plugins/plugins/lark/dist/lark-setup.d.ts +39 -0
  40. package/bundled-plugins/plugins/lark/dist/lark-setup.js +103 -0
  41. package/bundled-plugins/plugins/lark/dist/lark.d.ts +507 -0
  42. package/bundled-plugins/plugins/lark/dist/lark.js +1977 -0
  43. package/bundled-plugins/plugins/lark/dist/runtime.d.ts +13 -0
  44. package/bundled-plugins/plugins/lark/dist/runtime.js +131316 -0
  45. package/bundled-plugins/plugins/lark/dist/settings.d.ts +7 -0
  46. package/bundled-plugins/plugins/lark/dist/settings.js +126958 -0
  47. package/bundled-plugins/plugins/lark/package.json +57 -0
  48. package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +1 -0
  49. package/bundled-plugins/plugins/lark/ui/assets/index-D9N-AkLX.js +201 -0
  50. package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  51. package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  52. package/bundled-plugins/plugins/lark/ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  53. package/bundled-plugins/plugins/lark/ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  54. package/bundled-plugins/plugins/lark/ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  55. package/bundled-plugins/plugins/lark/ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  56. package/bundled-plugins/plugins/lark/ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  57. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  58. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  59. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  60. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  61. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  62. package/bundled-plugins/plugins/lark/ui/index.html +13 -0
  63. package/dist/bundled-plugins.d.ts +15 -0
  64. package/dist/bundled-plugins.js +134 -0
  65. package/dist/cli.js +26 -208
  66. package/dist/control-deps.js +2 -79
  67. package/dist/daemon-server.d.ts +4 -1
  68. package/dist/daemon-server.js +167 -57
  69. package/dist/db.js +145 -124
  70. package/dist/index-daemon.js +25 -4
  71. package/dist/migrations/cleanup-legacy-sessions.d.ts +1 -1
  72. package/dist/migrations/cleanup-legacy-sessions.js +33 -15
  73. package/dist/official-plugins.d.ts +2 -0
  74. package/dist/official-plugins.js +2 -0
  75. package/dist/plugin-cli.d.ts +1 -3
  76. package/dist/plugin-cli.js +56 -45
  77. package/dist/plugin-host-leases.d.ts +4 -0
  78. package/dist/plugin-host-leases.js +7 -0
  79. package/dist/plugin-host-rpc.d.ts +42 -4
  80. package/dist/plugin-host-rpc.js +144 -34
  81. package/dist/plugin-installer.d.ts +24 -55
  82. package/dist/plugin-installer.js +202 -415
  83. package/dist/plugin-management-service.d.ts +30 -23
  84. package/dist/plugin-management-service.js +290 -105
  85. package/dist/plugin-marketplace.d.ts +35 -0
  86. package/dist/plugin-marketplace.js +299 -0
  87. package/dist/plugin-package.d.ts +22 -24
  88. package/dist/plugin-package.js +520 -571
  89. package/dist/plugin-skill-context.d.ts +4 -0
  90. package/dist/plugin-skill-context.js +35 -0
  91. package/dist/plugin-store.d.ts +36 -37
  92. package/dist/plugin-store.js +140 -92
  93. package/dist/plugin-supervisor.d.ts +45 -125
  94. package/dist/plugin-supervisor.js +516 -1053
  95. package/dist/pm2.js +15 -10
  96. package/dist/runtime-project-store.d.ts +9 -0
  97. package/dist/runtime-project-store.js +83 -0
  98. package/dist/session-fallback-workspace.d.ts +22 -0
  99. package/dist/session-fallback-workspace.js +120 -0
  100. package/dist/session-fork-store.d.ts +41 -0
  101. package/dist/session-fork-store.js +288 -0
  102. package/dist/session-launch-operations.d.ts +14 -2
  103. package/dist/session-launch-operations.js +34 -2
  104. package/dist/session-meta-store.d.ts +7 -9
  105. package/dist/session-meta-store.js +28 -49
  106. package/dist/setup.js +26 -14
  107. package/package.json +14 -11
  108. package/dist/channel-store.d.ts +0 -22
  109. package/dist/channel-store.js +0 -65
  110. package/dist/instance-store.d.ts +0 -38
  111. package/dist/instance-store.js +0 -85
  112. package/dist/plugin-install-restart.d.ts +0 -13
  113. package/dist/plugin-install-restart.js +0 -24
  114. package/dist/plugin-runtime-control.d.ts +0 -2
  115. package/dist/plugin-runtime-control.js +0 -37
  116. package/dist/registry.d.ts +0 -10
  117. package/dist/registry.js +0 -52
@@ -0,0 +1,4 @@
1
+ import { type ResolvedPluginSkillSourceSnapshot } from "@rynx-ai/core";
2
+ /** Capture enabled plugin skill roots for persistence in a Session snapshot. */
3
+ export declare function snapshotEnabledPluginSkillSources(): ResolvedPluginSkillSourceSnapshot[];
4
+ export declare function removeLegacyPluginSkillSnapshot(sessionId: string): void;
@@ -0,0 +1,35 @@
1
+ import { createHash } from "node:crypto";
2
+ import { rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { rynxHome } from "@rynx-ai/core";
5
+ import { resolvePackagePath } from "./plugin-package.js";
6
+ import { listEnabledPlugins } from "./plugin-store.js";
7
+ /** Capture enabled plugin skill roots for persistence in a Session snapshot. */
8
+ export function snapshotEnabledPluginSkillSources() {
9
+ return listEnabledPlugins().flatMap((record) => {
10
+ if (!record.manifest.skills)
11
+ return [];
12
+ return [{
13
+ installationId: record.canonicalId,
14
+ integrity: record.integrity,
15
+ packageRoot: record.installPath,
16
+ root: resolvePackagePath(record.installPath, record.manifest.skills, "skills", "directory"),
17
+ namespace: pluginSkillNamespace(record.canonicalId),
18
+ }];
19
+ });
20
+ }
21
+ export function removeLegacyPluginSkillSnapshot(sessionId) {
22
+ const digest = createHash("sha256").update(sessionId).digest("hex");
23
+ rmSync(join(rynxHome(), "plugins", "task-skill-snapshots", `${digest}.json`), {
24
+ force: true,
25
+ });
26
+ }
27
+ function pluginSkillNamespace(canonicalId) {
28
+ const slug = canonicalId
29
+ .toLowerCase()
30
+ .replace(/[^a-z0-9-]+/g, "-")
31
+ .replace(/^-+|-+$/g, "")
32
+ .slice(0, 96);
33
+ const digest = createHash("sha256").update(canonicalId).digest("hex").slice(0, 12);
34
+ return `plugin-${slug || "package"}-${digest}`;
35
+ }
@@ -1,56 +1,55 @@
1
- /**
2
- * Durable inventory of installed plugin packages.
3
- *
4
- * The registry stores only static, validated manifest data and a host-owned
5
- * installation path. Runtime modules are never imported while reading this
6
- * table. Rows from the pre-v1 executable-manifest format remain visible to the
7
- * CLI, but are not runnable and must be reinstalled.
8
- */
9
- import { type PluginCapability, type PluginManifestV1 } from "@rynx-ai/plugin-sdk";
10
- export type PluginSource = "npm" | "local" | "git" | "url";
1
+ /** Durable source-qualified inventory of installed plugin packages. */
2
+ import { type PluginManifestV1, type PluginPackageSourceV1 } from "@rynx-ai/plugin-sdk";
3
+ export type PluginSource = "local" | "git";
4
+ export type PluginRuntimeState = "absent" | "starting" | "running" | "degraded" | "failed" | "stopped";
11
5
  export interface PluginRecord {
12
- /** Manifest id. The SQLite column remains `name` for migration stability. */
6
+ /** Canonical installation identity: `<plugin-id>@<marketplace-id>`. */
7
+ canonicalId: string;
8
+ /** Canonical display/CLI value; always equals canonicalId. */
13
9
  name: string;
14
- /** User-supplied install source, retained for an explicit update. */
15
- spec: string;
10
+ marketplaceId: string;
11
+ pluginId: string;
16
12
  source: PluginSource;
17
- version?: string;
18
- /** Whether daemon runtime contributions are loaded; explicit CLI stays available. */
13
+ requestedSource: PluginPackageSourceV1;
14
+ resolvedRevision: string;
15
+ sourceFingerprint: string;
16
+ /** Stable serialized source used for explicit updates. */
17
+ spec: string;
18
+ version: string;
19
19
  enabled: boolean;
20
20
  installedAt: string;
21
21
  updatedAt: string;
22
- /** Host-generated immutable installation root. Missing only on legacy rows. */
23
- installPath?: string;
24
- /** Validated, pure-data manifest. Missing only on legacy rows. */
25
- manifest?: PluginManifestV1;
26
- /** sha256 integrity string for the materialized package. */
27
- integrity?: string;
28
- /** Capabilities approved for this installation. */
29
- grants: PluginCapability[];
30
- }
31
- export interface RunnablePluginRecord extends PluginRecord {
32
22
  installPath: string;
33
23
  manifest: PluginManifestV1;
34
24
  integrity: string;
25
+ runtimeState: PluginRuntimeState;
26
+ runtimeError?: string;
35
27
  }
36
- /** All installed plugins, including incompatible legacy rows. */
28
+ export type RunnablePluginRecord = PluginRecord;
29
+ export declare function canonicalPluginId(pluginId: string, marketplaceId: string): string;
30
+ export declare function parseCanonicalPluginId(value: string): {
31
+ pluginId: string;
32
+ marketplaceId: string;
33
+ };
37
34
  export declare function listPlugins(): PluginRecord[];
38
- /** Enabled v1 installations that are safe for the runner to consider. */
39
- export declare function listEnabledPlugins(): RunnablePluginRecord[];
40
- export declare function getPlugin(name: string): PluginRecord | undefined;
41
- /** Atomically point a plugin id at a completely materialized installation. */
35
+ export declare function listEnabledPlugins(): PluginRecord[];
36
+ /** Exact canonical lookup. Unqualified ids are accepted only when unambiguous. */
37
+ export declare function getPlugin(identity: string): PluginRecord | undefined;
42
38
  export declare function upsertPlugin(rec: {
43
- name: string;
44
- spec: string;
39
+ canonicalId: string;
40
+ marketplaceId: string;
41
+ pluginId: string;
45
42
  source: PluginSource;
46
- version?: string;
43
+ requestedSource: PluginPackageSourceV1;
44
+ resolvedRevision: string;
45
+ sourceFingerprint: string;
46
+ version: string;
47
47
  installPath: string;
48
48
  manifest: PluginManifestV1;
49
49
  integrity: string;
50
- grants: readonly PluginCapability[];
51
50
  enabled?: boolean;
52
51
  }): void;
53
- export declare function setPluginEnabled(name: string, enabled: boolean): void;
54
- /** Remove only the registry row. Package deletion is owned by the installer. */
55
- export declare function removePlugin(name: string): boolean;
52
+ export declare function setPluginEnabled(identity: string, enabled: boolean): void;
53
+ export declare function setPluginRuntimeState(identity: string, state: PluginRuntimeState, error?: string): void;
54
+ export declare function removePlugin(identity: string): boolean;
56
55
  export declare function isRunnablePluginRecord(record: PluginRecord): record is RunnablePluginRecord;
@@ -1,121 +1,169 @@
1
- /**
2
- * Durable inventory of installed plugin packages.
3
- *
4
- * The registry stores only static, validated manifest data and a host-owned
5
- * installation path. Runtime modules are never imported while reading this
6
- * table. Rows from the pre-v1 executable-manifest format remain visible to the
7
- * CLI, but are not runnable and must be reinstalled.
8
- */
9
- import { parsePluginManifest, } from "@rynx-ai/plugin-sdk";
1
+ /** Durable source-qualified inventory of installed plugin packages. */
2
+ import { MARKETPLACE_ID_PATTERN, PLUGIN_ID_PATTERN, parsePluginManifest, } from "@rynx-ai/plugin-sdk";
10
3
  import { db } from "./db.js";
11
- function toRecord(row) {
12
- const manifest = parseStoredManifest(row.manifest);
13
- return {
14
- name: row.name,
15
- spec: row.spec,
16
- source: parseSource(row.source),
17
- version: row.version ?? undefined,
18
- enabled: row.enabled !== 0,
19
- installedAt: row.installed_at,
20
- updatedAt: row.updated_at ?? row.installed_at,
21
- installPath: row.install_path ?? undefined,
22
- manifest,
23
- integrity: row.integrity ?? undefined,
24
- grants: parseStoredGrants(row.grants, manifest),
25
- };
4
+ export function canonicalPluginId(pluginId, marketplaceId) {
5
+ if (!PLUGIN_ID_PATTERN.test(pluginId))
6
+ throw new Error(`invalid plugin id "${pluginId}"`);
7
+ if (!MARKETPLACE_ID_PATTERN.test(marketplaceId)) {
8
+ throw new Error(`invalid marketplace id "${marketplaceId}"`);
9
+ }
10
+ return `${pluginId}@${marketplaceId}`;
11
+ }
12
+ export function parseCanonicalPluginId(value) {
13
+ const separator = value.lastIndexOf("@");
14
+ if (separator <= 0 || separator === value.length - 1) {
15
+ throw new Error("plugin identity must be <plugin-id>@<marketplace-id>");
16
+ }
17
+ const pluginId = value.slice(0, separator);
18
+ const marketplaceId = value.slice(separator + 1);
19
+ canonicalPluginId(pluginId, marketplaceId);
20
+ return { pluginId, marketplaceId };
26
21
  }
27
- /** All installed plugins, including incompatible legacy rows. */
28
22
  export function listPlugins() {
29
- const rows = db().prepare("SELECT * FROM plugins ORDER BY name").all();
23
+ const rows = db()
24
+ .prepare("SELECT * FROM plugin_installations ORDER BY marketplace_id, plugin_id")
25
+ .all();
30
26
  return rows.map(toRecord);
31
27
  }
32
- /** Enabled v1 installations that are safe for the runner to consider. */
33
28
  export function listEnabledPlugins() {
34
- return listPlugins().filter(isRunnablePluginRecord).filter((plugin) => plugin.enabled);
29
+ return listPlugins().filter((plugin) => plugin.enabled);
35
30
  }
36
- export function getPlugin(name) {
37
- const row = db().prepare("SELECT * FROM plugins WHERE name = ?").get(name);
38
- return row ? toRecord(row) : undefined;
31
+ /** Exact canonical lookup. Unqualified ids are accepted only when unambiguous. */
32
+ export function getPlugin(identity) {
33
+ if (identity.includes("@")) {
34
+ const row = db()
35
+ .prepare("SELECT * FROM plugin_installations WHERE canonical_id = ?")
36
+ .get(identity);
37
+ return row ? toRecord(row) : undefined;
38
+ }
39
+ if (!PLUGIN_ID_PATTERN.test(identity))
40
+ return undefined;
41
+ const rows = db()
42
+ .prepare("SELECT * FROM plugin_installations WHERE plugin_id = ? ORDER BY marketplace_id")
43
+ .all(identity);
44
+ if (rows.length > 1) {
45
+ throw new Error(`plugin id "${identity}" is ambiguous; use <plugin-id>@<marketplace-id>`);
46
+ }
47
+ return rows[0] ? toRecord(rows[0]) : undefined;
39
48
  }
40
- /** Atomically point a plugin id at a completely materialized installation. */
41
49
  export function upsertPlugin(rec) {
42
- if (rec.name !== rec.manifest.id) {
43
- throw new Error(`plugin registry id mismatch: ${rec.name} != ${rec.manifest.id}`);
50
+ if (rec.pluginId !== rec.manifest.id) {
51
+ throw new Error(`plugin registry id mismatch: ${rec.pluginId} != ${rec.manifest.id}`);
44
52
  }
45
- const requested = new Set(rec.manifest.requestedCapabilities);
46
- for (const grant of rec.grants) {
47
- if (!requested.has(grant))
48
- throw new Error(`plugin grant was not requested: ${grant}`);
53
+ if (rec.canonicalId !== canonicalPluginId(rec.pluginId, rec.marketplaceId)) {
54
+ throw new Error("plugin canonical identity mismatch");
49
55
  }
50
56
  const now = new Date().toISOString();
51
- db()
52
- .prepare(`INSERT INTO plugins (
53
- name, spec, source, version, enabled, installed_at, updated_at,
54
- install_path, manifest, integrity, grants
55
- ) VALUES (
56
- @name, @spec, @source, @version, @enabled, @now, @now,
57
- @installPath, @manifest, @integrity, @grants
58
- )
59
- ON CONFLICT(name) DO UPDATE SET
60
- spec = excluded.spec,
61
- source = excluded.source,
62
- version = excluded.version,
63
- enabled = excluded.enabled,
64
- installed_at = excluded.installed_at,
65
- updated_at = excluded.updated_at,
66
- install_path = excluded.install_path,
67
- manifest = excluded.manifest,
68
- integrity = excluded.integrity,
69
- grants = excluded.grants`)
70
- .run({
71
- name: rec.name,
72
- spec: rec.spec,
57
+ db().prepare(`INSERT INTO plugin_installations (
58
+ canonical_id, marketplace_id, plugin_id, source_type, requested_source,
59
+ resolved_revision, source_fingerprint, version, enabled, installed_at,
60
+ updated_at, install_path, manifest, integrity, capabilities, runtime_state, runtime_error
61
+ ) VALUES (
62
+ @canonicalId, @marketplaceId, @pluginId, @source, @requestedSource,
63
+ @resolvedRevision, @sourceFingerprint, @version, @enabled, @now,
64
+ @now, @installPath, @manifest, @integrity, @capabilities, 'stopped', NULL
65
+ )
66
+ ON CONFLICT(canonical_id) DO UPDATE SET
67
+ source_type = excluded.source_type,
68
+ requested_source = excluded.requested_source,
69
+ resolved_revision = excluded.resolved_revision,
70
+ source_fingerprint = excluded.source_fingerprint,
71
+ version = excluded.version,
72
+ enabled = excluded.enabled,
73
+ updated_at = excluded.updated_at,
74
+ install_path = excluded.install_path,
75
+ manifest = excluded.manifest,
76
+ integrity = excluded.integrity,
77
+ capabilities = excluded.capabilities,
78
+ runtime_state = 'stopped',
79
+ runtime_error = NULL`).run({
80
+ canonicalId: rec.canonicalId,
81
+ marketplaceId: rec.marketplaceId,
82
+ pluginId: rec.pluginId,
73
83
  source: rec.source,
74
- version: rec.version ?? null,
84
+ requestedSource: JSON.stringify(rec.requestedSource),
85
+ resolvedRevision: rec.resolvedRevision,
86
+ sourceFingerprint: rec.sourceFingerprint,
87
+ version: rec.version,
75
88
  enabled: rec.enabled === false ? 0 : 1,
76
89
  now,
77
90
  installPath: rec.installPath,
78
91
  manifest: JSON.stringify(rec.manifest),
79
92
  integrity: rec.integrity,
80
- grants: JSON.stringify([...rec.grants]),
93
+ capabilities: JSON.stringify(rec.manifest.capabilities),
81
94
  });
82
95
  }
83
- export function setPluginEnabled(name, enabled) {
84
- db()
85
- .prepare("UPDATE plugins SET enabled = ?, updated_at = ? WHERE name = ?")
86
- .run(enabled ? 1 : 0, new Date().toISOString(), name);
96
+ export function setPluginEnabled(identity, enabled) {
97
+ const record = getPlugin(identity);
98
+ if (!record)
99
+ return;
100
+ db().prepare("UPDATE plugin_installations SET enabled = ?, updated_at = ? WHERE canonical_id = ?").run(enabled ? 1 : 0, new Date().toISOString(), record.canonicalId);
87
101
  }
88
- /** Remove only the registry row. Package deletion is owned by the installer. */
89
- export function removePlugin(name) {
90
- return db().prepare("DELETE FROM plugins WHERE name = ?").run(name).changes > 0;
102
+ export function setPluginRuntimeState(identity, state, error) {
103
+ const record = getPlugin(identity);
104
+ if (!record)
105
+ return;
106
+ db().prepare(`UPDATE plugin_installations
107
+ SET runtime_state = ?, runtime_error = ?
108
+ WHERE canonical_id = ?`).run(state, error ?? null, record.canonicalId);
109
+ }
110
+ export function removePlugin(identity) {
111
+ const record = getPlugin(identity);
112
+ if (!record)
113
+ return false;
114
+ return db()
115
+ .prepare("DELETE FROM plugin_installations WHERE canonical_id = ?")
116
+ .run(record.canonicalId).changes > 0;
91
117
  }
92
118
  export function isRunnablePluginRecord(record) {
93
119
  return Boolean(record.installPath && record.manifest && record.integrity);
94
120
  }
95
- function parseStoredManifest(raw) {
96
- if (!raw)
97
- return undefined;
98
- try {
99
- return parsePluginManifest(JSON.parse(raw));
100
- }
101
- catch {
102
- return undefined;
103
- }
121
+ function toRecord(row) {
122
+ const manifest = parsePluginManifest(JSON.parse(row.manifest));
123
+ const requestedSource = JSON.parse(row.requested_source);
124
+ return {
125
+ canonicalId: row.canonical_id,
126
+ name: row.canonical_id,
127
+ marketplaceId: row.marketplace_id,
128
+ pluginId: row.plugin_id,
129
+ source: row.source_type === "local" ? "local" : "git",
130
+ requestedSource,
131
+ resolvedRevision: row.resolved_revision,
132
+ sourceFingerprint: row.source_fingerprint,
133
+ spec: serializeRequestedSource(requestedSource),
134
+ version: row.version,
135
+ enabled: row.enabled !== 0,
136
+ installedAt: row.installed_at,
137
+ updatedAt: row.updated_at,
138
+ installPath: row.install_path,
139
+ manifest,
140
+ integrity: row.integrity,
141
+ runtimeState: parseRuntimeState(row.runtime_state),
142
+ ...(row.runtime_error ? { runtimeError: row.runtime_error } : {}),
143
+ };
104
144
  }
105
- function parseStoredGrants(raw, manifest) {
106
- if (!raw || !manifest)
107
- return [];
108
- try {
109
- const values = JSON.parse(raw);
110
- if (!Array.isArray(values))
111
- return [];
112
- const requested = new Set(manifest.requestedCapabilities);
113
- return values.filter((value) => typeof value === "string" && requested.has(value));
114
- }
115
- catch {
116
- return [];
145
+ function serializeRequestedSource(source) {
146
+ if (source.type === "local") {
147
+ return `${source.path}${source.subdir ? `#path=${source.subdir}` : ""}`;
117
148
  }
149
+ const params = new URLSearchParams();
150
+ if (source.subdir)
151
+ params.set("path", source.subdir);
152
+ if (source.ref)
153
+ params.set("ref", source.ref);
154
+ if (source.commit)
155
+ params.set("ref", source.commit);
156
+ const fragment = params.toString();
157
+ return `${source.url}${fragment ? `#${fragment}` : ""}`;
118
158
  }
119
- function parseSource(value) {
120
- return value === "local" || value === "git" || value === "url" ? value : "npm";
159
+ function parseRuntimeState(value) {
160
+ if (value === "absent" ||
161
+ value === "starting" ||
162
+ value === "running" ||
163
+ value === "degraded" ||
164
+ value === "failed")
165
+ return value;
166
+ if (value === "needs_setup")
167
+ return "running";
168
+ return "stopped";
121
169
  }
@@ -1,35 +1,8 @@
1
- import { type ChannelTypeDescriptor } from "@rynx-ai/core";
2
- import { type SpawnPluginRunnerOptions } from "@rynx-ai/plugin-runner";
3
- import { type PluginAuthMaterial, type PluginContributionStatus, type PluginContributionTarget, type PluginJsonValue, type PluginRunnerHandshake, type PluginRuntimeStatus, type PluginStartContributionRequest } from "@rynx-ai/plugin-sdk";
1
+ import { type PluginHealth, type PluginJsonValue } from "@rynx-ai/plugin-sdk";
2
+ import { type SpawnedPluginRunner, type SpawnPluginRunnerOptions } from "@rynx-ai/plugin-runner";
4
3
  import { type PluginHostServices } from "./plugin-host-rpc.js";
5
- import type { RunnablePluginRecord } from "./plugin-store.js";
6
- export interface PluginRunnerClientHandle {
7
- startContribution(request: PluginStartContributionRequest): Promise<void>;
8
- stopContribution(target: PluginContributionTarget): Promise<void>;
9
- status(target: PluginContributionTarget): Promise<PluginContributionStatus>;
10
- runtimeStatus(): Promise<PluginRuntimeStatus>;
11
- authorize(request: {
12
- kind: "chat-channel";
13
- contributionId: string;
14
- instanceId?: string;
15
- config: Readonly<Record<string, PluginJsonValue>>;
16
- }, onMaterial: (material: PluginAuthMaterial) => void, options?: {
17
- signal?: AbortSignal;
18
- timeoutMs?: number;
19
- }): Promise<Record<string, PluginJsonValue>>;
20
- emitHostEvent(event: string, payload?: unknown): Promise<void>;
21
- }
22
- /** The subset of a spawned runner the supervisor owns. Kept structural for tests. */
23
- export interface SupervisedPluginRunner {
24
- readonly client: PluginRunnerClientHandle;
25
- readonly handshake: PluginRunnerHandshake;
26
- readonly completion: Promise<{
27
- code: number | null;
28
- signal: NodeJS.Signals | null;
29
- }>;
30
- close(): Promise<void>;
31
- }
32
- export type PluginRunnerSpawnFactory = (options: SpawnPluginRunnerOptions) => Promise<SupervisedPluginRunner>;
4
+ import { type PluginRecord, type PluginRuntimeState, type RunnablePluginRecord } from "./plugin-store.js";
5
+ export type PluginRunnerSpawnFactory = (options: SpawnPluginRunnerOptions) => Promise<SpawnedPluginRunner>;
33
6
  export interface PluginSupervisorLogEntry {
34
7
  level: "info" | "warn" | "error";
35
8
  event: string;
@@ -40,121 +13,68 @@ export interface PluginSupervisorLogEntry {
40
13
  signal?: NodeJS.Signals | null;
41
14
  }
42
15
  export interface PluginSupervisorOptions {
43
- /**
44
- * Trusted daemon-owned services. May be bound later because the composition
45
- * root needs the static channel catalog before startServer constructs them.
46
- */
47
16
  services?: PluginHostServices;
48
17
  spawnRunner?: PluginRunnerSpawnFactory;
49
18
  log?: (entry: PluginSupervisorLogEntry) => void;
50
- /** Test knobs; production restart delay is always capped at 30 seconds. */
51
19
  restartBaseMs?: number;
52
20
  restartMaxMs?: number;
53
- stableResetMs?: number;
54
- /** Test override. Production generates one identity per supervisor process lifetime. */
21
+ healthPollMs?: number;
55
22
  daemonInstanceId?: string;
56
- }
57
- export interface PluginContributionRuntimeSnapshot {
58
- key: string;
59
- target: PluginContributionTarget;
60
- desired: boolean;
61
- generation?: number;
62
- failureCount: number;
63
- status: PluginContributionStatus;
64
- }
65
- export interface PluginRunnerRuntimeSnapshot {
66
- state: "stopped" | "starting" | "running" | "backoff" | "failed";
67
- generation: number;
68
- failureCount: number;
69
- lastError?: string;
23
+ verifyPackage?: (record: RunnablePluginRecord) => Promise<void>;
70
24
  }
71
25
  export interface PluginRuntimeStatusSnapshot {
72
- capturedAt: string;
73
26
  plugins: Array<{
74
27
  pluginId: string;
75
- runner: PluginRunnerRuntimeSnapshot;
76
- runtimeStatus: PluginRuntimeStatus;
77
- contributions: PluginContributionRuntimeSnapshot[];
28
+ generation: number;
29
+ state: PluginRuntimeState;
30
+ health?: PluginHealth;
31
+ error?: string;
32
+ settings: boolean;
78
33
  }>;
79
34
  }
80
35
  /**
81
- * Owns isolated plugin child processes and projects their static chat-channel
82
- * declarations onto trusted host-side ChannelManager handles.
83
- *
84
- * There is at most one resident child per enabled plugin. Chat-channel
85
- * instances are independent desired-state entries inside that child, so
86
- * stopping the final channel does not tear down the plugin runtime itself.
36
+ * Supervises exactly one language-neutral runtime process per enabled plugin.
37
+ * It has no channel-instance API and never interprets plugin-owned state.
87
38
  */
88
39
  export declare class PluginSupervisor {
89
40
  private readonly states;
90
- private services?;
41
+ private readonly queues;
91
42
  private readonly spawnRunner;
92
- private readonly logger?;
43
+ private readonly log;
44
+ private readonly daemonInstanceId;
93
45
  private readonly restartBaseMs;
94
46
  private readonly restartMaxMs;
95
- private readonly stableResetMs;
96
- private readonly daemonInstanceId;
97
- private readonly runtimeOperations;
47
+ private readonly healthPollMs;
48
+ private readonly verifyPackage;
49
+ private services?;
98
50
  private disposed;
99
- constructor(plugins: readonly RunnablePluginRecord[], options: PluginSupervisorOptions);
100
- /** Static channel catalog. Calling this never loads or executes plugin code. */
101
- createChannelTypeDescriptors(): ChannelTypeDescriptor[];
102
- /** Bind the trusted host once, after the server composition root constructs it. */
103
- bindHostServices(services: PluginHostServices): this;
104
- /** Cached, non-blocking runner/contribution health for the generic control API. */
105
- statusSnapshot(): PluginRuntimeStatusSnapshot;
106
- /** Start one resident runner for every enabled runtime plugin. */
51
+ constructor(records: readonly RunnablePluginRecord[], options?: PluginSupervisorOptions);
52
+ bindHostServices(services: PluginHostServices): void;
107
53
  startPluginRuntimes(): Promise<void>;
108
- /**
109
- * Atomically replace or add one plugin runtime, preserving its live chat
110
- * desired state. Concurrent reloads of the same plugin are applied in call
111
- * order; other plugins continue running independently.
112
- */
113
- reloadPluginRuntime(record: RunnablePluginRecord, { readyTimeoutMs }?: {
114
- readyTimeoutMs?: number;
115
- }): Promise<PluginRuntimeStatus>;
116
- /**
117
- * Make one enabled plugin runtime ready without replacing an already healthy
118
- * runner. This is the idempotent operation plugin CLIs use when their durable
119
- * configuration has not changed.
120
- */
121
- ensurePluginRuntime(record: RunnablePluginRecord, { readyTimeoutMs }?: {
122
- readyTimeoutMs?: number;
123
- }): Promise<PluginRuntimeStatus>;
124
- private enqueueRuntimeOperation;
125
- private performPluginRuntimeEnsure;
126
- private performPluginRuntimeReload;
127
- /** Permanently stop all children and cancel every pending automatic restart. */
54
+ ensurePluginRuntime(record: RunnablePluginRecord): Promise<PluginRuntimeState>;
55
+ /** Strict cutover: stop current generation, then start the supplied generation. */
56
+ reloadPluginRuntime(record: RunnablePluginRecord): Promise<PluginRuntimeState>;
57
+ stopPluginRuntime(identity: string): Promise<void>;
58
+ changeEnabled(record: PluginRecord, enabled: boolean, mutateRegistry: () => PluginRecord): Promise<PluginRuntimeState>;
59
+ cutoverInstallation(identity: string, commitRegistry: () => Promise<PluginRecord>): Promise<PluginRuntimeState>;
60
+ uninstallInstallation(identity: string, removeRegistry: () => Promise<boolean>): Promise<boolean>;
61
+ settingsCall(identity: string, expectedGeneration: string, method: string, params?: PluginJsonValue, options?: {
62
+ signal?: AbortSignal;
63
+ }): Promise<PluginJsonValue>;
64
+ supportsSettings(identity: string): boolean;
65
+ statusSnapshot(): PluginRuntimeStatusSnapshot;
128
66
  dispose(): Promise<void>;
129
- private channelDescriptor;
130
- private createRemoteChannel;
131
- private authorize;
132
- private rememberDesired;
133
- private ensureDesiredStarted;
134
- private startDesiredOnRunner;
135
- private stopDesired;
136
- private refreshContributionStatus;
137
- private refreshRuntimeStatus;
138
- private waitForRuntimeReady;
139
- private runtimeReadyTimeoutError;
140
- private ensureRunner;
141
- private spawnAndBind;
142
- private trackRunnerExit;
143
- private runnerExited;
144
- private joinExitTransition;
67
+ private upsertState;
68
+ private startState;
69
+ private startSettingsState;
70
+ private stopSettingsState;
71
+ private handleSettingsExit;
72
+ private stopState;
73
+ private handleExit;
74
+ private failState;
145
75
  private scheduleRestart;
146
- private restart;
147
- private restoreDesiredContributions;
148
- private closeRunner;
149
- private disposeState;
150
- private cancelRestart;
151
- private markRunnerUnavailable;
152
- private markContributionFailed;
153
- private stderrSink;
154
- private writeLog;
155
- private assertOpen;
156
- private assertReady;
157
- private requireHostServices;
76
+ private scheduleHealthPoll;
77
+ private refreshHealth;
78
+ private failRunningState;
79
+ private runSerialized;
158
80
  }
159
- /** Convenience export for composition roots that only need the static catalog. */
160
- export declare function createChannelTypeDescriptors(supervisor: PluginSupervisor): ChannelTypeDescriptor[];