@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.
- package/bundled-plugins/.rynx-plugin/marketplace.json +10 -0
- package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +37 -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 +54 -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 +504 -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 +1977 -0
- package/bundled-plugins/plugins/lark/dist/runtime.d.ts +13 -0
- package/bundled-plugins/plugins/lark/dist/runtime.js +131316 -0
- package/bundled-plugins/plugins/lark/dist/settings.d.ts +7 -0
- package/bundled-plugins/plugins/lark/dist/settings.js +126958 -0
- package/bundled-plugins/plugins/lark/package.json +57 -0
- package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +1 -0
- package/bundled-plugins/plugins/lark/ui/assets/index-D9N-AkLX.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/bundled-plugins.d.ts +15 -0
- package/dist/bundled-plugins.js +134 -0
- package/dist/cli.js +26 -208
- package/dist/control-deps.js +2 -79
- package/dist/daemon-server.d.ts +4 -1
- package/dist/daemon-server.js +167 -57
- package/dist/db.js +145 -124
- package/dist/index-daemon.js +25 -4
- 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 +42 -4
- package/dist/plugin-host-rpc.js +144 -34
- package/dist/plugin-installer.d.ts +24 -55
- package/dist/plugin-installer.js +202 -415
- package/dist/plugin-management-service.d.ts +30 -23
- package/dist/plugin-management-service.js +290 -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 +140 -92
- package/dist/plugin-supervisor.d.ts +45 -125
- package/dist/plugin-supervisor.js +516 -1053
- package/dist/pm2.js +15 -10
- 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 +14 -2
- package/dist/session-launch-operations.js +34 -2
- package/dist/session-meta-store.d.ts +7 -9
- package/dist/session-meta-store.js +28 -49
- package/dist/setup.js +26 -14
- package/package.json +14 -11
- package/dist/channel-store.d.ts +0 -22
- package/dist/channel-store.js +0 -65
- 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
|
@@ -1,49 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { PluginInstallCommitInput, PluginInstallCommitResult, PluginInstallPreparation, PluginInstallPrepareInput, PluginManagementItem } from "@rynx-ai/protocol/plugin-management";
|
|
2
|
+
import type { PluginJsonValue } from "@rynx-ai/plugin-sdk";
|
|
3
3
|
import { type PreparePluginInstallationOptions, type PreparedPluginInstallation } from "./plugin-installer.js";
|
|
4
|
-
import { type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export interface PluginManagementItem {
|
|
8
|
-
id: string;
|
|
9
|
-
version?: string;
|
|
10
|
-
source: PluginSource;
|
|
11
|
-
state: PluginManagementState;
|
|
12
|
-
enabled: boolean;
|
|
13
|
-
requestedCapabilities: PluginCapability[];
|
|
14
|
-
grants: PluginCapability[];
|
|
15
|
-
updatedAt: string;
|
|
16
|
-
}
|
|
4
|
+
import { type PluginMarketplaceRecord } from "./plugin-marketplace.js";
|
|
5
|
+
import { type PluginRecord } from "./plugin-store.js";
|
|
6
|
+
import type { PluginSupervisor } from "./plugin-supervisor.js";
|
|
17
7
|
export interface PluginManagementServiceDependencies {
|
|
18
8
|
list(): PluginRecord[];
|
|
19
9
|
get(id: string): PluginRecord | undefined;
|
|
20
10
|
setEnabled(id: string, enabled: boolean): void;
|
|
21
11
|
uninstall(id: string): Promise<boolean>;
|
|
22
12
|
prepare(spec: string, onProgress: (line: string) => void, options: PreparePluginInstallationOptions): Promise<PreparedPluginInstallation>;
|
|
13
|
+
resolveMarketplace(identity: string): {
|
|
14
|
+
marketplace: {
|
|
15
|
+
marketplaceId: string;
|
|
16
|
+
};
|
|
17
|
+
pluginId: string;
|
|
18
|
+
spec: string;
|
|
19
|
+
};
|
|
20
|
+
getMarketplace(id: string): Pick<PluginMarketplaceRecord, "marketplaceId" | "sourceType" | "resolvedRevision" | "builtIn"> | undefined;
|
|
23
21
|
}
|
|
24
22
|
export interface PluginManagementServiceOptions {
|
|
25
23
|
preparationTtlMs?: number;
|
|
26
24
|
now?: () => number;
|
|
27
25
|
token?: () => string;
|
|
28
|
-
|
|
26
|
+
supervisor?: PluginSupervisor;
|
|
29
27
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Daemon-owned plugin registry mutations exposed to the authenticated local
|
|
32
|
-
* management API. Package installation remains outside this service until its
|
|
33
|
-
* capability approval is cryptographically bound to one staged artifact.
|
|
34
|
-
*/
|
|
35
28
|
export declare class PluginManagementService {
|
|
36
29
|
private readonly dependencies;
|
|
37
30
|
private readonly pending;
|
|
38
31
|
private readonly now;
|
|
39
32
|
private readonly token;
|
|
40
|
-
private readonly schedule;
|
|
41
33
|
private readonly preparationTtlMs;
|
|
34
|
+
private readonly supervisor?;
|
|
35
|
+
private readonly activeSettingsCalls;
|
|
42
36
|
constructor(dependencies?: PluginManagementServiceDependencies, options?: PluginManagementServiceOptions);
|
|
43
37
|
list(): {
|
|
44
38
|
plugins: PluginManagementItem[];
|
|
45
39
|
};
|
|
46
|
-
|
|
40
|
+
settingsUi(id: string): {
|
|
41
|
+
generation: string;
|
|
42
|
+
assetGeneration: string;
|
|
43
|
+
html: string;
|
|
44
|
+
} | undefined;
|
|
45
|
+
settingsUiAsset(id: string, assetGeneration: string, assetPath: string): {
|
|
46
|
+
contentType: string;
|
|
47
|
+
data: Buffer;
|
|
48
|
+
} | undefined;
|
|
49
|
+
callSettings(id: string, generation: string, method: string, params: PluginJsonValue | undefined, options?: {
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
}): Promise<PluginJsonValue>;
|
|
52
|
+
setEnabled(id: string, enabled: boolean): Promise<PluginManagementItem | undefined>;
|
|
47
53
|
uninstall(id: string): Promise<boolean>;
|
|
48
54
|
prepareInstallation(input: PluginInstallPrepareInput, options?: {
|
|
49
55
|
signal?: AbortSignal;
|
|
@@ -55,4 +61,5 @@ export declare class PluginManagementService {
|
|
|
55
61
|
dispose(): void;
|
|
56
62
|
private takePreparation;
|
|
57
63
|
private uniqueToken;
|
|
64
|
+
private managementItem;
|
|
58
65
|
}
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
2
|
-
import {
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
3
|
+
import { dirname, extname, relative, resolve, sep } from "node:path";
|
|
3
4
|
import { preparePluginInstallation, uninstallPlugin, } from "./plugin-installer.js";
|
|
4
|
-
import {
|
|
5
|
+
import { getPluginMarketplace, resolveMarketplacePlugin, } from "./plugin-marketplace.js";
|
|
6
|
+
import { resolvePackagePath } from "./plugin-package.js";
|
|
7
|
+
import { getPlugin, listPlugins, setPluginEnabled, } from "./plugin-store.js";
|
|
5
8
|
const defaultDependencies = {
|
|
6
9
|
list: listPlugins,
|
|
7
10
|
get: getPlugin,
|
|
8
11
|
setEnabled: setPluginEnabled,
|
|
9
12
|
uninstall: (id) => uninstallPlugin(id),
|
|
10
13
|
prepare: preparePluginInstallation,
|
|
14
|
+
resolveMarketplace: resolveMarketplacePlugin,
|
|
15
|
+
getMarketplace: getPluginMarketplace,
|
|
11
16
|
};
|
|
12
17
|
const DEFAULT_PREPARATION_TTL_MS = 5 * 60 * 1_000;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* management API. Package installation remains outside this service until its
|
|
16
|
-
* capability approval is cryptographically bound to one staged artifact.
|
|
17
|
-
*/
|
|
18
|
+
const MAX_SETTINGS_UI_DOCUMENT_BYTES = 2 * 1024 * 1024;
|
|
19
|
+
const MAX_SETTINGS_UI_ASSET_BYTES = 16 * 1024 * 1024;
|
|
18
20
|
export class PluginManagementService {
|
|
19
21
|
dependencies;
|
|
20
22
|
pending = new Map();
|
|
21
23
|
now;
|
|
22
24
|
token;
|
|
23
|
-
schedule;
|
|
24
25
|
preparationTtlMs;
|
|
26
|
+
supervisor;
|
|
27
|
+
activeSettingsCalls = new Map();
|
|
25
28
|
constructor(dependencies = defaultDependencies, options = {}) {
|
|
26
29
|
this.dependencies = dependencies;
|
|
27
30
|
this.preparationTtlMs = options.preparationTtlMs ?? DEFAULT_PREPARATION_TTL_MS;
|
|
@@ -30,62 +33,169 @@ export class PluginManagementService {
|
|
|
30
33
|
}
|
|
31
34
|
this.now = options.now ?? Date.now;
|
|
32
35
|
this.token = options.token ?? (() => randomBytes(32).toString("base64url"));
|
|
33
|
-
this.
|
|
36
|
+
this.supervisor = options.supervisor;
|
|
34
37
|
}
|
|
35
38
|
list() {
|
|
36
|
-
return { plugins: this.dependencies.list().map(
|
|
39
|
+
return { plugins: this.dependencies.list().map((record) => this.managementItem(record)) };
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
settingsUi(id) {
|
|
42
|
+
const record = this.dependencies.get(id);
|
|
43
|
+
const entry = record?.manifest.settings?.ui.entry;
|
|
44
|
+
if (!record || !entry)
|
|
45
|
+
return undefined;
|
|
46
|
+
const path = resolvePackagePath(record.installPath, entry, "settings.ui.entry", "file");
|
|
47
|
+
const info = lstatSync(path);
|
|
48
|
+
if (info.size > MAX_SETTINGS_UI_DOCUMENT_BYTES) {
|
|
49
|
+
throw new Error("plugin settings UI exceeds 2 MiB");
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
generation: record.integrity,
|
|
53
|
+
assetGeneration: settingsUiAssetGeneration(record.integrity),
|
|
54
|
+
html: readFileSync(path, "utf8"),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
settingsUiAsset(id, assetGeneration, assetPath) {
|
|
58
|
+
const record = this.dependencies.get(id);
|
|
59
|
+
const entry = record?.manifest.settings?.ui.entry;
|
|
60
|
+
if (!record || !entry)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (assetGeneration !== settingsUiAssetGeneration(record.integrity))
|
|
63
|
+
return undefined;
|
|
64
|
+
if (!isSettingsUiAssetPath(assetPath))
|
|
65
|
+
return undefined;
|
|
66
|
+
const entryPath = resolvePackagePath(record.installPath, entry, "settings.ui.entry", "file");
|
|
67
|
+
const root = realpathSync(dirname(entryPath));
|
|
68
|
+
const unresolved = resolve(root, ...assetPath.split("/"));
|
|
69
|
+
let candidate;
|
|
70
|
+
let info;
|
|
71
|
+
try {
|
|
72
|
+
info = lstatSync(unresolved);
|
|
73
|
+
if (!info.isFile() || info.isSymbolicLink())
|
|
74
|
+
return undefined;
|
|
75
|
+
candidate = realpathSync(unresolved);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const rel = relative(root, candidate);
|
|
81
|
+
if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || resolve(root, rel) !== candidate) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
if (info.size > MAX_SETTINGS_UI_ASSET_BYTES) {
|
|
85
|
+
throw new Error("plugin settings UI asset exceeds 16 MiB");
|
|
86
|
+
}
|
|
87
|
+
const contentType = settingsUiAssetContentType(candidate);
|
|
88
|
+
if (!contentType)
|
|
89
|
+
return undefined;
|
|
90
|
+
return { contentType, data: readFileSync(candidate) };
|
|
91
|
+
}
|
|
92
|
+
async callSettings(id, generation, method, params, options = {}) {
|
|
93
|
+
const record = this.dependencies.get(id);
|
|
94
|
+
if (!record)
|
|
95
|
+
throw new Error(`plugin "${id}" not found`);
|
|
96
|
+
if (record.integrity !== generation)
|
|
97
|
+
throw new Error("plugin settings UI generation is stale");
|
|
98
|
+
if (!this.supervisor)
|
|
99
|
+
throw new Error("plugin settings background is unavailable");
|
|
100
|
+
const active = this.activeSettingsCalls.get(record.canonicalId) ?? 0;
|
|
101
|
+
if (active >= 16)
|
|
102
|
+
throw new Error("plugin settings UI has too many requests in flight");
|
|
103
|
+
this.activeSettingsCalls.set(record.canonicalId, active + 1);
|
|
104
|
+
const deadline = AbortSignal.timeout(30_000);
|
|
105
|
+
const combined = options.signal
|
|
106
|
+
? AbortSignal.any([options.signal, deadline])
|
|
107
|
+
: deadline;
|
|
108
|
+
try {
|
|
109
|
+
return await this.supervisor.settingsCall(record.canonicalId, generation, method, params, { signal: combined });
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
const remaining = (this.activeSettingsCalls.get(record.canonicalId) ?? 1) - 1;
|
|
113
|
+
if (remaining <= 0)
|
|
114
|
+
this.activeSettingsCalls.delete(record.canonicalId);
|
|
115
|
+
else
|
|
116
|
+
this.activeSettingsCalls.set(record.canonicalId, remaining);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async setEnabled(id, enabled) {
|
|
40
120
|
const current = this.dependencies.get(id);
|
|
41
121
|
if (!current)
|
|
42
122
|
return undefined;
|
|
43
|
-
|
|
44
|
-
|
|
123
|
+
const mutate = () => {
|
|
124
|
+
this.dependencies.setEnabled(current.canonicalId, enabled);
|
|
125
|
+
const updated = this.dependencies.get(current.canonicalId);
|
|
126
|
+
if (!updated)
|
|
127
|
+
throw new Error(`plugin "${current.canonicalId}" disappeared`);
|
|
128
|
+
return updated;
|
|
129
|
+
};
|
|
130
|
+
if (this.supervisor) {
|
|
131
|
+
await this.supervisor.changeEnabled(current, enabled, mutate);
|
|
45
132
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!updated) {
|
|
49
|
-
throw new Error(`plugin "${id}" disappeared while updating its state`);
|
|
133
|
+
else {
|
|
134
|
+
mutate();
|
|
50
135
|
}
|
|
51
|
-
return
|
|
136
|
+
return this.managementItem(this.dependencies.get(current.canonicalId));
|
|
52
137
|
}
|
|
53
138
|
async uninstall(id) {
|
|
54
|
-
|
|
55
|
-
|
|
139
|
+
const current = this.dependencies.get(id);
|
|
140
|
+
if (!current)
|
|
141
|
+
return false;
|
|
142
|
+
if (this.dependencies.getMarketplace(current.marketplaceId)?.builtIn) {
|
|
143
|
+
throw new Error(`bundled plugin "${current.canonicalId}" cannot be uninstalled; disable it instead`);
|
|
144
|
+
}
|
|
145
|
+
if (this.supervisor) {
|
|
146
|
+
return await this.supervisor.uninstallInstallation(current.canonicalId, () => this.dependencies.uninstall(current.canonicalId));
|
|
147
|
+
}
|
|
148
|
+
return await this.dependencies.uninstall(current.canonicalId);
|
|
56
149
|
}
|
|
57
150
|
async prepareInstallation(input, options = {}) {
|
|
58
151
|
options.signal?.throwIfAborted();
|
|
59
152
|
let spec;
|
|
60
|
-
let
|
|
61
|
-
if (input.operation === "install") {
|
|
62
|
-
|
|
63
|
-
|
|
153
|
+
let prepareOptions;
|
|
154
|
+
if (input.operation === "install" && input.marketplacePlugin) {
|
|
155
|
+
const resolved = this.dependencies.resolveMarketplace(input.marketplacePlugin);
|
|
156
|
+
spec = resolved.spec;
|
|
157
|
+
prepareOptions = {
|
|
158
|
+
marketplaceId: resolved.marketplace.marketplaceId,
|
|
159
|
+
expectedId: resolved.pluginId,
|
|
160
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
else if (input.operation === "install" && input.spec) {
|
|
64
164
|
spec = input.spec;
|
|
165
|
+
prepareOptions = options.signal ? { signal: options.signal } : {};
|
|
65
166
|
}
|
|
66
|
-
else {
|
|
67
|
-
if (!input.pluginId)
|
|
68
|
-
throw new Error("plugin update id is required");
|
|
69
|
-
validatePluginId(input.pluginId);
|
|
167
|
+
else if (input.operation === "update" && input.pluginId) {
|
|
70
168
|
const existing = this.dependencies.get(input.pluginId);
|
|
71
169
|
if (!existing)
|
|
72
170
|
throw new Error(`plugin "${input.pluginId}" not found`);
|
|
73
|
-
|
|
74
|
-
|
|
171
|
+
const marketplace = this.dependencies.getMarketplace(existing.marketplaceId);
|
|
172
|
+
if (marketplace) {
|
|
173
|
+
const resolved = this.dependencies.resolveMarketplace(existing.canonicalId);
|
|
174
|
+
if (resolved.marketplace.marketplaceId !== existing.marketplaceId ||
|
|
175
|
+
resolved.pluginId !== existing.pluginId) {
|
|
176
|
+
throw new Error(`marketplace resolution changed identity for "${existing.canonicalId}"`);
|
|
177
|
+
}
|
|
178
|
+
spec = resolved.spec;
|
|
75
179
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
180
|
+
else {
|
|
181
|
+
spec = existing.spec;
|
|
182
|
+
}
|
|
183
|
+
prepareOptions = {
|
|
184
|
+
expectedId: existing.pluginId,
|
|
185
|
+
expectedIdentity: existing.canonicalId,
|
|
186
|
+
marketplaceId: existing.marketplaceId,
|
|
187
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
throw new Error("invalid plugin installation request");
|
|
192
|
+
}
|
|
193
|
+
const prepared = await this.dependencies.prepare(spec, () => { }, prepareOptions);
|
|
84
194
|
try {
|
|
85
195
|
options.signal?.throwIfAborted();
|
|
86
196
|
const token = this.uniqueToken();
|
|
87
197
|
const expiresAtMs = this.now() + this.preparationTtlMs;
|
|
88
|
-
const expiryTimer =
|
|
198
|
+
const expiryTimer = setTimeout(() => {
|
|
89
199
|
const current = this.pending.get(token);
|
|
90
200
|
if (!current || current.prepared !== prepared)
|
|
91
201
|
return;
|
|
@@ -117,23 +227,22 @@ export class PluginManagementService {
|
|
|
117
227
|
throw new Error("prepared plugin digest does not match expected digest");
|
|
118
228
|
}
|
|
119
229
|
if (prepared.existing && !input.overwrite) {
|
|
120
|
-
throw new Error(`plugin "${prepared.
|
|
230
|
+
throw new Error(`plugin "${prepared.canonicalId}" already exists; explicit overwrite confirmation is required`);
|
|
121
231
|
}
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return { plugin: toManagementItem(installed) };
|
|
232
|
+
const commit = async () => {
|
|
233
|
+
await prepared.commit({
|
|
234
|
+
expectedDigest: input.expectedDigest,
|
|
235
|
+
signal: options.signal,
|
|
236
|
+
});
|
|
237
|
+
const installed = this.dependencies.get(prepared.canonicalId);
|
|
238
|
+
if (!installed)
|
|
239
|
+
throw new Error(`plugin "${prepared.canonicalId}" was not registered`);
|
|
240
|
+
return installed;
|
|
241
|
+
};
|
|
242
|
+
const installed = this.supervisor
|
|
243
|
+
? await this.supervisor.cutoverInstallation(prepared.canonicalId, commit).then(() => this.dependencies.get(prepared.canonicalId))
|
|
244
|
+
: await commit();
|
|
245
|
+
return { plugin: this.managementItem(installed) };
|
|
137
246
|
}
|
|
138
247
|
finally {
|
|
139
248
|
prepared.discard();
|
|
@@ -170,71 +279,147 @@ export class PluginManagementService {
|
|
|
170
279
|
}
|
|
171
280
|
throw new Error("failed to allocate a plugin preparation token");
|
|
172
281
|
}
|
|
282
|
+
managementItem(record) {
|
|
283
|
+
return toManagementItem(record, Boolean(record.manifest.settings), pluginUpdateState(record, this.dependencies.getMarketplace(record.marketplaceId)));
|
|
284
|
+
}
|
|
173
285
|
}
|
|
174
|
-
function toManagementItem(record) {
|
|
175
|
-
const runnable = isRunnablePluginRecord(record);
|
|
286
|
+
function toManagementItem(record, settingsAvailable = false, updateState = "unknown") {
|
|
176
287
|
return {
|
|
177
|
-
id: record.
|
|
178
|
-
|
|
288
|
+
id: record.canonicalId,
|
|
289
|
+
pluginId: record.pluginId,
|
|
290
|
+
marketplaceId: record.marketplaceId,
|
|
291
|
+
version: record.version,
|
|
179
292
|
source: record.source,
|
|
180
|
-
state:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
grants: [...record.grants],
|
|
293
|
+
state: record.enabled ? "enabled" : "disabled",
|
|
294
|
+
updateState,
|
|
295
|
+
enabled: record.enabled,
|
|
296
|
+
runtimeState: record.manifest.runtime ? record.runtimeState : "absent",
|
|
297
|
+
...(record.runtimeError ? { runtimeError: record.runtimeError } : {}),
|
|
298
|
+
capabilities: [...record.manifest.capabilities],
|
|
299
|
+
hasSettingsUi: settingsAvailable,
|
|
188
300
|
updatedAt: record.updatedAt,
|
|
189
301
|
};
|
|
190
302
|
}
|
|
191
303
|
function toInstallPreparation(token, expiresAtMs, prepared) {
|
|
192
|
-
const requested = [...prepared.manifest.requestedCapabilities];
|
|
193
|
-
const prior = prepared.existing?.grants ?? [];
|
|
194
|
-
const requestedSet = new Set(requested);
|
|
195
|
-
const priorSet = new Set(prior);
|
|
196
304
|
return {
|
|
197
305
|
token,
|
|
198
306
|
expiresAt: new Date(expiresAtMs).toISOString(),
|
|
199
307
|
artifact: {
|
|
200
|
-
id: prepared.
|
|
201
|
-
|
|
308
|
+
id: prepared.canonicalId,
|
|
309
|
+
pluginId: prepared.pluginId,
|
|
310
|
+
marketplaceId: prepared.marketplaceId,
|
|
311
|
+
version: prepared.version,
|
|
202
312
|
source: prepared.source,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
: {}),
|
|
214
|
-
source: prepared.existing.source,
|
|
215
|
-
state: managementState(prepared.existing),
|
|
216
|
-
grants: [...prepared.existing.grants],
|
|
313
|
+
resolvedSource: prepared.requestedSource.type === "git"
|
|
314
|
+
? {
|
|
315
|
+
type: "git",
|
|
316
|
+
repository: prepared.requestedSource.url,
|
|
317
|
+
subdirectory: prepared.requestedSource.subdir ?? ".",
|
|
318
|
+
commit: prepared.resolvedRevision,
|
|
319
|
+
}
|
|
320
|
+
: {
|
|
321
|
+
type: "local",
|
|
322
|
+
subdirectory: prepared.requestedSource.subdir ?? ".",
|
|
217
323
|
},
|
|
218
|
-
|
|
219
|
-
:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
324
|
+
digest: prepared.integrity,
|
|
325
|
+
capabilities: [...prepared.manifest.capabilities],
|
|
326
|
+
hasRuntime: Boolean(prepared.manifest.runtime),
|
|
327
|
+
hasSettingsUi: Boolean(prepared.manifest.settings),
|
|
328
|
+
...(prepared.manifest.runtime ? {
|
|
329
|
+
runtimeEntry: "entry" in prepared.manifest.runtime
|
|
330
|
+
? {
|
|
331
|
+
type: "module",
|
|
332
|
+
entry: prepared.manifest.runtime.entry,
|
|
333
|
+
}
|
|
334
|
+
: {
|
|
335
|
+
type: "process",
|
|
336
|
+
command: prepared.manifest.runtime.command,
|
|
337
|
+
args: [...(prepared.manifest.runtime.args ?? [])],
|
|
338
|
+
},
|
|
339
|
+
} : {}),
|
|
340
|
+
...(prepared.manifest.settings?.background ? {
|
|
341
|
+
settingsBackgroundEntry: "entry" in prepared.manifest.settings.background
|
|
342
|
+
? {
|
|
343
|
+
type: "module",
|
|
344
|
+
entry: prepared.manifest.settings.background.entry,
|
|
345
|
+
}
|
|
346
|
+
: {
|
|
347
|
+
type: "process",
|
|
348
|
+
command: prepared.manifest.settings.background.command,
|
|
349
|
+
args: [...(prepared.manifest.settings.background.args ?? [])],
|
|
350
|
+
},
|
|
351
|
+
} : {}),
|
|
352
|
+
...(prepared.manifest.commands ? {
|
|
353
|
+
commandsEntry: prepared.manifest.commands.command,
|
|
354
|
+
} : {}),
|
|
355
|
+
...(prepared.manifest.settings ? {
|
|
356
|
+
settingsUiEntry: prepared.manifest.settings.ui.entry,
|
|
357
|
+
} : {}),
|
|
358
|
+
...(prepared.manifest.skills ? { skillDirectory: prepared.manifest.skills } : {}),
|
|
224
359
|
},
|
|
360
|
+
...(prepared.existing ? {
|
|
361
|
+
existing: {
|
|
362
|
+
id: prepared.existing.canonicalId,
|
|
363
|
+
version: prepared.existing.version,
|
|
364
|
+
source: prepared.existing.source,
|
|
365
|
+
state: prepared.existing.enabled ? "enabled" : "disabled",
|
|
366
|
+
},
|
|
367
|
+
unchanged: prepared.existing.integrity === prepared.integrity,
|
|
368
|
+
} : {}),
|
|
225
369
|
};
|
|
226
370
|
}
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
371
|
+
function pluginUpdateState(record, marketplace) {
|
|
372
|
+
if (!marketplace || marketplace.sourceType !== "git" || record.source !== "git") {
|
|
373
|
+
return "unknown";
|
|
374
|
+
}
|
|
375
|
+
return record.resolvedRevision === marketplace.resolvedRevision
|
|
376
|
+
? "current"
|
|
377
|
+
: "available";
|
|
233
378
|
}
|
|
234
379
|
function invalidPreparationError() {
|
|
235
|
-
return new Error("plugin installation preparation is invalid or
|
|
380
|
+
return new Error("plugin installation preparation is invalid, expired, or already consumed");
|
|
236
381
|
}
|
|
237
|
-
function
|
|
238
|
-
|
|
239
|
-
|
|
382
|
+
function settingsUiAssetGeneration(integrity) {
|
|
383
|
+
return createHash("sha256").update(integrity).digest("base64url");
|
|
384
|
+
}
|
|
385
|
+
function isSettingsUiAssetPath(value) {
|
|
386
|
+
if (!value ||
|
|
387
|
+
value.length > 1024 ||
|
|
388
|
+
value.includes("\\") ||
|
|
389
|
+
value.includes("\0") ||
|
|
390
|
+
value.startsWith("/")) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
return value.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
394
|
+
}
|
|
395
|
+
function settingsUiAssetContentType(path) {
|
|
396
|
+
switch (extname(path).toLowerCase()) {
|
|
397
|
+
case ".js":
|
|
398
|
+
case ".mjs":
|
|
399
|
+
return "text/javascript; charset=utf-8";
|
|
400
|
+
case ".css":
|
|
401
|
+
return "text/css; charset=utf-8";
|
|
402
|
+
case ".json":
|
|
403
|
+
case ".map":
|
|
404
|
+
return "application/json; charset=utf-8";
|
|
405
|
+
case ".png":
|
|
406
|
+
return "image/png";
|
|
407
|
+
case ".jpg":
|
|
408
|
+
case ".jpeg":
|
|
409
|
+
return "image/jpeg";
|
|
410
|
+
case ".webp":
|
|
411
|
+
return "image/webp";
|
|
412
|
+
case ".gif":
|
|
413
|
+
return "image/gif";
|
|
414
|
+
case ".ico":
|
|
415
|
+
return "image/x-icon";
|
|
416
|
+
case ".woff":
|
|
417
|
+
return "font/woff";
|
|
418
|
+
case ".woff2":
|
|
419
|
+
return "font/woff2";
|
|
420
|
+
case ".ttf":
|
|
421
|
+
return "font/ttf";
|
|
422
|
+
default:
|
|
423
|
+
return undefined;
|
|
424
|
+
}
|
|
240
425
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type PluginMarketplaceV1, type PluginPackageSourceV1 } from "@rynx-ai/plugin-sdk";
|
|
2
|
+
import { type PluginPackageMaterializer } from "./plugin-package.js";
|
|
3
|
+
export interface PluginMarketplaceRecord {
|
|
4
|
+
marketplaceId: string;
|
|
5
|
+
sourceType: "git" | "local";
|
|
6
|
+
builtIn: boolean;
|
|
7
|
+
requestedSource: PluginPackageSourceV1;
|
|
8
|
+
resolvedRevision: string;
|
|
9
|
+
sourceFingerprint: string;
|
|
10
|
+
catalog: PluginMarketplaceV1;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
refreshedAt: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function listPluginMarketplaces(): PluginMarketplaceRecord[];
|
|
15
|
+
export declare function getPluginMarketplace(id: string): PluginMarketplaceRecord | undefined;
|
|
16
|
+
export declare function addPluginMarketplace(spec: string, options?: {
|
|
17
|
+
alias?: string;
|
|
18
|
+
allowSourceRebind?: boolean;
|
|
19
|
+
builtIn?: boolean;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
materializer?: PluginPackageMaterializer;
|
|
22
|
+
onProgress?: (line: string) => void;
|
|
23
|
+
}): Promise<PluginMarketplaceRecord>;
|
|
24
|
+
export declare function refreshPluginMarketplace(marketplaceId: string, options?: {
|
|
25
|
+
signal?: AbortSignal;
|
|
26
|
+
materializer?: PluginPackageMaterializer;
|
|
27
|
+
onProgress?: (line: string) => void;
|
|
28
|
+
}): Promise<PluginMarketplaceRecord>;
|
|
29
|
+
export declare function removePluginMarketplace(marketplaceId: string): boolean;
|
|
30
|
+
export declare function isBuiltInPluginMarketplace(marketplaceId: string): boolean;
|
|
31
|
+
export declare function resolveMarketplacePlugin(identity: string): {
|
|
32
|
+
marketplace: PluginMarketplaceRecord;
|
|
33
|
+
pluginId: string;
|
|
34
|
+
spec: string;
|
|
35
|
+
};
|