@xopcai/xopc 0.0.396 → 0.0.397
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DwTaeL1e.js → agent-avatar-dicebear-CIEGrioy.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-D8Q9Cpge.js → agent-defaults-settings-panel-CzKNVkRn.js} +1 -1
- package/dist/gateway/static/root/assets/agent-plugin-dialog-mpA9rwYd.js +1 -0
- package/dist/gateway/static/root/assets/{agents-DOv68iBN.js → agents-7VMa7Rzo.js} +3 -3
- package/dist/gateway/static/root/assets/{app-management-settings-panel-B1ApwlFh.js → app-management-settings-panel-DIJ0ii-n.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-Avd_jVji.js → appearance-settings-BGnyHS-p.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CvqDUvof.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-C26WGbj9.js → archive-plugin-CR8FcRAa.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-T3FSuURJ.js → automations-page-BLInZo7N.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-B9r27rPk.js → automations-workspace-DvTSHLt3.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-CLaLkM3M.js → binary-plugins-DW9pu205.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CB4oRyM5.js → block-editor-ClgTc5v8.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-0nkk3bLA.js → browser-automation-inputs-Bn7kDuUB.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-KLG8IXvl.js → browser-automations-page-CJGBhDnv.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-Tcejw5fF.js → browser-settings-page-tsi5W-cJ.js} +1 -1
- package/dist/gateway/static/root/assets/capabilities-page-CfbGk8q_.js +2 -0
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-kEvUR6Yu.js → capabilities-settings-panel-BkO7QEyA.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-header-actions-Br5PchG2.js → capability-header-actions-9ZG71-BO.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-DRaE6_ub.js → channels-D0CwJktG.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-Cl0hAav9.js → chat-terminal-dock-x1W2_hwc.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-B6JI2S1b.js → computer-settings-page-D_b_bFVz.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-AqsoqTnF.js → connector-service-page-Kf8kaNm_.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-cDRZ8bqm.js → connectors-page-DlpfL2Rc.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-CyFehuto.js → date-picker-d5kiPUG0.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-Vu_5QGGJ.js → dependency-picker-BcH76tQC.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-Dw9k0Z2p.js → desktop-pet-B-925tPX.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DKqkhvxu.js → desktop-pet-settings-3XJ4qR65.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-12m5Z9-8.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-7svbL2tR.js → directory-picker-path-field-B-qZCjGR.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CFa6PCBV.js → extension-debug-page-BlQu1u6X.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BD4nZ9sf.js → extension-page-CXxM1M62.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CxPIMrmn.js → extension-settings-page-AF5nvsPP.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-Da2CtQ7c.js → gateway-settings-ABXvW_yL.js} +1 -1
- package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-DzEeyJpH.js → highlighted-body-KQOG7T2V-Czdbbi0H.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-Cp4-sTfX.js → home-page-LXGxGwVl.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-Zo56ZmKs.js → image-generation-api-DPFPJ2ps.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-C_PPaOFv.js → imports-page-Bwyotfbt.js} +1 -1
- package/dist/gateway/static/root/assets/index-BKHxJaGD.css +1 -0
- package/dist/gateway/static/root/assets/{index-XRbEWVyZ.js → index-Dsp_gNdl.js} +116 -116
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BGtrzFT6.js → keyboard-shortcuts-settings-C20gl-l7.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-Cu80fCA0.js → local-app-workbench-page-o0M_OSjC.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-Zsu8FnwP.js → local-apps-page-DoWJCmOq.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-DlIhfEW_.js → logs-page-BFTL93RX.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-CDV-xItG.js → management-settings-C52cuMg1.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Ba1_QMmZ.js → media-plugins-DLrTRF0a.js} +1 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-CPYJsRUJ.js +1 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-BXi6BrXd.js → note-detail-panel-CNN41byS.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-ClR9R90o.js → notes-page-BsfBp_8z.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-BqNWtf_f.js → page-context-capture-button-YTVWZ02-.js} +1 -1
- package/dist/gateway/static/root/assets/{personal-page-CSF-sn8w.js → personal-page-N4KoCZYG.js} +1 -1
- package/dist/gateway/static/root/assets/plugin-details-page-DcrrzQI-.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-kkbrbiVd.js → preview-open-alternatives-DzA__S54.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-Dheqo_4J.js → product-open-page-DvlMKj0T.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-B9Y6dIuh.js → product-preview-content-qdFyo4NG.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-l5dXxnSq.js → project-detail-page-y67s_fA8.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-95Od8gLy.js → projects-page-D5iYgi0O.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-DPEpPFax.js → remote-access-hub-DeFF7Re-.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BjyZGNP3.js → runtime-tools-settings-panel-BCHLh-KQ.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DelMOLjg.js → schema-form-CJvaVHkx.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-BN0nMm0w.js → sessions-page-DZ93_D5M.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-F1WX7vxq.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-DNWEDA71.js → settings-form-section-B3eODxNv.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-DJ27gC7J.js → settings-page-Dg86uCnr.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-BWA_puDN.js → setup-status-panel-ruFzAArp.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-Du4mumVM.js → share-preview-page-DCy1f25V.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-C0ixka9_.js → shares-settings-DeIVZvpJ.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-zOxHjU7m.js → skill-api-RisyPAMA.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-743oJIiS.js → skill-reload-api-Dwkm1Fof.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-DBYhEjP9.js → skills-page-UiTLsrFc.js} +1 -1
- package/dist/gateway/static/root/assets/{style-DsnKw5jo.js → style-CwMX73G7.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-mz9hWo_H.js → system-settings-panel-Cma37Pfm.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-Brp2wrEc.js → task-detail-page-BKOF_kxS.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-aC-ziRbY.js → tracing-settings-page-DC-kLysq.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-Ch2Veztk.js → tunnel-api-BgdhbR1L.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-KfKZ2a3t.js → usage-page-BSlFg3Xj.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-BLW3t_OK.js → use-autosave-iKvZ1pvC.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-BSsT8WjA.js → user-model-page-D4GB8QsM.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-DulF6Cm4.js → voice-api-key-field-CESbtuwM.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-bBMZM8tN.js → work-discovery-overlay-dtPpmYyh.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-CsxXS1WI.js → workflow-run-setup-panel-DmFn3E_B.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-a-mfjQ2h.js → workflows-page-DEtaRuxH.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-wwIRBFCs.js → xopc-cloud-account-card-DTZnabfg.js} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/embedded/codemode-extension.d.ts +1 -0
- package/dist/src/agent/embedded/codemode-extension.js +6 -1
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/embedded/run-turn.js +34 -2
- package/dist/src/agent/embedded/session-runner.js +1 -1
- package/dist/src/agent/mcp/config.d.ts +2 -0
- package/dist/src/agent/mcp/config.js +2 -2
- package/dist/src/agent/mcp/connection-setup.js +13 -1
- package/dist/src/agent/mcp/effective-server.d.ts +3 -0
- package/dist/src/agent/mcp/effective-server.js +27 -0
- package/dist/src/agent/mcp/native-mcp.d.ts +1 -1
- package/dist/src/agent/mcp/native-mcp.js +13 -4
- package/dist/src/agent/mcp/tool-catalog.js +2 -1
- package/dist/src/agent/skills/index.d.ts +1 -0
- package/dist/src/agent/skills/index.js +6 -2
- package/dist/src/agent/skills/skill-manager.d.ts +1 -0
- package/dist/src/agent/skills/skill-manager.js +6 -1
- package/dist/src/agent/skills/skill-sources.d.ts +1 -0
- package/dist/src/agent/skills/skill-sources.js +4 -3
- package/dist/src/agent/skills/types.d.ts +1 -0
- package/dist/src/agent/workspace-runtime/registry.js +6 -2
- package/dist/src/browser/native-messaging-host.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +2 -2
- package/dist/src/channels/index.js +1 -1
- package/dist/src/channels/manager.js +1 -1
- package/dist/src/cli/commands/channels.js +1 -1
- package/dist/src/cli/commands/connectors.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-agent-plugin.js +92 -7
- package/dist/src/cli/commands/extension-marketplace.js +2 -2
- package/dist/src/cli/commands/extension.js +1 -1
- package/dist/src/cli/commands/mcp.js +6 -4
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/extensions/agent-plugins/auth.d.ts +7 -1
- package/dist/src/extensions/agent-plugins/auth.js +53 -12
- package/dist/src/extensions/agent-plugins/branding.d.ts +6 -0
- package/dist/src/extensions/agent-plugins/branding.js +44 -0
- package/dist/src/extensions/agent-plugins/cli-authorization.d.ts +22 -0
- package/dist/src/extensions/agent-plugins/cli-authorization.js +198 -0
- package/dist/src/extensions/agent-plugins/connection-reuse.d.ts +10 -0
- package/dist/src/extensions/agent-plugins/connection-reuse.js +23 -0
- package/dist/src/extensions/agent-plugins/credentials.js +24 -1
- package/dist/src/extensions/agent-plugins/formats.d.ts +27 -0
- package/dist/src/extensions/agent-plugins/formats.js +208 -0
- package/dist/src/extensions/agent-plugins/inventory.d.ts +142 -2
- package/dist/src/extensions/agent-plugins/inventory.js +39 -5
- package/dist/src/extensions/agent-plugins/mcp-adapter.d.ts +2 -1
- package/dist/src/extensions/agent-plugins/mcp-adapter.js +42 -8
- package/dist/src/extensions/agent-plugins/mcp-format.d.ts +2 -0
- package/dist/src/extensions/agent-plugins/mcp-format.js +82 -0
- package/dist/src/extensions/agent-plugins/resources.d.ts +14 -0
- package/dist/src/extensions/agent-plugins/resources.js +112 -0
- package/dist/src/extensions/agent-plugins/setup-contract.d.ts +54 -0
- package/dist/src/extensions/agent-plugins/setup-contract.js +58 -0
- package/dist/src/extensions/agent-plugins/setup.d.ts +119 -0
- package/dist/src/extensions/agent-plugins/setup.js +491 -0
- package/dist/src/extensions/agent-plugins/source-catalog.d.ts +21 -0
- package/dist/src/extensions/agent-plugins/source-catalog.js +175 -0
- package/dist/src/extensions/agent-plugins/sources.d.ts +1 -1
- package/dist/src/extensions/agent-plugins/sources.js +12 -3
- package/dist/src/extensions/agent-plugins/store.d.ts +33 -2
- package/dist/src/extensions/agent-plugins/store.js +175 -25
- package/dist/src/extensions/agent-plugins/user-config.d.ts +5 -0
- package/dist/src/extensions/agent-plugins/user-config.js +49 -0
- package/dist/src/extensions/agent-plugins/validation.d.ts +26 -1
- package/dist/src/extensions/agent-plugins/validation.js +142 -27
- package/dist/src/extensions/sdk/index.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/routes/agent-plugins.js +120 -9
- package/dist/src/gateway/hono/routes/browser-install.js +1 -1
- package/dist/src/gateway/hono/routes/composer-history.js +22 -4
- package/dist/src/gateway/hono/routes/mcp.js +6 -4
- package/dist/src/gateway/service.js +5 -3
- package/dist/src/gateway/session-context-summary.js +1 -1
- package/dist/src/storage/sqlite/composer-input-history-repository.d.ts +4 -3
- package/dist/src/storage/sqlite/composer-input-history-repository.js +10 -8
- package/dist/src/storage/sqlite/device-pairing-approval.js +1 -1
- package/dist/src/storage/sqlite/migrations/257_session_composer_history.sql +11 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +2 -2
- package/dist/src/tui/backends/embedded-backend.js +4 -4
- package/dist/src/tui/backends/gateway-realtime-backend.d.ts +2 -2
- package/dist/src/tui/backends/gateway-realtime-backend.js +7 -4
- package/dist/src/tui/composer-history.d.ts +14 -0
- package/dist/src/tui/composer-history.js +85 -0
- package/dist/src/tui/tui-backend.d.ts +4 -3
- package/dist/src/tui/tui.js +19 -46
- package/package.json +12 -4
- package/dist/gateway/static/root/assets/agent-plugin-dialog-C-UKhDkS.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-Da6Ar2U3.js +0 -1
- package/dist/gateway/static/root/assets/capabilities-page-BJlw9rhA.js +0 -2
- package/dist/gateway/static/root/assets/device-pairing-wizard-4l4ESMon.js +0 -1
- package/dist/gateway/static/root/assets/index-zIgp-ZTu.css +0 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-CpK57Kgp.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BvfEKyfy.js +0 -1
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { McpOAuthConfigSchema, init_schema } from "../../config/schema.js";
|
|
2
|
+
import { pluginAuthorizations, safeAuthorizationUrl } from "./setup-contract.js";
|
|
3
|
+
import { normalizePluginMcpServer } from "./mcp-format.js";
|
|
4
|
+
import { pluginResources } from "./resources.js";
|
|
5
|
+
import { collectPluginSkillFiles, readPluginLayout } from "./formats.js";
|
|
1
6
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
2
7
|
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
3
8
|
import { createHash } from "node:crypto";
|
|
@@ -5,6 +10,7 @@ import { z } from "zod";
|
|
|
5
10
|
import yaml from "js-yaml";
|
|
6
11
|
import { validateHeaderName, validateHeaderValue } from "node:http";
|
|
7
12
|
//#region src/extensions/agent-plugins/validation.ts
|
|
13
|
+
init_schema();
|
|
8
14
|
const PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
9
15
|
const MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
|
|
10
16
|
const pluginName = z.string().min(1).max(64).regex(/^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/);
|
|
@@ -34,7 +40,17 @@ const serverSchema = z.discriminatedUnion("type", [z.strictObject({
|
|
|
34
40
|
}), z.strictObject({
|
|
35
41
|
type: z.literal("streamable-http"),
|
|
36
42
|
url: z.string().min(1),
|
|
37
|
-
headers: strings.optional()
|
|
43
|
+
headers: strings.optional(),
|
|
44
|
+
oauth: McpOAuthConfigSchema.omit({ clientSecret: true }).optional(),
|
|
45
|
+
connectionAuth: z.strictObject({
|
|
46
|
+
mode: z.enum([
|
|
47
|
+
"none",
|
|
48
|
+
"oauth",
|
|
49
|
+
"api-key"
|
|
50
|
+
]),
|
|
51
|
+
headerName: z.string().optional(),
|
|
52
|
+
prefix: z.string().optional()
|
|
53
|
+
}).optional()
|
|
38
54
|
})]);
|
|
39
55
|
function containedPath(root, path) {
|
|
40
56
|
const base = resolve(root);
|
|
@@ -67,7 +83,7 @@ function readPackageFile(root, path) {
|
|
|
67
83
|
return readFileSync(safe, "utf8");
|
|
68
84
|
}
|
|
69
85
|
function expandPluginVariables(value, root, data) {
|
|
70
|
-
return value.replace(/\$\{PLUGIN_(ROOT|DATA)\}/g, (_, key) => key === "ROOT" ? root : data);
|
|
86
|
+
return value.replace(/\$\{(?:CLAUDE_)?PLUGIN_(ROOT|DATA)\}/g, (_, key) => key === "ROOT" ? root : data);
|
|
71
87
|
}
|
|
72
88
|
function resolvePluginCwd(cwd, root, data) {
|
|
73
89
|
if (cwd === void 0) return root;
|
|
@@ -80,7 +96,7 @@ function validatePluginServer(raw, root, data) {
|
|
|
80
96
|
if (server.type === "stdio") {
|
|
81
97
|
if (server.command.startsWith("./")) {
|
|
82
98
|
if (!statSync(containedPath(root, resolve(root, server.command))).isFile()) throw new Error("MCP command must be a file");
|
|
83
|
-
} else if (!/^[^\s/\\:;|&<>`$\x00-\x1f]+$/.test(server.command)) throw new Error("MCP command must be one bare executable or ./package-path");
|
|
99
|
+
} else if (!/^\$\{PLUGIN_RESOURCES\}\/[a-z][a-z0-9.-]{0,63}$/.test(server.command) && !/^[^\s/\\:;|&<>`$\x00-\x1f]+$/.test(server.command)) throw new Error("MCP command must be one bare executable or ./package-path");
|
|
84
100
|
for (const key of Object.keys(server.env ?? {})) {
|
|
85
101
|
if (["PLUGIN_ROOT", "PLUGIN_DATA"].includes(process.platform === "win32" ? key.toUpperCase() : key)) throw new Error("Reserved plugin environment variable");
|
|
86
102
|
if (!key || /[=\0]/.test(key)) throw new Error("Invalid environment name");
|
|
@@ -96,6 +112,16 @@ function validatePluginServer(raw, root, data) {
|
|
|
96
112
|
const url = new URL(server.url);
|
|
97
113
|
const loopback = url.hostname === "localhost" || url.hostname === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(url.hostname);
|
|
98
114
|
if (!["https:", "http:"].includes(url.protocol) || url.protocol === "http:" && !loopback || url.username || url.password || server.url.includes("#")) throw new Error("Remote MCP requires HTTPS (HTTP is allowed on loopback), without user information or fragments");
|
|
115
|
+
if (server.connectionAuth?.headerName) {
|
|
116
|
+
validateHeaderName(server.connectionAuth.headerName);
|
|
117
|
+
if ([
|
|
118
|
+
"host",
|
|
119
|
+
"cookie",
|
|
120
|
+
"content-length",
|
|
121
|
+
"connection",
|
|
122
|
+
"transfer-encoding"
|
|
123
|
+
].includes(server.connectionAuth.headerName.toLowerCase())) throw new Error("Unsupported authentication header");
|
|
124
|
+
}
|
|
99
125
|
const names = /* @__PURE__ */ new Set();
|
|
100
126
|
for (const [key, value] of Object.entries(server.headers ?? {})) {
|
|
101
127
|
validateHeaderName(key);
|
|
@@ -106,15 +132,19 @@ function validatePluginServer(raw, root, data) {
|
|
|
106
132
|
}
|
|
107
133
|
return server;
|
|
108
134
|
}
|
|
109
|
-
function inspectAgentPlugin(rootDir, dataDir = join(rootDir, ".inspection-data")) {
|
|
135
|
+
function inspectAgentPlugin(rootDir, dataDir = join(rootDir, ".inspection-data"), importManifest) {
|
|
110
136
|
const root = realpathSync(rootDir);
|
|
111
137
|
if (existsSync(join(root, "xopc.extension.json"))) throw new Error("This package is a native xopc extension");
|
|
112
138
|
if (existsSync(join(root, "package.json"))) {
|
|
113
139
|
if (JSON.parse(readPackageFile(root, join(root, "package.json")))?.xopc?.extension) throw new Error("This package is a native xopc extension");
|
|
114
140
|
}
|
|
115
|
-
const
|
|
141
|
+
const layout = readPluginLayout(root, (path) => readPackageFile(root, path), (path) => containedPath(root, path), importManifest);
|
|
142
|
+
const raw = layout.manifest;
|
|
116
143
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("Plugin manifest must be an object");
|
|
117
|
-
const diagnostics =
|
|
144
|
+
const diagnostics = layout.compatibility.filter((item) => item.support !== "supported").map((item) => ({
|
|
145
|
+
component: item.component,
|
|
146
|
+
message: item.message
|
|
147
|
+
}));
|
|
118
148
|
const cleaned = { ...raw };
|
|
119
149
|
for (const key of Object.keys(cleaned)) if (!Object.hasOwn(manifestSchema.shape, key)) {
|
|
120
150
|
diagnostics.push({
|
|
@@ -130,21 +160,34 @@ function inspectAgentPlugin(rootDir, dataDir = join(rootDir, ".inspection-data")
|
|
|
130
160
|
});
|
|
131
161
|
delete cleaned.extensions;
|
|
132
162
|
}
|
|
163
|
+
const manifest = manifestSchema.parse(cleaned);
|
|
133
164
|
const result = {
|
|
134
|
-
|
|
165
|
+
layout,
|
|
166
|
+
manifest,
|
|
135
167
|
rootDir: root,
|
|
136
168
|
skills: [],
|
|
137
169
|
servers: Object.create(null),
|
|
138
170
|
capabilities: [],
|
|
139
171
|
diagnostics
|
|
140
172
|
};
|
|
173
|
+
const report = (item) => {
|
|
174
|
+
const existing = layout.compatibility.findIndex((value) => value.component === item.component);
|
|
175
|
+
if (existing >= 0) layout.compatibility[existing] = item;
|
|
176
|
+
else layout.compatibility.push(item);
|
|
177
|
+
};
|
|
141
178
|
const attempt = (component, fn) => {
|
|
142
179
|
try {
|
|
143
180
|
fn();
|
|
144
181
|
} catch (error) {
|
|
182
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
145
183
|
diagnostics.push({
|
|
146
184
|
component,
|
|
147
|
-
message
|
|
185
|
+
message
|
|
186
|
+
});
|
|
187
|
+
report({
|
|
188
|
+
component,
|
|
189
|
+
support: "invalid",
|
|
190
|
+
message
|
|
148
191
|
});
|
|
149
192
|
}
|
|
150
193
|
};
|
|
@@ -153,47 +196,119 @@ function inspectAgentPlugin(rootDir, dataDir = join(rootDir, ".inspection-data")
|
|
|
153
196
|
lstatSync(path);
|
|
154
197
|
return true;
|
|
155
198
|
} catch (error) {
|
|
156
|
-
if (error.code
|
|
199
|
+
if (["ENOENT", "ENOTDIR"].includes(error.code ?? "")) return false;
|
|
157
200
|
throw error;
|
|
158
201
|
}
|
|
159
202
|
};
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
const filePath = containedPath(root, join(dir, "SKILL.md"));
|
|
168
|
-
const match = readPackageFile(root, filePath).match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
203
|
+
const seenSkills = /* @__PURE__ */ new Set();
|
|
204
|
+
for (const skillsDir of layout.skillRoots) attempt("skills", () => {
|
|
205
|
+
const files = layout.dialect === "portable" ? readdirSync(skillsDir).sort().map((name) => join(skillsDir, name, "SKILL.md")).filter(present) : collectPluginSkillFiles(skillsDir, (path) => containedPath(root, path));
|
|
206
|
+
for (const file of files) attempt(`skill:${file.slice(root.length + 1)}`, () => {
|
|
207
|
+
const filePath = containedPath(root, file);
|
|
208
|
+
const content = readPackageFile(root, filePath);
|
|
209
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
169
210
|
if (!match) throw new Error("Skill requires YAML frontmatter");
|
|
170
|
-
const
|
|
211
|
+
const parsed = yaml.load(match[1], { schema: yaml.JSON_SCHEMA });
|
|
212
|
+
const metadata = (layout.dialect === "portable" ? z.object({
|
|
171
213
|
name: z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
|
|
172
214
|
description: z.string().min(1).max(1024),
|
|
173
215
|
license: z.string().optional(),
|
|
174
216
|
compatibility: z.string().min(1).max(500).optional(),
|
|
175
217
|
metadata: strings.optional(),
|
|
176
218
|
"allowed-tools": z.string().optional()
|
|
177
|
-
})
|
|
178
|
-
|
|
219
|
+
}) : z.object({
|
|
220
|
+
name: z.string().min(1).max(64).optional(),
|
|
221
|
+
description: z.string().min(1).max(1024)
|
|
222
|
+
})).parse(parsed);
|
|
223
|
+
const name = metadata.name ?? dirname(filePath).split(/[\\/]/).pop();
|
|
224
|
+
if (layout.dialect === "portable" && metadata.name !== dirname(filePath).split(/[\\/]/).pop()) throw new Error("Skill name must match its directory");
|
|
225
|
+
if (!metadata.description.trim() || seenSkills.has(name)) throw new Error("Blank description or duplicate skill name");
|
|
226
|
+
seenSkills.add(name);
|
|
179
227
|
result.skills.push({
|
|
180
228
|
name,
|
|
181
229
|
filePath
|
|
182
230
|
});
|
|
231
|
+
const references = [...new Set(content.match(/\bmcp__[A-Za-z0-9_-]+__[A-Za-z0-9_-]+|\bfunctions\.(?:exec_command|apply_patch|spawn_agent|send_input|wait_agent)|\$\{(?:CLAUDE_)?PLUGIN_(?:ROOT|DATA|RESOURCES)\}/g) ?? [])].slice(0, 20);
|
|
232
|
+
const message = references.length ? `Host-specific references need execution verification: ${references.join(", ")}` : "Skill format accepted; business execution is not verified.";
|
|
233
|
+
report({
|
|
234
|
+
component: `skill:${name}`,
|
|
235
|
+
support: references.length ? "needs_adapter" : "supported",
|
|
236
|
+
message
|
|
237
|
+
});
|
|
238
|
+
if (references.length) diagnostics.push({
|
|
239
|
+
component: `skill:${name}`,
|
|
240
|
+
message
|
|
241
|
+
});
|
|
183
242
|
});
|
|
184
243
|
});
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const mcp = z.strictObject({
|
|
244
|
+
for (const rawConfig of layout.mcpConfigs) attempt("mcp", () => {
|
|
245
|
+
const mcp = layout.dialect === "portable" && present(join(root, "mcp.json")) ? z.strictObject({
|
|
188
246
|
$schema: z.literal(MCP_SCHEMA),
|
|
189
247
|
mcpServers: z.record(z.string(), z.unknown())
|
|
190
|
-
}).parse(
|
|
191
|
-
for (const [name,
|
|
248
|
+
}).parse(rawConfig) : z.object({ mcpServers: z.record(z.string(), z.unknown()) }).parse(rawConfig);
|
|
249
|
+
for (const [name, value] of Object.entries(mcp.mcpServers)) attempt(`mcp:${name}`, () => {
|
|
250
|
+
delete result.servers[name];
|
|
251
|
+
let incomplete = false;
|
|
252
|
+
const rawServer = normalizePluginMcpServer(value, layout.dialect !== "portable", (message) => {
|
|
253
|
+
incomplete = true;
|
|
254
|
+
diagnostics.push({
|
|
255
|
+
component: `mcp:${name}`,
|
|
256
|
+
message
|
|
257
|
+
});
|
|
258
|
+
});
|
|
192
259
|
result.servers[name] = validatePluginServer(rawServer, root, dataDir);
|
|
260
|
+
report({
|
|
261
|
+
component: `mcp:${name}`,
|
|
262
|
+
support: incomplete ? "needs_adapter" : "supported",
|
|
263
|
+
message: incomplete ? "Some MCP declarations require adaptation." : "MCP configuration accepted; connection and tools require setup."
|
|
264
|
+
});
|
|
193
265
|
});
|
|
194
266
|
});
|
|
195
267
|
for (const skill of result.skills) result.capabilities.push(`content.skills:${skill.name}`);
|
|
196
|
-
for (const [name, server] of Object.entries(result.servers)) result.capabilities.push(server.type === "stdio" ? `runtime.mcp.stdio:${name}:${JSON.stringify(server)}` : `network.mcp:${name}:${server.type}:${server.url}:${JSON.stringify(server.headers ?? {})}`);
|
|
268
|
+
for (const [name, server] of Object.entries(result.servers)) result.capabilities.push(server.type === "stdio" ? `runtime.mcp.stdio:${name}:${JSON.stringify(server)}` : `network.mcp:${name}:${server.type}:${server.url}:${JSON.stringify(server.headers ?? {})}:${JSON.stringify(server.oauth ?? {})}:${JSON.stringify(server.connectionAuth ?? {})}`);
|
|
269
|
+
attempt("resources", () => {
|
|
270
|
+
result.resources = pluginResources(result);
|
|
271
|
+
for (const resource of result.resources) {
|
|
272
|
+
const supported = (!resource.platform || resource.platform === process.platform) && (!resource.arch || resource.arch === process.arch);
|
|
273
|
+
report({
|
|
274
|
+
component: `resource:${resource.id}`,
|
|
275
|
+
support: supported ? "supported" : "unsupported",
|
|
276
|
+
message: supported ? "Pinned resource declaration accepted." : "Resource does not support this host platform or architecture."
|
|
277
|
+
});
|
|
278
|
+
if (!supported) diagnostics.push({
|
|
279
|
+
component: `resource:${resource.id}`,
|
|
280
|
+
message: "Resource does not support this host platform or architecture."
|
|
281
|
+
});
|
|
282
|
+
result.capabilities.push(`${resource.executable ? "runtime.resource" : "network.resource"}:${resource.id}:${resource.url}:${resource.sha256}:${resource.maxBytes}:${resource.platform ?? "any"}:${resource.arch ?? "any"}`);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
attempt("authorizations", () => {
|
|
286
|
+
const declarations = pluginAuthorizations(manifest);
|
|
287
|
+
const ids = /* @__PURE__ */ new Set();
|
|
288
|
+
for (const authorization of declarations) {
|
|
289
|
+
if (ids.has(authorization.id) || authorization.servers.some((name) => !result.servers[name])) throw new Error("Invalid or duplicate authorization target");
|
|
290
|
+
ids.add(authorization.id);
|
|
291
|
+
const methods = /* @__PURE__ */ new Set();
|
|
292
|
+
for (const method of authorization.methods) {
|
|
293
|
+
if (methods.has(method.id)) throw new Error("Duplicate authorization method");
|
|
294
|
+
methods.add(method.id);
|
|
295
|
+
if (method.helpUrl) safeAuthorizationUrl(method.helpUrl);
|
|
296
|
+
if ((method.type === "form" || method.type === "cli") && method.fields.some((key) => !layout.userConfig[key])) throw new Error("Authorization field is not declared in userConfig");
|
|
297
|
+
if (method.type === "oauth" && (!authorization.servers.includes(method.server) || result.servers[method.server]?.type === "stdio")) throw new Error("OAuth requires a remote MCP server");
|
|
298
|
+
if (method.type === "cli") {
|
|
299
|
+
validatePluginServer({
|
|
300
|
+
type: "stdio",
|
|
301
|
+
...method.login
|
|
302
|
+
}, root, dataDir);
|
|
303
|
+
validatePluginServer({
|
|
304
|
+
type: "stdio",
|
|
305
|
+
...method.check
|
|
306
|
+
}, root, dataDir);
|
|
307
|
+
result.capabilities.push(`runtime.authorization:${authorization.id}:${JSON.stringify(method)}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
});
|
|
197
312
|
result.capabilities.sort();
|
|
198
313
|
return result;
|
|
199
314
|
}
|
|
@@ -7,8 +7,8 @@ import { DEFAULT_SECURITY_CONFIG, checkExtensionDirSafety, checkExtensionPathSaf
|
|
|
7
7
|
import { SlotRegistry, getSlotRegistry, registerSlotType } from "../slots.js";
|
|
8
8
|
import { ExtensionDiagnostics, ExtensionLoaderCacheImpl, getExtensionCache, getExtensionDiagnostics } from "../diagnostics.js";
|
|
9
9
|
import { ExtensionHookRunner, createHookContext, isHookEvent } from "../hooks.js";
|
|
10
|
-
import { defineChannelPluginEntry } from "./channel-entry.js";
|
|
11
10
|
import { registerExtensionCliProgram } from "./channel-helpers.js";
|
|
11
|
+
import { defineChannelPluginEntry } from "./channel-entry.js";
|
|
12
12
|
import { lazyFunction, lazyModule } from "./lazy.js";
|
|
13
13
|
import { createMockExtensionApi, createTestGateway } from "./testing.js";
|
|
14
14
|
//#region src/extensions/sdk/index.ts
|
|
@@ -7,10 +7,10 @@ import { resolveGatewayEffectiveHost } from "../../config/gateway-bind.js";
|
|
|
7
7
|
import { loadTunnelState } from "../../tunnel/tunnel-state.js";
|
|
8
8
|
import { resolveReverseProxyPublicUrl } from "../public-url.js";
|
|
9
9
|
import { maxSessionInputRequestBodyBytes } from "../chat-limits.js";
|
|
10
|
-
import { BROWSER_EXTENSION_ID } from "../../browser/extension-identity.js";
|
|
11
10
|
import { isLoopbackIpAddress, isTrustedProxyAddress } from "../client-ip.js";
|
|
12
11
|
import { buildGatewayConsoleCspHeader } from "../security/csp.js";
|
|
13
12
|
import { checkBrowserOrigin, isChromeExtensionOrigin } from "../security/origin-check.js";
|
|
13
|
+
import { BROWSER_EXTENSION_ID } from "../../browser/extension-identity.js";
|
|
14
14
|
import { auth } from "./middleware/auth.js";
|
|
15
15
|
import { gatewayScopes } from "./middleware/scopes.js";
|
|
16
16
|
import { createChannelRateLimitMiddleware, createChatRateLimitMiddleware, createMediaRateLimitMiddleware, createStrictRateLimitMiddleware, createTaskRateLimitMiddleware, createXopcCloudPollRateLimitMiddleware } from "./middleware/strict-rate-limit.js";
|
|
@@ -1,17 +1,28 @@
|
|
|
1
|
+
import { getWorkspacePath } from "../../../config/workspace-path-helpers.js";
|
|
2
|
+
import { PluginSelectionRequired } from "../../../extensions/agent-plugins/source-catalog.js";
|
|
1
3
|
import { AgentPluginStore } from "../../../extensions/agent-plugins/store.js";
|
|
2
4
|
import { savePluginAuthBinding } from "../../../extensions/agent-plugins/auth.js";
|
|
3
5
|
import { evictAllEmbeddedSessionRunners } from "../../../agent/embedded/session-runner.js";
|
|
4
6
|
import { withAgentPluginSource } from "../../../extensions/agent-plugins/sources.js";
|
|
7
|
+
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
5
8
|
import { agentPluginInventoryRow } from "../../../extensions/agent-plugins/inventory.js";
|
|
9
|
+
import { cancelPluginCliAuthorizations } from "../../../extensions/agent-plugins/cli-authorization.js";
|
|
10
|
+
import { PluginSetupService, setupActionSchema } from "../../../extensions/agent-plugins/setup.js";
|
|
6
11
|
import { removePluginCredentials } from "../../../extensions/agent-plugins/credentials.js";
|
|
7
12
|
import { z } from "zod";
|
|
8
13
|
//#region src/gateway/hono/routes/agent-plugins.ts
|
|
9
14
|
const sourceBody = z.strictObject({
|
|
10
15
|
source: z.string().min(1),
|
|
11
|
-
reviewHash: z.string().optional()
|
|
16
|
+
reviewHash: z.string().optional(),
|
|
17
|
+
setup: z.boolean().optional()
|
|
12
18
|
});
|
|
19
|
+
function setupOwner(c) {
|
|
20
|
+
const principal = getGatewayPrincipal(c);
|
|
21
|
+
return principal.kind === "owner" ? "gateway-owner" : principal.principalId;
|
|
22
|
+
}
|
|
13
23
|
function registerAgentPluginRoutes(app, deps) {
|
|
14
24
|
const store = new AgentPluginStore();
|
|
25
|
+
const setup = new PluginSetupService(store);
|
|
15
26
|
const refresh = async () => {
|
|
16
27
|
evictAllEmbeddedSessionRunners();
|
|
17
28
|
deps.service.marketplace.reloadSkills();
|
|
@@ -21,9 +32,16 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
21
32
|
const body = sourceBody.parse(await c.req.json());
|
|
22
33
|
return c.json({
|
|
23
34
|
ok: true,
|
|
24
|
-
payload: await withAgentPluginSource(body.source, deps.service.currentConfig, (source) =>
|
|
35
|
+
payload: await withAgentPluginSource(body.source, deps.service.currentConfig, (source, _provenance, resolvedCommit, importManifest) => ({
|
|
36
|
+
...store.inspect(source, body.source, importManifest),
|
|
37
|
+
resolvedCommit
|
|
38
|
+
}))
|
|
25
39
|
});
|
|
26
40
|
} catch (error) {
|
|
41
|
+
if (error instanceof PluginSelectionRequired) return c.json({
|
|
42
|
+
ok: true,
|
|
43
|
+
payload: { candidates: error.candidates }
|
|
44
|
+
});
|
|
27
45
|
return c.json({
|
|
28
46
|
ok: false,
|
|
29
47
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -33,15 +51,18 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
33
51
|
app.post("/api/extensions/install", deps.strictRateLimitMiddleware, async (c) => {
|
|
34
52
|
try {
|
|
35
53
|
const body = sourceBody.parse(await c.req.json());
|
|
36
|
-
const plugin = await withAgentPluginSource(body.source, deps.service.currentConfig, (source, provenance) => store.install(source, {
|
|
54
|
+
const plugin = await withAgentPluginSource(body.source, deps.service.currentConfig, (source, provenance, resolvedCommit, importManifest) => store.install(source, {
|
|
37
55
|
reviewHash: body.reviewHash,
|
|
38
56
|
sourceLabel: body.source,
|
|
39
|
-
provenance
|
|
57
|
+
provenance,
|
|
58
|
+
resolvedCommit,
|
|
59
|
+
importManifest
|
|
40
60
|
}));
|
|
61
|
+
if (body.setup && plugin.readiness !== "blocked") setup.create(plugin.id, setupOwner(c));
|
|
41
62
|
await refresh();
|
|
42
63
|
return c.json({
|
|
43
64
|
ok: true,
|
|
44
|
-
payload: agentPluginInventoryRow(plugin)
|
|
65
|
+
payload: agentPluginInventoryRow(store.get(plugin.id))
|
|
45
66
|
});
|
|
46
67
|
} catch (error) {
|
|
47
68
|
return c.json({
|
|
@@ -52,7 +73,7 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
52
73
|
});
|
|
53
74
|
app.get("/api/extensions/agent-plugins/:id", (c) => {
|
|
54
75
|
try {
|
|
55
|
-
const plugin = store.get(c.req.param("id"));
|
|
76
|
+
const plugin = store.pending(c.req.param("id")) ?? store.get(c.req.param("id"));
|
|
56
77
|
return plugin ? c.json({
|
|
57
78
|
ok: true,
|
|
58
79
|
payload: agentPluginInventoryRow(plugin)
|
|
@@ -67,9 +88,91 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
67
88
|
}, 400);
|
|
68
89
|
}
|
|
69
90
|
});
|
|
91
|
+
app.get("/api/extensions/agent-plugins/:id/setup", (c) => {
|
|
92
|
+
try {
|
|
93
|
+
const id = c.req.param("id"), job = setup.get(id, setupOwner(c));
|
|
94
|
+
const plugin = store.pending(id) ?? store.get(id);
|
|
95
|
+
return c.json({
|
|
96
|
+
ok: true,
|
|
97
|
+
payload: job?.packageRevision === plugin?.receipt.revision ? job : null
|
|
98
|
+
});
|
|
99
|
+
} catch {
|
|
100
|
+
return c.json({
|
|
101
|
+
ok: false,
|
|
102
|
+
error: "Setup unavailable"
|
|
103
|
+
}, 403);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
app.get("/api/extensions/agent-plugins/:id/setup/reusable", async (c) => {
|
|
107
|
+
try {
|
|
108
|
+
return c.json({
|
|
109
|
+
ok: true,
|
|
110
|
+
payload: await setup.reusableConnections(c.req.param("id"), setupOwner(c), deps.service.currentConfig)
|
|
111
|
+
});
|
|
112
|
+
} catch {
|
|
113
|
+
return c.json({
|
|
114
|
+
ok: false,
|
|
115
|
+
error: "Reusable connections unavailable"
|
|
116
|
+
}, 400);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
app.post("/api/extensions/agent-plugins/:id/setup", deps.strictRateLimitMiddleware, async (c) => {
|
|
120
|
+
try {
|
|
121
|
+
const id = c.req.param("id"), owner = setupOwner(c);
|
|
122
|
+
const body = await c.req.json();
|
|
123
|
+
if (body.action === "start") return c.json({
|
|
124
|
+
ok: true,
|
|
125
|
+
payload: setup.create(id, owner)
|
|
126
|
+
});
|
|
127
|
+
const action = setupActionSchema.parse(body);
|
|
128
|
+
if (action.action === "prepare") return c.json({
|
|
129
|
+
ok: true,
|
|
130
|
+
payload: setup.startPrepare(id, owner, action.expectedRevision, deps.service.currentConfig, getWorkspacePath(deps.service.currentConfig) || process.cwd(), action.idempotencyKey)
|
|
131
|
+
});
|
|
132
|
+
const job = await setup.act(id, owner, action, deps.service.currentConfig, getWorkspacePath(deps.service.currentConfig) || process.cwd());
|
|
133
|
+
await refresh();
|
|
134
|
+
return c.json({
|
|
135
|
+
ok: true,
|
|
136
|
+
payload: job
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return c.json({
|
|
140
|
+
ok: false,
|
|
141
|
+
error: error instanceof Error ? error.message : "Setup failed"
|
|
142
|
+
}, 400);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
app.get("/api/extensions/agent-plugins/:id/setup/auth/:server", async (c) => {
|
|
146
|
+
try {
|
|
147
|
+
const result = await setup.authorization(c.req.param("id"), setupOwner(c), c.req.param("server"), deps.service.currentConfig);
|
|
148
|
+
return c.json({
|
|
149
|
+
ok: true,
|
|
150
|
+
payload: result ?? null
|
|
151
|
+
});
|
|
152
|
+
} catch {
|
|
153
|
+
return c.json({
|
|
154
|
+
ok: false,
|
|
155
|
+
error: "Authorization unavailable"
|
|
156
|
+
}, 400);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
app.post("/api/extensions/agent-plugins/:id/setup/auth/:server/redirect", deps.strictRateLimitMiddleware, async (c) => {
|
|
160
|
+
try {
|
|
161
|
+
const body = z.strictObject({ url: z.string().max(16384) }).parse(await c.req.json());
|
|
162
|
+
if (!setup.redirect(c.req.param("id"), setupOwner(c), c.req.param("server"), body.url, deps.service.currentConfig)) throw new Error("Callback does not match this sign-in");
|
|
163
|
+
return c.json({ ok: true });
|
|
164
|
+
} catch {
|
|
165
|
+
return c.json({
|
|
166
|
+
ok: false,
|
|
167
|
+
error: "Invalid sign-in callback"
|
|
168
|
+
}, 400);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
70
171
|
app.post("/api/extensions/agent-plugins/:id/activation", deps.strictRateLimitMiddleware, async (c) => {
|
|
71
172
|
try {
|
|
72
173
|
const body = z.strictObject({ enabled: z.boolean() }).parse(await c.req.json());
|
|
174
|
+
const job = setup.get(c.req.param("id"), setupOwner(c));
|
|
175
|
+
if (body.enabled && job?.packageRevision === store.receipt(c.req.param("id"))?.revision && job.state !== "completed") throw new Error("Finish plugin setup before enabling");
|
|
73
176
|
const plugin = store.setEnabled(c.req.param("id"), body.enabled);
|
|
74
177
|
await refresh();
|
|
75
178
|
return c.json({
|
|
@@ -86,17 +189,21 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
86
189
|
app.post("/api/extensions/agent-plugins/:id/update", deps.strictRateLimitMiddleware, async (c) => {
|
|
87
190
|
try {
|
|
88
191
|
const body = sourceBody.parse(await c.req.json());
|
|
89
|
-
const plugin = await withAgentPluginSource(body.source, deps.service.currentConfig, (source, provenance) => store.install(source, {
|
|
192
|
+
const plugin = await withAgentPluginSource(body.source, deps.service.currentConfig, (source, provenance, resolvedCommit, importManifest) => store.install(source, {
|
|
90
193
|
reviewHash: body.reviewHash,
|
|
91
194
|
replace: true,
|
|
92
195
|
expectedId: c.req.param("id"),
|
|
196
|
+
stage: body.setup,
|
|
93
197
|
sourceLabel: body.source,
|
|
94
|
-
provenance
|
|
198
|
+
provenance,
|
|
199
|
+
resolvedCommit,
|
|
200
|
+
importManifest
|
|
95
201
|
}));
|
|
202
|
+
if (body.setup && plugin.readiness !== "blocked") setup.create(plugin.id, setupOwner(c));
|
|
96
203
|
await refresh();
|
|
97
204
|
return c.json({
|
|
98
205
|
ok: true,
|
|
99
|
-
payload: agentPluginInventoryRow(plugin)
|
|
206
|
+
payload: agentPluginInventoryRow(store.pending(plugin.id) ?? store.get(plugin.id))
|
|
100
207
|
});
|
|
101
208
|
} catch (error) {
|
|
102
209
|
return c.json({
|
|
@@ -131,7 +238,10 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
131
238
|
prefix: z.string().optional()
|
|
132
239
|
})).max(20).optional()
|
|
133
240
|
}).parse(await c.req.json());
|
|
241
|
+
const owner = setupOwner(c);
|
|
242
|
+
setup.get(c.req.param("id"), owner);
|
|
134
243
|
await savePluginAuthBinding(store, c.req.param("id"), c.req.param("server"), body);
|
|
244
|
+
setup.invalidateConnection(c.req.param("id"), owner, c.req.param("server"));
|
|
135
245
|
await refresh();
|
|
136
246
|
return c.json({ ok: true });
|
|
137
247
|
} catch (error) {
|
|
@@ -143,6 +253,7 @@ function registerAgentPluginRoutes(app, deps) {
|
|
|
143
253
|
});
|
|
144
254
|
app.delete("/api/extensions/agent-plugins/:id", deps.strictRateLimitMiddleware, async (c) => {
|
|
145
255
|
try {
|
|
256
|
+
cancelPluginCliAuthorizations(c.req.param("id"));
|
|
146
257
|
const body = z.strictObject({
|
|
147
258
|
removeData: z.boolean().optional(),
|
|
148
259
|
removeCredentials: z.boolean().optional()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { acquireBrowserInstallLock, cancelBrowserInstall } from "../../../browser/install-lock.js";
|
|
2
1
|
import { createGatewayRouteLogger } from "../lib/route-logger.js";
|
|
2
|
+
import { acquireBrowserInstallLock, cancelBrowserInstall } from "../../../browser/install-lock.js";
|
|
3
3
|
import { runPlaywrightChromiumInstallWithProgress } from "../../../browser/providers/playwright-install.js";
|
|
4
4
|
import { streamSSE } from "hono/streaming";
|
|
5
5
|
//#region src/gateway/hono/routes/browser-install.ts
|
|
@@ -1,24 +1,42 @@
|
|
|
1
|
+
import { conversationIdSchema } from "../../../../packages/gateway-contract/src/conversation-identity.js";
|
|
2
|
+
import { init_src } from "../../../../packages/gateway-contract/src/index.js";
|
|
1
3
|
import { appendComposerInputHistory, clearComposerInputHistory, listComposerInputHistory } from "../../../storage/sqlite/composer-input-history-repository.js";
|
|
4
|
+
import { getSessionMetadata } from "../../../storage/sqlite/session-read-repository.js";
|
|
5
|
+
import { init_session_repository } from "../../../storage/sqlite/session-repository.js";
|
|
2
6
|
import { init_sqlite } from "../../../storage/sqlite/index.js";
|
|
3
7
|
//#region src/gateway/hono/routes/composer-history.ts
|
|
8
|
+
init_src();
|
|
4
9
|
init_sqlite();
|
|
10
|
+
init_session_repository();
|
|
5
11
|
const MAX_TEXT_LENGTH = 1e5;
|
|
6
12
|
function registerComposerHistoryRoutes(authenticated, deps) {
|
|
7
13
|
authenticated.get("/api/composer-history", (c) => {
|
|
8
|
-
|
|
14
|
+
const conversationId = c.req.query("conversationId");
|
|
15
|
+
if (!conversationId || !conversationIdSchema.safeParse(conversationId).success) return c.json({ error: "valid conversationId is required" }, 400);
|
|
16
|
+
if (!getSessionMetadata(conversationId)) return c.json({ error: "conversation not found" }, 404);
|
|
17
|
+
return c.json({ items: listComposerInputHistory(conversationId) });
|
|
9
18
|
});
|
|
10
19
|
authenticated.post("/api/composer-history", deps.chatRateLimitMiddleware, async (c) => {
|
|
11
20
|
const body = await c.req.json().catch(() => null);
|
|
21
|
+
const conversationId = body?.conversationId;
|
|
22
|
+
if (typeof conversationId !== "string" || !conversationIdSchema.safeParse(conversationId).success) return c.json({ error: "valid conversationId is required" }, 400);
|
|
23
|
+
if (!getSessionMetadata(conversationId)) return c.json({ error: "conversation not found" }, 404);
|
|
12
24
|
const text = body && typeof body === "object" && typeof body.text === "string" ? body.text.trim() : "";
|
|
13
25
|
if (!text) return c.json({ error: "text is required" }, 400);
|
|
14
26
|
if (text.length > MAX_TEXT_LENGTH) return c.json({ error: "text is too long" }, 413);
|
|
15
|
-
const result = appendComposerInputHistory(text);
|
|
27
|
+
const result = appendComposerInputHistory(conversationId, text);
|
|
16
28
|
if (result.inserted) deps.service.emit("composer-history.appended", result.item);
|
|
17
29
|
return c.json(result);
|
|
18
30
|
});
|
|
19
31
|
authenticated.delete("/api/composer-history", deps.strictRateLimitMiddleware, (c) => {
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
32
|
+
const conversationId = c.req.query("conversationId");
|
|
33
|
+
if (!conversationId || !conversationIdSchema.safeParse(conversationId).success) return c.json({ error: "valid conversationId is required" }, 400);
|
|
34
|
+
if (!getSessionMetadata(conversationId)) return c.json({ error: "conversation not found" }, 404);
|
|
35
|
+
const cleared = clearComposerInputHistory(conversationId);
|
|
36
|
+
if (cleared > 0) deps.service.emit("composer-history.cleared", {
|
|
37
|
+
conversationId,
|
|
38
|
+
cleared
|
|
39
|
+
});
|
|
22
40
|
return c.json({
|
|
23
41
|
ok: true,
|
|
24
42
|
cleared
|
|
@@ -2,6 +2,7 @@ import { getWorkspacePath } from "../../../config/workspace-path-helpers.js";
|
|
|
2
2
|
import { isManagedConnectorServer } from "../../../connectors/materialize.js";
|
|
3
3
|
import { loadMergedMcpConfig } from "../../../agent/mcp/config.js";
|
|
4
4
|
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
5
|
+
import { resolveMcpSetupConfig } from "../../../agent/mcp/effective-server.js";
|
|
5
6
|
import { discoverMcpTools } from "../../../agent/mcp/tool-catalog.js";
|
|
6
7
|
import { cancelMcpAuthorization, getMcpAuthorization, probeMcpConnection, startMcpAuthorization, submitMcpRedirect } from "../../../agent/mcp/connection-setup.js";
|
|
7
8
|
import { McpConnectionDrafts, McpDraftError, mcpConfigRevision } from "../../../agent/mcp/connection-drafts.js";
|
|
@@ -66,8 +67,8 @@ function registerMcpRoutes(app, deps) {
|
|
|
66
67
|
});
|
|
67
68
|
}));
|
|
68
69
|
app.post("/api/mcp/servers/:id/probe", deps.strictRateLimitMiddleware, async (c) => {
|
|
69
|
-
const config = deps.service.currentConfig;
|
|
70
70
|
const id = c.req.param("id");
|
|
71
|
+
const config = resolveMcpSetupConfig(deps.service.currentConfig, id);
|
|
71
72
|
if (!Object.hasOwn(config.mcp?.servers ?? {}, id)) return c.json({
|
|
72
73
|
ok: false,
|
|
73
74
|
error: "MCP server not found."
|
|
@@ -79,8 +80,8 @@ function registerMcpRoutes(app, deps) {
|
|
|
79
80
|
});
|
|
80
81
|
});
|
|
81
82
|
app.post("/api/mcp/servers/:id/auth", deps.strictRateLimitMiddleware, async (c) => {
|
|
82
|
-
const config = deps.service.currentConfig;
|
|
83
83
|
const id = c.req.param("id");
|
|
84
|
+
const config = resolveMcpSetupConfig(deps.service.currentConfig, id);
|
|
84
85
|
if (!Object.hasOwn(config.mcp?.servers ?? {}, id)) return c.json({
|
|
85
86
|
ok: false,
|
|
86
87
|
error: "MCP server not found."
|
|
@@ -129,8 +130,8 @@ function registerMcpRoutes(app, deps) {
|
|
|
129
130
|
}, 404);
|
|
130
131
|
});
|
|
131
132
|
app.post("/api/mcp/servers/:id/tools", deps.strictRateLimitMiddleware, async (c) => {
|
|
132
|
-
const cfg = deps.service.currentConfig;
|
|
133
133
|
const id = c.req.param("id");
|
|
134
|
+
const cfg = resolveMcpSetupConfig(deps.service.currentConfig, id);
|
|
134
135
|
if (!Object.hasOwn(cfg.mcp?.servers ?? {}, id)) return c.json({
|
|
135
136
|
ok: false,
|
|
136
137
|
error: "MCP server not found."
|
|
@@ -155,7 +156,8 @@ function registerMcpRoutes(app, deps) {
|
|
|
155
156
|
const cfg = deps.service.currentConfig;
|
|
156
157
|
const merged = loadMergedMcpConfig({
|
|
157
158
|
workspaceDir: getWorkspacePath(cfg) || "./workspace",
|
|
158
|
-
cfg
|
|
159
|
+
cfg,
|
|
160
|
+
scope: "management"
|
|
159
161
|
});
|
|
160
162
|
return c.json({
|
|
161
163
|
ok: true,
|
|
@@ -81,9 +81,6 @@ import "../infra/bus/index.js";
|
|
|
81
81
|
import { collectMediaUrisFromMessages, deleteMediaUris } from "../media/session-references.js";
|
|
82
82
|
import { AgentService } from "../agent/service.js";
|
|
83
83
|
import { bootstrapApplicationStateSync } from "../bootstrap/application-state.js";
|
|
84
|
-
import { setPairingBroadcastSink } from "../channels/pairing/pairing-events.js";
|
|
85
|
-
import { registerClarificationChannelRuntime } from "./clarify-runtime.js";
|
|
86
|
-
import { ChannelManager } from "../channels/manager.js";
|
|
87
84
|
import { ChatPreviewService } from "../chat-previews/service.js";
|
|
88
85
|
import "../chat-previews/index.js";
|
|
89
86
|
import { configureGatewayHttpProxy } from "../infra/http-proxy-env.js";
|
|
@@ -109,6 +106,7 @@ import { HomeIntelligenceHost } from "../home-intelligence/host.js";
|
|
|
109
106
|
import { HomeSnapshotBuilder } from "../home-intelligence/snapshot.js";
|
|
110
107
|
import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
|
|
111
108
|
import { prepareAutomationAgentSession } from "./automation-agent-session.js";
|
|
109
|
+
import { registerClarificationChannelRuntime } from "./clarify-runtime.js";
|
|
112
110
|
import { stopTailscaleExposure } from "./tailscale-lifecycle.js";
|
|
113
111
|
import { getGatewayFileSpaceService } from "./file-space-service.js";
|
|
114
112
|
import { reconcileHomeIntelligenceAutomation } from "./home-intelligence-automation-reconciler.js";
|
|
@@ -122,6 +120,8 @@ import { WorkDiscoveryService } from "../work-discovery/service.js";
|
|
|
122
120
|
import { buildVoiceMemoryContext } from "../voice/realtime/memory-context.js";
|
|
123
121
|
import { buildVoicePersonaContext } from "../voice/realtime/persona-context.js";
|
|
124
122
|
import { voiceMemoryBudget, voicePersonaBudget } from "../voice/realtime/conversation-context.js";
|
|
123
|
+
import { setPairingBroadcastSink } from "../channels/pairing/pairing-events.js";
|
|
124
|
+
import { ChannelManager } from "../channels/manager.js";
|
|
125
125
|
import "../notes/index.js";
|
|
126
126
|
import { buildWorkflowChildTools } from "../agent/workflow/workflow-child-tools.js";
|
|
127
127
|
import { WorkflowRunService } from "../workflows/service/workflow-run-service.js";
|
|
@@ -1572,6 +1572,8 @@ var GatewayService = class {
|
|
|
1572
1572
|
await flushTracing();
|
|
1573
1573
|
if (!this.running) return;
|
|
1574
1574
|
this.stopping = true;
|
|
1575
|
+
const { cancelAllPluginCliAuthorizations } = await import("../extensions/agent-plugins/cli-authorization.js");
|
|
1576
|
+
cancelAllPluginCliAuthorizations();
|
|
1575
1577
|
this.taskAllocationDispatcher?.stop();
|
|
1576
1578
|
this.taskRunDispatcher?.stop();
|
|
1577
1579
|
if (this.taskRunDispatchTimer) {
|