@pasko70/pibo 3.0.0 → 3.0.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/README.md +20 -6
- package/dist/agent-runtime/context-build.js +26 -2
- package/dist/agent-runtime/routed-session.js +108 -9
- package/dist/agent-runtimes/codex-native/adapter.js +12 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
- package/dist/agent-runtimes/omp/adapter.js +3 -7
- package/dist/agent-runtimes/pi/adapter.js +5 -3
- package/dist/agent-runtimes/pi/routed-session.js +49 -8
- package/dist/agent-runtimes/pi/runtime.js +6 -0
- package/dist/apps/chat/agent-profiles.js +31 -2
- package/dist/apps/chat/agent-store.js +43 -3
- package/dist/apps/chat/chat-request-normalizers.js +31 -0
- package/dist/apps/chat/data/chat-data-mappers.js +61 -11
- package/dist/apps/chat/data/session-query-service.js +33 -10
- package/dist/apps/chat/data/timeline-query-service.js +60 -35
- package/dist/apps/chat/output-compactor.js +144 -21
- package/dist/apps/chat/output-event-policy.js +179 -2
- package/dist/apps/chat/stream.js +16 -7
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +236 -64
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-B6GZgWSj.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-BPotHecb.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-BzqQKeVO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CvaPTBTN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-paagejNj.js} +1 -1
- package/dist/apps/chat-ui/assets/index-6JJV-eic.js +228 -0
- package/dist/apps/chat-ui/assets/index-CeL9JPP5.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/{index-Dtw2Z7jz.js → index-U-MSErGa.js} +4 -4
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
- package/dist/cli-session/localSessionSource.js +153 -161
- package/dist/cli.js +7 -11
- package/dist/core/context-build.js +7 -16
- package/dist/core/output-persistence-retry.js +484 -0
- package/dist/core/output-render-sequence.js +331 -0
- package/dist/core/profiles.js +24 -0
- package/dist/core/provider-recovery.js +7 -1
- package/dist/core/session-model.js +37 -1
- package/dist/core/session-router.js +67 -20
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/schema.js +122 -2
- package/dist/data/session-store.js +3 -3
- package/dist/debug/index.js +44 -2
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- package/dist/plugins/context-files-store.js +30 -0
- package/dist/plugins/context-files.js +52 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/user-profile-resources.js +22 -0
- package/dist/reliability/store.js +246 -37
- package/dist/session-ui/terminalRows.js +6 -3
- package/dist/sessions/pibo-data-store.js +145 -0
- package/dist/sessions/store.js +42 -0
- package/dist/setup/cli.js +422 -6
- package/dist/setup/installation-profiles.js +464 -0
- package/dist/shared/deterministic-digest.js +94 -0
- package/dist/shared/trace-engine.js +54 -0
- package/dist/shared/trace-event-projection.js +124 -67
- package/dist/shared/trace-history.js +47 -12
- package/dist/shared/trace-live-reducer.js +23 -4
- package/dist/shared/trace-nodes.js +17 -28
- package/dist/shared/trace-order.js +41 -5
- package/dist/shared/trace-page-merge.js +44 -14
- package/dist/shared/trace-patch-nodes.js +2 -0
- package/dist/shared/trace-subagent-links.js +4 -1
- package/dist/shared/trace-tool-identity.js +15 -8
- package/dist/signals/projector.js +28 -6
- package/dist/subagents/tool.js +7 -3
- package/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
package/dist/debug/index.js
CHANGED
|
@@ -639,7 +639,7 @@ async function runDebugJobs(args) {
|
|
|
639
639
|
if (command === "list") {
|
|
640
640
|
const jobs = reliability.listJobs({ queue: options.queue, limit: options.limit ? Number(options.limit) : undefined });
|
|
641
641
|
if (options.json)
|
|
642
|
-
console.log(formatJson({ jobs }));
|
|
642
|
+
console.log(formatJson({ jobs: jobs.map(observableJobRow) }));
|
|
643
643
|
else
|
|
644
644
|
console.log(formatRows(jobs.map(compactJobRow)));
|
|
645
645
|
return;
|
|
@@ -647,7 +647,7 @@ async function runDebugJobs(args) {
|
|
|
647
647
|
if (command === "dead") {
|
|
648
648
|
const jobs = reliability.listDead({ queue: options.queue, limit: options.limit ? Number(options.limit) : undefined });
|
|
649
649
|
if (options.json)
|
|
650
|
-
console.log(formatJson({ jobs }));
|
|
650
|
+
console.log(formatJson({ jobs: jobs.map(observableDeadJobRow) }));
|
|
651
651
|
else
|
|
652
652
|
console.log(formatRows(jobs.map(compactDeadJobRow)));
|
|
653
653
|
return;
|
|
@@ -976,26 +976,68 @@ function compactEventRow(event) {
|
|
|
976
976
|
};
|
|
977
977
|
}
|
|
978
978
|
function compactJobRow(job) {
|
|
979
|
+
const correlation = outputPersistenceJobCorrelation(job.payload);
|
|
979
980
|
return {
|
|
980
981
|
jobId: job.jobId,
|
|
981
982
|
queue: job.queue,
|
|
982
983
|
state: job.state,
|
|
983
984
|
runAt: job.runAt,
|
|
984
985
|
attempts: `${job.attempts}/${job.maxAttempts}`,
|
|
986
|
+
piboSessionId: correlation.piboSessionId,
|
|
987
|
+
eventId: correlation.eventId,
|
|
988
|
+
phase: correlation.phase,
|
|
985
989
|
workerId: job.workerId,
|
|
986
990
|
lastError: job.lastError,
|
|
987
991
|
};
|
|
988
992
|
}
|
|
989
993
|
function compactDeadJobRow(job) {
|
|
994
|
+
const correlation = outputPersistenceJobCorrelation(job.payload);
|
|
990
995
|
return {
|
|
991
996
|
jobId: job.jobId,
|
|
992
997
|
queue: job.queue,
|
|
993
998
|
attempts: `${job.attempts}/${job.maxAttempts}`,
|
|
999
|
+
piboSessionId: correlation.piboSessionId,
|
|
1000
|
+
eventId: correlation.eventId,
|
|
1001
|
+
phase: correlation.phase,
|
|
994
1002
|
deadAt: job.deadAt,
|
|
995
1003
|
deadReason: job.deadReason,
|
|
996
1004
|
lastError: job.lastError,
|
|
997
1005
|
};
|
|
998
1006
|
}
|
|
1007
|
+
function observableJobRow(job) {
|
|
1008
|
+
return { ...compactJobRow(job), attempts: job.attempts, maxAttempts: job.maxAttempts };
|
|
1009
|
+
}
|
|
1010
|
+
function observableDeadJobRow(job) {
|
|
1011
|
+
return { ...compactDeadJobRow(job), attempts: job.attempts, maxAttempts: job.maxAttempts };
|
|
1012
|
+
}
|
|
1013
|
+
function outputPersistenceJobCorrelation(payload) {
|
|
1014
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
1015
|
+
return {};
|
|
1016
|
+
const envelope = payload;
|
|
1017
|
+
const state = envelope.state;
|
|
1018
|
+
let phase;
|
|
1019
|
+
if (state && typeof state === "object" && !Array.isArray(state)) {
|
|
1020
|
+
const deliveries = state.deliveries;
|
|
1021
|
+
if (Array.isArray(deliveries) && deliveries.length) {
|
|
1022
|
+
const rows = deliveries.filter((delivery) => !!delivery && typeof delivery === "object" && !Array.isArray(delivery));
|
|
1023
|
+
if (rows.length === deliveries.length) {
|
|
1024
|
+
if (rows.every((delivery) => delivery.sideEffectsDelivered === true || delivery.persisted === true))
|
|
1025
|
+
phase = "delivered";
|
|
1026
|
+
else if (rows.every((delivery) => delivery.reliabilityDelivered === true))
|
|
1027
|
+
phase = "side-effects";
|
|
1028
|
+
else if (rows.every((delivery) => delivery.v2 !== undefined))
|
|
1029
|
+
phase = "reliability";
|
|
1030
|
+
else
|
|
1031
|
+
phase = "v2";
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
...(typeof envelope.piboSessionId === "string" ? { piboSessionId: envelope.piboSessionId } : {}),
|
|
1037
|
+
...(typeof envelope.eventId === "string" ? { eventId: envelope.eventId } : {}),
|
|
1038
|
+
...(phase ? { phase } : {}),
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
999
1041
|
function compactRunRow(run) {
|
|
1000
1042
|
return {
|
|
1001
1043
|
runId: run.runId,
|
package/dist/gateway/web.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { CHAT_WEB_APP_NAME } from "../apps/chat/web-app.js";
|
|
2
2
|
import { createDefaultPiboPlugins } from "../plugins/builtin.js";
|
|
3
3
|
import { createPiboBetterAuthPlugin } from "../plugins/better-auth.js";
|
|
4
|
-
import { createPiboChatCustomAgentProfilesPlugin } from "../plugins/chat-custom-agents.js";
|
|
5
|
-
import { createPiboChatUserSkillsPlugin } from "../plugins/chat-user-skills.js";
|
|
6
4
|
import { createPiboChatWebPlugin } from "../plugins/chat-web.js";
|
|
7
5
|
import { createPiboChatVscodeWebPlugin } from "../plugins/chat-vscode-web.js";
|
|
8
|
-
import {
|
|
6
|
+
import { createPiboUserProfileResourcePlugins } from "../plugins/user-profile-resources.js";
|
|
9
7
|
import { createPiboPreviewPlugin } from "../previews/plugin.js";
|
|
10
8
|
import { createPiboCronPlugin } from "../cron/plugin.js";
|
|
11
9
|
import { createPiboLoopPlugin } from "../loops/plugin.js";
|
|
@@ -154,13 +152,15 @@ export function createWebPiboPluginRegistry(options = {}) {
|
|
|
154
152
|
landingAppName: CHAT_WEB_APP_NAME,
|
|
155
153
|
}),
|
|
156
154
|
createPiboCronPlugin({ cronStorePath: resolvedOptions.chat?.cronStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
...createPiboUserProfileResourcePlugins({
|
|
156
|
+
userSkills: {
|
|
157
|
+
globalRoot: resolvedOptions.chat?.userSkillGlobalRoot,
|
|
158
|
+
workspaceRoot: resolvedOptions.chat?.userSkillWorkspaceRoot,
|
|
159
|
+
},
|
|
160
|
+
contextFiles: resolvedOptions.contextFiles,
|
|
161
|
+
customAgents: { agentStorePath: resolvedOptions.chat?.agentStorePath },
|
|
160
162
|
}),
|
|
161
|
-
createPiboChatCustomAgentProfilesPlugin({ agentStorePath: resolvedOptions.chat?.agentStorePath }),
|
|
162
163
|
createPiboLoopPlugin({ loopStorePath: resolvedOptions.chat?.ralphStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
|
|
163
|
-
createPiboContextFilesPlugin(resolvedOptions.contextFiles),
|
|
164
164
|
createPiboPreviewPlugin(),
|
|
165
165
|
createPiboChatWebPlugin(resolvedOptions.chat),
|
|
166
166
|
createPiboChatVscodeWebPlugin(),
|
package/dist/local/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { PiboSessionRouter } from "../core/session-router.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createPiboProfileFromRegistryOrDefault, resolvePiboProfileNameFromRegistryOrDefault } from "../plugins/builtin.js";
|
|
4
|
+
import { createDefaultPiboUserProfileRegistry } from "../plugins/user-profile-resources.js";
|
|
4
5
|
import { getDefaultPiboWorkspace } from "../core/workspace.js";
|
|
5
6
|
import { InMemoryPiboSessionStore, } from "../sessions/store.js";
|
|
6
7
|
export const LOCAL_TUI_CHANNEL_NAME = "local-tui";
|
|
@@ -52,7 +53,7 @@ export class LocalRoutedTuiClient {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
export function createLocalRoutedTuiClient(options = {}) {
|
|
55
|
-
const registry = options.pluginRegistry ??
|
|
56
|
+
const registry = options.pluginRegistry ?? createDefaultPiboUserProfileRegistry();
|
|
56
57
|
const profileName = resolvePiboProfileNameFromRegistryOrDefault(registry, options.profile);
|
|
57
58
|
const profile = createPiboProfileFromRegistryOrDefault(registry, profileName);
|
|
58
59
|
const sessionName = options.sessionName ?? "default";
|
|
@@ -101,6 +101,36 @@ function revisionRowToRecord(row) {
|
|
|
101
101
|
...(row.actor_id ? { actorId: row.actor_id } : {}),
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
export function readContextFileCatalog(path) {
|
|
105
|
+
const resolvedPath = resolve(path);
|
|
106
|
+
if (!existsSync(resolvedPath))
|
|
107
|
+
return [];
|
|
108
|
+
const db = new DatabaseSync(resolvedPath, { readOnly: true });
|
|
109
|
+
try {
|
|
110
|
+
const table = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'context_files'").get();
|
|
111
|
+
if (!table)
|
|
112
|
+
return [];
|
|
113
|
+
const columns = new Set(db.prepare("PRAGMA table_info(context_files)").all().map((column) => column.name));
|
|
114
|
+
const required = ["key", "label", "managed_path", "scope", "agent_profile_name"];
|
|
115
|
+
if (!required.every((column) => columns.has(column)))
|
|
116
|
+
return [];
|
|
117
|
+
const rows = db.prepare(`
|
|
118
|
+
SELECT key, label, managed_path, scope, agent_profile_name
|
|
119
|
+
FROM context_files
|
|
120
|
+
ORDER BY updated_at DESC
|
|
121
|
+
`).all();
|
|
122
|
+
return rows.map((row) => ({
|
|
123
|
+
key: row.key,
|
|
124
|
+
label: row.label,
|
|
125
|
+
managedPath: row.managed_path,
|
|
126
|
+
scope: row.scope,
|
|
127
|
+
...(row.agent_profile_name ? { agentProfileName: row.agent_profile_name } : {}),
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
db.close();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
104
134
|
export class ContextFileMetadataStore {
|
|
105
135
|
legacyStorePath;
|
|
106
136
|
db;
|
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { readFallbackPidFile, readPidFile } from "../gateway/pidfile.js";
|
|
7
7
|
import { PiboWebHttpError, readJsonBody, responseHtml, responseJson } from "../web/http.js";
|
|
8
8
|
import { definePiboPlugin } from "./registry.js";
|
|
9
|
-
import { buildContextFileDiff, ContextFileMetadataStore, hashContextFileContent, } from "./context-files-store.js";
|
|
9
|
+
import { buildContextFileDiff, ContextFileMetadataStore, hashContextFileContent, readContextFileCatalog, } from "./context-files-store.js";
|
|
10
10
|
export const CONTEXT_FILES_APP_NAME = "pibo.context-files";
|
|
11
11
|
export const CONTEXT_FILES_MOUNT_PATH = "/apps/context-files";
|
|
12
12
|
export const CONTEXT_FILES_API_PREFIX = "/api/context-files";
|
|
@@ -1028,6 +1028,57 @@ function createContextFilesWebApp(service) {
|
|
|
1028
1028
|
},
|
|
1029
1029
|
};
|
|
1030
1030
|
}
|
|
1031
|
+
export function createPiboContextFilesCatalogPlugin(options = {}) {
|
|
1032
|
+
const paths = resolveContextFilesPaths(options);
|
|
1033
|
+
return definePiboPlugin({
|
|
1034
|
+
id: "pibo.context-files-catalog",
|
|
1035
|
+
name: "Pibo Context Files Catalog",
|
|
1036
|
+
register(api) {
|
|
1037
|
+
const registeredPaths = new Set();
|
|
1038
|
+
const registeredKeys = new Set();
|
|
1039
|
+
try {
|
|
1040
|
+
for (const file of readContextFileCatalog(paths.metadataPath)) {
|
|
1041
|
+
api.upsertContextFile(profileForManaged(file));
|
|
1042
|
+
registeredPaths.add(resolve(file.managedPath));
|
|
1043
|
+
registeredKeys.add(file.key);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
catch (error) {
|
|
1047
|
+
console.warn(`[pibo] Skipping managed context-file catalog registration: ${error instanceof Error ? error.message : String(error)}`);
|
|
1048
|
+
}
|
|
1049
|
+
let entries = [];
|
|
1050
|
+
try {
|
|
1051
|
+
entries = readdirSync(paths.globalDir, { withFileTypes: true });
|
|
1052
|
+
}
|
|
1053
|
+
catch (error) {
|
|
1054
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
1055
|
+
if (code !== "ENOENT") {
|
|
1056
|
+
console.warn(`[pibo] Skipping global context-file discovery: ${error instanceof Error ? error.message : String(error)}`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const usedKeys = new Set(registeredKeys);
|
|
1060
|
+
for (const entry of entries) {
|
|
1061
|
+
if (!entry.isFile())
|
|
1062
|
+
continue;
|
|
1063
|
+
const extension = extname(entry.name).toLowerCase();
|
|
1064
|
+
if (extension !== ".md" && extension !== ".markdown")
|
|
1065
|
+
continue;
|
|
1066
|
+
const path = resolve(paths.globalDir, entry.name);
|
|
1067
|
+
if (registeredPaths.has(path))
|
|
1068
|
+
continue;
|
|
1069
|
+
const key = uniqueKey(`ctx:${slugSegment(entry.name.slice(0, -extension.length) || entry.name)}`, usedKeys);
|
|
1070
|
+
usedKeys.add(key);
|
|
1071
|
+
api.upsertContextFile({
|
|
1072
|
+
key,
|
|
1073
|
+
label: labelFromManagedPath(entry.name),
|
|
1074
|
+
path,
|
|
1075
|
+
source: "managed",
|
|
1076
|
+
scope: "global",
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1031
1082
|
export function createPiboContextFilesPlugin(options = {}) {
|
|
1032
1083
|
const paths = resolveContextFilesPaths(options);
|
|
1033
1084
|
return definePiboPlugin({
|
package/dist/plugins/registry.js
CHANGED
|
@@ -521,11 +521,13 @@ export class PiboPluginRegistry {
|
|
|
521
521
|
nativeTools: sessionContext.tools.filter((tool) => tool.enabled !== false).map((tool) => tool.name),
|
|
522
522
|
skills: sessionContext.skills.filter((skill) => skill.enabled !== false).map((skill) => skill.name),
|
|
523
523
|
contextFiles: sessionContext.contextFiles.filter((contextFile) => contextFile.enabled !== false).map(contextFileKey),
|
|
524
|
+
diagnostics: sessionContext.diagnostics.map((diagnostic) => ({ ...diagnostic })),
|
|
524
525
|
subagents: sessionContext.subagents.filter((subagent) => subagent.enabled !== false),
|
|
525
526
|
mcpServers: [...sessionContext.mcpServers],
|
|
526
527
|
piPackages: sessionContext.piPackages.filter((pkg) => pkg.enabled !== false).map((pkg) => pkg.id),
|
|
527
528
|
model: sessionContext.model ? { ...sessionContext.model } : undefined,
|
|
528
529
|
mainModel: sessionContext.mainModel ? { ...sessionContext.mainModel } : undefined,
|
|
530
|
+
mainModelFallbacks: sessionContext.mainModelFallbacks.map((model) => ({ ...model })),
|
|
529
531
|
subagentModel: sessionContext.subagentModel ? { ...sessionContext.subagentModel } : undefined,
|
|
530
532
|
thinkingLevel: sessionContext.thinkingLevel,
|
|
531
533
|
mainThinkingLevel: sessionContext.mainThinkingLevel,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createPiboChatCustomAgentProfilesPlugin, } from "./chat-custom-agents.js";
|
|
2
|
+
import { createPiboChatUserSkillsPlugin } from "./chat-user-skills.js";
|
|
3
|
+
import { createPiboContextFilesCatalogPlugin, createPiboContextFilesPlugin, } from "./context-files.js";
|
|
4
|
+
import { createDefaultPiboPluginRegistry } from "./builtin.js";
|
|
5
|
+
export function createPiboUserProfileResourcePlugins(options = {}) {
|
|
6
|
+
return [
|
|
7
|
+
createPiboChatUserSkillsPlugin(options.userSkills),
|
|
8
|
+
options.contextFilesMode === "catalog"
|
|
9
|
+
? createPiboContextFilesCatalogPlugin(options.contextFiles)
|
|
10
|
+
: createPiboContextFilesPlugin(options.contextFiles),
|
|
11
|
+
createPiboChatCustomAgentProfilesPlugin(options.customAgents),
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
export function createDefaultPiboUserProfileRegistry(options = {}) {
|
|
15
|
+
const registry = createDefaultPiboPluginRegistry();
|
|
16
|
+
registerPiboUserProfileResources(registry, { contextFilesMode: "catalog", ...options });
|
|
17
|
+
return registry;
|
|
18
|
+
}
|
|
19
|
+
export function registerPiboUserProfileResources(registry, options = {}) {
|
|
20
|
+
for (const plugin of createPiboUserProfileResourcePlugins(options))
|
|
21
|
+
registry.registerPlugin(plugin);
|
|
22
|
+
}
|