@xopcai/xopc 0.0.233 → 0.0.235
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/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-DQ5L3PTi.js → activity-store-BzswYHcj.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Bps8fvBp.js → agent-avatar-dicebear-dvh9aEm6.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-B9tkmkUj.js +1 -0
- package/dist/gateway/static/root/assets/agents-DTXYo-7z.js +1 -0
- package/dist/gateway/static/root/assets/{api-DE6tZKG0.js → api-7zOtMbaE.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BryzDVod.js → app-management-settings-panel-DXFTk7x6.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BoweLBDm.js → appearance-settings-D-FD5SOq.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BT102W58.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-DyujzZNu.js → archive-plugin-DawToDU8.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DmXGUWve.js → attachment-load-DmBAKvsi.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-DrTVSK7o.js → automations-page-BGkF6XNH.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BRb45-_R.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-mFyeC74W.js → binary-plugins-B-6Ski7l.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-D5yMFrgH.js → block-editor-BgeKQN1F.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-BkRWxC7D.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-B6rLBtua.js → browser-workflow-inputs-4ooVX-Tx.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-C74sg50f.js → browser-workflows-page-D92g81lS.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DfT-vWTA.js +7 -0
- package/dist/gateway/static/root/assets/channels-settings-C9oGpLR0.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-DowVNjNw.js → channels-status-swr-BDJvMnA_.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BO6_BgjE.js → chat-terminal-dock-C_9LUs7G.js} +2 -2
- package/dist/gateway/static/root/assets/connectors-page-SJKV6SHT.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-CYMXfk4M.js → cron-expression-BYmr5ZuN.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-DIM_OkQP.js → date-picker-FKL_b_S0.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-Cp8I6rsL.js → dependency-picker-BP68vqBI.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-wzSzZ6S8.js → desktop-pet-DOXjRTqK.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-NjPiz3M_.js → desktop-pet-settings-CuG2qYoY.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CoMl13ET.js → extension-debug-page-BeJ1-5ID.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CXc1Lc8x.js → extension-page-CncDndLS.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CfEWpgjl.js → extension-settings-page-BxxL0HqU.js} +1 -1
- package/dist/gateway/static/root/assets/{files-Dp8_uGTO.js → files-DQjhZjr2.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-yH4KAEkK.js → gateway-config-api-Dc6A6HyJ.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-YyZJKvFf.js → gateway-config-swr-R702lNJR.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BmjJIiKh.js → gateway-settings-C2XcazGD.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-Dnrl91pU.js → gateway-startup-retry-2rVSUvyw.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-BjUEqkkC.js +1 -0
- package/dist/gateway/static/root/assets/home-page-DTSjwfAQ.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-BNzAAw4Y.js → image-generation-api-B28USRCK.js} +1 -1
- package/dist/gateway/static/root/assets/index-DFfvY8IN.js +70 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CfNV1ysF.js → keyboard-shortcuts-settings-Cwinks_5.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-0wntj9mK.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-MPd4Fl_l.js → local-apps-page-WeKm5vZ0.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-D_dq_0LV.js → locale-store-8k4mvYGL.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DFgJr7Zx.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-DeIjN4e0.js → management-settings-8bEvKO0R.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-BUAVv6Gu.js → markdown-split-B6wQhwLV.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-CPkBOY3h.js → markdown-view-D02mJdoU.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DFsVnOY-.js → media-plugins-BSEPuO76.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-D4qOHT4D.js → messages-CoovNoMn.js} +2 -2
- package/dist/gateway/static/root/assets/{mobile-device-api-D9JevyCE.js → mobile-device-api-CvjVB_TB.js} +1 -1
- package/dist/gateway/static/root/assets/models-hub-cache-CYr6LzvY.js +1 -0
- package/dist/gateway/static/root/assets/{notes-api-BWSfTP1w.js → notes-api-p2Pncp2B.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-CV8UJj7g.js → notes-page-CPgbeO1u.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-DIXCtwhs.js +4 -0
- package/dist/gateway/static/root/assets/{oauth-api-DnanEBsf.js → oauth-api-DpLONwfK.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-zNg1KOy2.js → product-open-page-DNHWn_26.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-6jgRJg4w.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-Bd4nXs4y.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-BeVzfRvs.js → registry-api-B7b0LJgI.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-C_5-u9U-.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BxoioSvT.js → runtime-tools-settings-panel-D3TCJGy3.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-vzbmscrE.js → schema-form-DUAlwNoO.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-BqYAR86S.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-a146I0uZ.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-x9k7MWD3.js → settings-form-section-D7nQ3Lfu.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BLFOD9h9.js → settings-loading-skeleton-CwGM9zKH.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-CwgTooar.js → settings-page-CMAkMc3I.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-rhaERxAL.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-COTrPAd-.js → share-preview-page-BzkXvuH6.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BvsM3Im5.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-u7E0gqFo.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-D9wM6_Jn.js → skills-page-DJRr5HFw.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-PtlQLM5a.js → skills-page.utils-DcrBe39q.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BR74mNEK.js → system-settings-panel-BUsykmjx.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-C9QgWfYG.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-Bud54q9e.js → theme-store-CpcBI73Q.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-DH47MH7s.js → use-autosave-DDEypreQ.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-Ch0cfQfI.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-B59GaqRa.js → voice-api-key-field-BLa_vdXE.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-C0ahrZ0e.js → voice-config-api-ynE-vY6I.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-DTn4RCcx.js → work-discovery-overlay-DS3w1Fyq.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-yDFoEX06.js → workflow-run-setup-panel-jQl_wmYC.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DyTwkdzs.js +5 -0
- package/dist/gateway/static/root/index.html +13 -13
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +4 -4
- package/dist/src/agent/background-review/run-background-review.js +1 -1
- package/dist/src/agent/embedded/model-runtime.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +57 -8
- package/dist/src/agent/embedded/types.d.ts +5 -0
- package/dist/src/agent/image/tool-model-config.js +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +1 -1
- package/dist/src/agent/service.js +2 -2
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/tasks/task-judge-service.js +3 -3
- package/dist/src/agent/tools/create-share-tool.js +1 -1
- package/dist/src/agent/tools/exec-command.d.ts +2 -0
- package/dist/src/agent/tools/exec-command.js +23 -7
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/index.js +1 -1
- package/dist/src/agent/tools/publish-artifacts.d.ts +6 -0
- package/dist/src/agent/tools/publish-artifacts.js +55 -42
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/tools/xopc-use-tool.js +4 -4
- package/dist/src/ai-assist/text-assist.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +2 -2
- package/dist/src/browser/providers/browser-ext-install.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/provider-auth.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/models.js +2 -2
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/config/index.js +4 -4
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/sdk/index.js +1 -1
- package/dist/src/files/file-service.d.ts +10 -2
- package/dist/src/files/file-service.js +33 -12
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +5 -0
- package/dist/src/gateway/file-path-classifier.d.ts +30 -0
- package/dist/src/gateway/file-path-classifier.js +105 -0
- package/dist/src/gateway/file-reference-errors.d.ts +6 -0
- package/dist/src/gateway/file-reference-errors.js +21 -0
- package/dist/src/gateway/file-reference-registry.d.ts +34 -0
- package/dist/src/gateway/file-reference-registry.js +45 -0
- package/dist/src/gateway/file-space-service.d.ts +4 -0
- package/dist/src/gateway/file-space-service.js +19 -0
- package/dist/src/gateway/hono/app.js +3 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/routes/connectors.js +1 -1
- package/dist/src/gateway/hono/routes/file-references.d.ts +4 -0
- package/dist/src/gateway/hono/routes/file-references.js +98 -0
- package/dist/src/gateway/hono/routes/files.js +39 -17
- package/dist/src/gateway/hono/routes/html-preview.d.ts +4 -0
- package/dist/src/gateway/hono/routes/html-preview.js +45 -0
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/shares.js +18 -10
- package/dist/src/gateway/hono/routes/tasks.js +3 -3
- package/dist/src/gateway/security/csp.d.ts +1 -1
- package/dist/src/gateway/security/csp.js +2 -2
- package/dist/src/gateway/service/config-coordinator.js +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +0 -35
- package/dist/src/gateway/service.js +8 -8
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/register-runtime-providers.js +1 -1
- package/dist/src/session/store.js +4 -2
- package/dist/src/session/turn-outcome-projector.d.ts +9 -0
- package/dist/src/session/turn-outcome-projector.js +209 -0
- package/dist/src/share/hosted-session-share.js +1 -1
- package/dist/src/storage/sqlite/config-repository.d.ts +5 -0
- package/dist/src/storage/sqlite/config-repository.js +8 -1
- package/dist/src/tasks/home-query-service.js +2 -2
- package/dist/src/tasks/task-signal-service.js +1 -1
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +2 -2
- package/package.json +4 -4
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-JrK80PvP.js +0 -1
- package/dist/gateway/static/root/assets/agents-CJYEnz94.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-Bt8_sWtU.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-86UTesz3.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-DfMU7yXv.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-lsHRuBlX.js +0 -7
- package/dist/gateway/static/root/assets/channels-settings-CxR_clkO.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CpNLyKB4.js +0 -2
- package/dist/gateway/static/root/assets/heartbeat-settings-DGq9q4b6.js +0 -1
- package/dist/gateway/static/root/assets/home-page-Dj4B1wb3.js +0 -1
- package/dist/gateway/static/root/assets/index-BGf-TiZj.js +0 -70
- package/dist/gateway/static/root/assets/local-app-workbench-page-IJg75WaN.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-NsfoazgK.js +0 -2
- package/dist/gateway/static/root/assets/models-hub-cache-C5y2gzMU.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-DrjMVgEI.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-tfcGYL-O.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BkjnXLdJ.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BXn-3n2d.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-MLNjrOkw.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-ByNLBY2q.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-bnueBZg8.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-D-v_psqL.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DJICr4wh.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-B-rmeE8t.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-B6eyxxOT.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-BOwSbfWm.js +0 -5
- package/dist/src/gateway/chat-stream/turn-outcome-collector.d.ts +0 -16
- package/dist/src/gateway/chat-stream/turn-outcome-collector.js +0 -146
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
//#region src/gateway/file-reference-registry.ts
|
|
3
|
+
const DEFAULT_TTL_MS = 1800 * 1e3;
|
|
4
|
+
var FileReferenceRegistry = class {
|
|
5
|
+
refs = /* @__PURE__ */ new Map();
|
|
6
|
+
register(input) {
|
|
7
|
+
this.expire();
|
|
8
|
+
const id = randomUUID();
|
|
9
|
+
const ref = {
|
|
10
|
+
id,
|
|
11
|
+
absolutePath: input.absolutePath,
|
|
12
|
+
sessionKey: input.sessionKey?.trim() || void 0,
|
|
13
|
+
scope: input.scope,
|
|
14
|
+
locationKind: input.locationKind,
|
|
15
|
+
capabilities: [...new Set(input.capabilities)],
|
|
16
|
+
expiresAt: Date.now() + (input.ttlMs ?? DEFAULT_TTL_MS)
|
|
17
|
+
};
|
|
18
|
+
this.refs.set(id, ref);
|
|
19
|
+
return ref;
|
|
20
|
+
}
|
|
21
|
+
resolve(id) {
|
|
22
|
+
this.expire();
|
|
23
|
+
const ref = this.refs.get(id);
|
|
24
|
+
if (!ref) return null;
|
|
25
|
+
if (ref.expiresAt <= Date.now()) {
|
|
26
|
+
this.refs.delete(id);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return ref;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Remove a specific reference (used after a one-shot action like
|
|
33
|
+
* import-to-workspace completes so the source card can't be re-triggered).
|
|
34
|
+
* Returns true if the reference existed and was removed.
|
|
35
|
+
*/
|
|
36
|
+
expireById(id) {
|
|
37
|
+
return this.refs.delete(id);
|
|
38
|
+
}
|
|
39
|
+
expire(now = Date.now()) {
|
|
40
|
+
for (const [id, ref] of this.refs) if (ref.expiresAt <= now) this.refs.delete(id);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const fileReferenceRegistry = new FileReferenceRegistry();
|
|
44
|
+
//#endregion
|
|
45
|
+
export { FileReferenceRegistry, fileReferenceRegistry };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { FileSpaceService } from '../files/file-service.js';
|
|
2
|
+
import type { GatewayService } from './service.js';
|
|
3
|
+
/** Share the file registry, including session-specific spaces, across gateway routes. */
|
|
4
|
+
export declare function getGatewayFileSpaceService(service: GatewayService): FileSpaceService;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { listSessionWorkspaceOverrides } from "../storage/sqlite/config-repository.js";
|
|
2
|
+
import { effectiveWorkspacePathForSession } from "../session/session-workspace.js";
|
|
3
|
+
import { getProjectForSession } from "../projects/workspace.js";
|
|
4
|
+
import { FileSpaceService } from "../files/file-service.js";
|
|
5
|
+
//#region src/gateway/file-space-service.ts
|
|
6
|
+
const services = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
/** Share the file registry, including session-specific spaces, across gateway routes. */
|
|
8
|
+
function getGatewayFileSpaceService(service) {
|
|
9
|
+
const existing = services.get(service);
|
|
10
|
+
if (existing) return existing;
|
|
11
|
+
const created = new FileSpaceService(() => service.currentConfig, service.projects, (sessionKey) => service.sessions.getEffectiveWorkspacePath(sessionKey), () => listSessionWorkspaceOverrides().map(({ sessionKey, workingDirectoryOverride }) => ({
|
|
12
|
+
sessionKey,
|
|
13
|
+
root: effectiveWorkspacePathForSession(service.currentConfig, sessionKey, { workingDirectoryOverride }, getProjectForSession(sessionKey))
|
|
14
|
+
})));
|
|
15
|
+
services.set(service, created);
|
|
16
|
+
return created;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { getGatewayFileSpaceService };
|
|
@@ -21,6 +21,7 @@ import { resetLazyRouteBundlesForTests } from "./routes/lazy-fallback.js";
|
|
|
21
21
|
import { registerAuthenticatedRoutes } from "./routes/index.js";
|
|
22
22
|
import { prewarmStaticUiCache } from "./lib/static-ui.js";
|
|
23
23
|
import { registerPublicGatewayRoutes } from "./routes/public-gateway.js";
|
|
24
|
+
import { registerPublicHtmlPreviewRoute } from "./routes/html-preview.js";
|
|
24
25
|
import { registerSiteShareMiddleware } from "../../share/site-share-router.js";
|
|
25
26
|
import { Hono } from "hono";
|
|
26
27
|
import { cors } from "hono/cors";
|
|
@@ -109,7 +110,7 @@ function createHonoApp(config) {
|
|
|
109
110
|
const gatewayConsoleCsp = buildGatewayConsoleCspHeader();
|
|
110
111
|
app.use(createMiddleware(async (c, next) => {
|
|
111
112
|
await next();
|
|
112
|
-
if (isExtensionGatewayUiAssetPath(c.req.path)) return;
|
|
113
|
+
if (isExtensionGatewayUiAssetPath(c.req.path) || c.req.path === "/api/preview/html" && (c.req.method === "GET" || c.req.method === "HEAD")) return;
|
|
113
114
|
c.header("X-Frame-Options", "DENY");
|
|
114
115
|
c.header("X-Content-Type-Options", "nosniff");
|
|
115
116
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -194,6 +195,7 @@ function createHonoApp(config) {
|
|
|
194
195
|
})(c, next);
|
|
195
196
|
});
|
|
196
197
|
registerPublicGatewayRoutes(app, service);
|
|
198
|
+
registerPublicHtmlPreviewRoute(app);
|
|
197
199
|
registerDeviceAuthPublicRoutes(app);
|
|
198
200
|
registerPublicExtensionAssetRoutes(app, service);
|
|
199
201
|
registerPublicLocalAppPreviewRoutes(app, service);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ContextCompactionPolicySchema, UserContextDreamingSchema, UserContextPrivacySchema, init_config } from "../../../user-context/config.js";
|
|
2
2
|
import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
|
|
3
3
|
import { resolveModelsJsonPath } from "../../../config/paths.js";
|
|
4
|
-
import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, getChannelSetupStatus, init_channel_catalog_service } from "../../../channels/catalog/channel-catalog-service.js";
|
|
5
4
|
import { init_models_json, loadModelsJson } from "../../../config/models-json.js";
|
|
6
5
|
import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
|
|
6
|
+
import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, getChannelSetupStatus, init_channel_catalog_service } from "../../../channels/catalog/channel-catalog-service.js";
|
|
7
7
|
import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
|
|
8
8
|
import { getAllProviders, init_providers, isProviderConfigured } from "../../../providers/index.js";
|
|
9
9
|
import { normalizeConfiguredMcpServers } from "../../../config/mcp-config-normalize.js";
|
|
@@ -15,7 +15,6 @@ import { listUnderstandingSourceGrants, revokeUnderstandingSourceGrant } from ".
|
|
|
15
15
|
import { recordConnectorHealthUsage } from "../../../connectors/usage.js";
|
|
16
16
|
import { getConnectorInstance, getInstalledConnectorDefinition, listConnectorInstances } from "../../../connectors/instances.js";
|
|
17
17
|
import { installConnector, installConnectorDefinition, uninstallConnector, updateConnectorConfig } from "../../../connectors/install.js";
|
|
18
|
-
import { ConfigPersistenceError, persistConfigMutation } from "../../../config/config-mutation.js";
|
|
19
18
|
import { getMcpOAuthManager } from "../../../agent/mcp/oauth/mcp-oauth-manager.js";
|
|
20
19
|
import { startConnectorAuthorization } from "../../../connectors/auth-provider-registry.js";
|
|
21
20
|
import { composioLogoResponse } from "../../../connectors/composio-logo.js";
|
|
@@ -25,6 +24,7 @@ import { ingestLocalFolderSource } from "../../../connectors/connected-source-in
|
|
|
25
24
|
import { previewConnectorDefinition, testConnectorInstance } from "../../../connectors/health.js";
|
|
26
25
|
import { setConnectorEnabled } from "../../../connectors/lifecycle.js";
|
|
27
26
|
import { projectComposioConnectionStatus } from "../../../connectors/runtime-status.js";
|
|
27
|
+
import { ConfigPersistenceError, persistConfigMutation } from "../../../config/config-mutation.js";
|
|
28
28
|
import { applySourceRevocationChoices, sourceRevocationImpact } from "./understanding-sources.js";
|
|
29
29
|
//#region src/gateway/hono/routes/connectors.ts
|
|
30
30
|
init_agent_scope();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { AuthenticatedRouteDeps } from './deps.js';
|
|
3
|
+
/** Metadata and explicit desktop actions for links outside a managed workspace. */
|
|
4
|
+
export declare function registerFileReferenceRoutes(app: Hono, deps: AuthenticatedRouteDeps): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { FileServiceError } from "../../../files/file-service.js";
|
|
2
|
+
import { buildFilePathClassifierContext, classifyFileLocation, displayNameForPath, fileRefSessionKeysMatch, resolveFileReferenceCandidate } from "../../file-path-classifier.js";
|
|
3
|
+
import { classifyFileReferenceFsError } from "../../file-reference-errors.js";
|
|
4
|
+
import { FileReferenceRegistry } from "../../file-reference-registry.js";
|
|
5
|
+
import { getGatewayFileSpaceService } from "../../file-space-service.js";
|
|
6
|
+
import { realpath, stat } from "node:fs/promises";
|
|
7
|
+
//#region src/gateway/hono/routes/file-references.ts
|
|
8
|
+
/** Metadata and explicit desktop actions for links outside a managed workspace. */
|
|
9
|
+
function registerFileReferenceRoutes(app, deps) {
|
|
10
|
+
const files = getGatewayFileSpaceService(deps.service);
|
|
11
|
+
const refs = new FileReferenceRegistry();
|
|
12
|
+
app.post("/api/files/resolve-reference", async (c) => {
|
|
13
|
+
const body = await c.req.json();
|
|
14
|
+
if (!body.spaceId || !body.path?.trim()) return c.json({ error: { message: "spaceId and path are required" } }, 400);
|
|
15
|
+
try {
|
|
16
|
+
const space = await files.get(body.spaceId);
|
|
17
|
+
const sessionKey = body.sessionKey?.trim();
|
|
18
|
+
const agentId = space.bindings.find((binding) => binding.kind === "agent")?.id;
|
|
19
|
+
const ctx = {
|
|
20
|
+
...buildFilePathClassifierContext(deps.service.currentConfig, sessionKey || (agentId ? `agent:${agentId}:main` : void 0)),
|
|
21
|
+
workspaceRoot: space.root
|
|
22
|
+
};
|
|
23
|
+
const inputPath = body.path.trim();
|
|
24
|
+
const base = {
|
|
25
|
+
inputPath,
|
|
26
|
+
displayName: displayNameForPath(inputPath)
|
|
27
|
+
};
|
|
28
|
+
const { candidate, invalid } = await resolveFileReferenceCandidate(inputPath, space.root, ctx);
|
|
29
|
+
if (!candidate || invalid) return c.json({ reference: {
|
|
30
|
+
...base,
|
|
31
|
+
scope: "invalid",
|
|
32
|
+
exists: false,
|
|
33
|
+
capabilities: []
|
|
34
|
+
} });
|
|
35
|
+
let absolutePath;
|
|
36
|
+
let info;
|
|
37
|
+
try {
|
|
38
|
+
absolutePath = await realpath(candidate);
|
|
39
|
+
info = await stat(absolutePath);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
const failure = classifyFileReferenceFsError(error);
|
|
42
|
+
if (failure.code !== "FILE_NOT_FOUND") return c.json({ error: { message: failure.message } }, failure.status === 403 ? 403 : 500);
|
|
43
|
+
return c.json({ reference: {
|
|
44
|
+
...base,
|
|
45
|
+
scope: "missing",
|
|
46
|
+
absolutePath: candidate,
|
|
47
|
+
exists: false,
|
|
48
|
+
capabilities: ["copyPath"]
|
|
49
|
+
} });
|
|
50
|
+
}
|
|
51
|
+
const classified = classifyFileLocation(absolutePath, ctx);
|
|
52
|
+
const capabilities = info.isFile() || info.isDirectory() ? [
|
|
53
|
+
"openExternal",
|
|
54
|
+
"revealInFolder",
|
|
55
|
+
"copyPath"
|
|
56
|
+
] : ["copyPath"];
|
|
57
|
+
const ref = refs.register({
|
|
58
|
+
absolutePath,
|
|
59
|
+
sessionKey,
|
|
60
|
+
...classified,
|
|
61
|
+
capabilities
|
|
62
|
+
});
|
|
63
|
+
return c.json({ reference: {
|
|
64
|
+
...base,
|
|
65
|
+
...classified,
|
|
66
|
+
fileRefId: ref.id,
|
|
67
|
+
absolutePath,
|
|
68
|
+
exists: true,
|
|
69
|
+
isDirectory: info.isDirectory(),
|
|
70
|
+
capabilities,
|
|
71
|
+
mtimeMs: info.mtimeMs
|
|
72
|
+
} });
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error instanceof FileServiceError) return c.json({ error: { message: error.message } }, error.status);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
app.post("/api/files/references/:id/action", async (c) => {
|
|
79
|
+
const ref = refs.resolve(c.req.param("id"));
|
|
80
|
+
if (!ref) return c.json({ error: { message: "File reference expired" } }, 404);
|
|
81
|
+
const body = await c.req.json();
|
|
82
|
+
if (!fileRefSessionKeysMatch(ref.sessionKey, body.sessionKey)) return c.json({ error: { message: "File reference forbidden" } }, 403);
|
|
83
|
+
if (body.action !== "openExternal" && body.action !== "revealInFolder" || !ref.capabilities.includes(body.action)) return c.json({ error: { message: "Action not allowed" } }, 403);
|
|
84
|
+
try {
|
|
85
|
+
if (await realpath(ref.absolutePath) !== ref.absolutePath) return c.json({ error: { message: "File reference changed" } }, 409);
|
|
86
|
+
const info = await stat(ref.absolutePath);
|
|
87
|
+
if (!info.isFile() && !info.isDirectory()) return c.json({ error: { message: "Not a file or directory" } }, 400);
|
|
88
|
+
return c.json({
|
|
89
|
+
absolutePath: ref.absolutePath,
|
|
90
|
+
isDirectory: info.isDirectory()
|
|
91
|
+
});
|
|
92
|
+
} catch {
|
|
93
|
+
return c.json({ error: { message: "File not found" } }, 404);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { registerFileReferenceRoutes };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { FileServiceError,
|
|
1
|
+
import { FileServiceError, fileResourceFromPath, resolveFilePath } from "../../../files/file-service.js";
|
|
2
|
+
import { getGatewayFileSpaceService } from "../../file-space-service.js";
|
|
3
|
+
import { fuzzySearchWorkspaceFiles, fuzzySubsequenceScore } from "../../workspace-file-search.js";
|
|
4
|
+
import { registerFileReferenceRoutes } from "./file-references.js";
|
|
2
5
|
import { basename, join } from "node:path";
|
|
3
6
|
import { readFile, writeFile } from "node:fs/promises";
|
|
4
7
|
//#region src/gateway/hono/routes/files.ts
|
|
5
|
-
const services = /* @__PURE__ */ new WeakMap();
|
|
6
8
|
const mutationLocks = /* @__PURE__ */ new Map();
|
|
7
9
|
async function withMutationLock(key, action) {
|
|
8
10
|
const previous = mutationLocks.get(key) ?? Promise.resolve();
|
|
@@ -20,13 +22,6 @@ async function withMutationLock(key, action) {
|
|
|
20
22
|
if (mutationLocks.get(key) === tail) mutationLocks.delete(key);
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
|
-
function getFiles(deps) {
|
|
24
|
-
const existing = services.get(deps.service);
|
|
25
|
-
if (existing) return existing;
|
|
26
|
-
const created = new FileSpaceService(() => deps.service.currentConfig, deps.service.projects, (sessionKey) => deps.service.agentService.getResolvedWorkspaceForSession(sessionKey));
|
|
27
|
-
services.set(deps.service, created);
|
|
28
|
-
return created;
|
|
29
|
-
}
|
|
30
25
|
function publicSpace(space) {
|
|
31
26
|
const { root: _root, ...value } = space;
|
|
32
27
|
return value;
|
|
@@ -39,22 +34,28 @@ function contentDisposition(disposition, fileName) {
|
|
|
39
34
|
const sanitized = fileName.replace(/[\u0000-\u001f\u007f]/g, "_");
|
|
40
35
|
return `${disposition}; filename="${sanitized.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_")}"; filename*=UTF-8''${encodeURIComponent(sanitized).replace(/['()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`)}`;
|
|
41
36
|
}
|
|
42
|
-
async function collectFiles(files, spaceId,
|
|
37
|
+
async function collectFiles(files, spaceId, max) {
|
|
43
38
|
const output = [];
|
|
44
39
|
const directories = [""];
|
|
40
|
+
const visited = /* @__PURE__ */ new Set();
|
|
41
|
+
const space = await files.get(spaceId);
|
|
45
42
|
while (directories.length && output.length < max) {
|
|
46
43
|
const directory = directories.shift();
|
|
44
|
+
const canonical = await resolveFilePath(space.root, directory).catch(() => null);
|
|
45
|
+
if (!canonical || visited.has(canonical)) continue;
|
|
46
|
+
visited.add(canonical);
|
|
47
47
|
const children = await files.children(spaceId, directory);
|
|
48
48
|
for (const child of children) {
|
|
49
49
|
if (child.kind === "directory") directories.push(child.relativePath);
|
|
50
|
-
else
|
|
50
|
+
else output.push(child);
|
|
51
51
|
if (output.length >= max) break;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
return output;
|
|
55
55
|
}
|
|
56
56
|
function registerFilesRoutes(authenticated, deps) {
|
|
57
|
-
const files =
|
|
57
|
+
const files = getGatewayFileSpaceService(deps.service);
|
|
58
|
+
registerFileReferenceRoutes(authenticated, deps);
|
|
58
59
|
authenticated.get("/api/files/spaces", async (c) => {
|
|
59
60
|
try {
|
|
60
61
|
return c.json({ spaces: (await files.list()).map(publicSpace) });
|
|
@@ -62,6 +63,13 @@ function registerFilesRoutes(authenticated, deps) {
|
|
|
62
63
|
return errorResponse(c, error);
|
|
63
64
|
}
|
|
64
65
|
});
|
|
66
|
+
authenticated.get("/api/files/default-space", async (c) => {
|
|
67
|
+
try {
|
|
68
|
+
return c.json({ space: publicSpace(await files.defaultSpace()) });
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return errorResponse(c, error);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
65
73
|
authenticated.get("/api/files/contexts/:kind/:id", async (c) => {
|
|
66
74
|
try {
|
|
67
75
|
const kind = c.req.param("kind");
|
|
@@ -82,7 +90,7 @@ function registerFilesRoutes(authenticated, deps) {
|
|
|
82
90
|
try {
|
|
83
91
|
const limit = Math.min(100, Math.max(1, Number(c.req.query("limit")) || 50));
|
|
84
92
|
const spaces = await files.list();
|
|
85
|
-
const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id,
|
|
93
|
+
const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id, 5e3)))).flat().sort((a, b) => b.modifiedAt - a.modifiedAt).slice(0, limit);
|
|
86
94
|
return c.json({ items });
|
|
87
95
|
} catch (error) {
|
|
88
96
|
return errorResponse(c, error);
|
|
@@ -91,11 +99,23 @@ function registerFilesRoutes(authenticated, deps) {
|
|
|
91
99
|
authenticated.get("/api/files/search", async (c) => {
|
|
92
100
|
try {
|
|
93
101
|
const query = (c.req.query("q") ?? "").trim().toLocaleLowerCase();
|
|
94
|
-
if (!query) return c.json({ items: [] });
|
|
95
102
|
const limit = Math.min(100, Math.max(1, Number(c.req.query("limit")) || 50));
|
|
96
103
|
const requestedSpaceId = c.req.query("spaceId");
|
|
97
104
|
const spaces = requestedSpaceId ? [await files.get(requestedSpaceId)] : await files.list();
|
|
98
|
-
const items = (await Promise.all(spaces.map(
|
|
105
|
+
const items = (await Promise.all(spaces.map(async (space) => {
|
|
106
|
+
const candidates = await fuzzySearchWorkspaceFiles(space.root, query, limit);
|
|
107
|
+
return Promise.all(candidates.map(async (candidate) => {
|
|
108
|
+
try {
|
|
109
|
+
return await fileResourceFromPath(space, await resolveFilePath(space.root, candidate.path));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (error instanceof FileServiceError) return null;
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}));
|
|
115
|
+
}))).flat().filter((file) => file !== null).map((file) => ({
|
|
116
|
+
file,
|
|
117
|
+
score: Math.max(fuzzySubsequenceScore(query, file.relativePath) ?? -Infinity, fuzzySubsequenceScore(query, file.name) ?? -Infinity)
|
|
118
|
+
})).sort((a, b) => b.score - a.score || a.file.relativePath.localeCompare(b.file.relativePath)).slice(0, limit).map((row) => row.file);
|
|
99
119
|
return c.json({ items });
|
|
100
120
|
} catch (error) {
|
|
101
121
|
return errorResponse(c, error);
|
|
@@ -144,12 +164,14 @@ function registerFilesRoutes(authenticated, deps) {
|
|
|
144
164
|
const body = await c.req.json();
|
|
145
165
|
if (typeof body.content !== "string" || !body.revision) throw new FileServiceError(400, "content and revision are required");
|
|
146
166
|
const id = c.req.param("id");
|
|
147
|
-
const
|
|
167
|
+
const initial = await files.resource(id);
|
|
168
|
+
const resource = await withMutationLock(initial.absolutePath, async () => {
|
|
148
169
|
const current = await files.resource(id);
|
|
170
|
+
if (current.absolutePath !== initial.absolutePath) throw new FileServiceError(409, "File has changed");
|
|
149
171
|
if (!current.resource.capabilities.includes("edit")) throw new FileServiceError(403, "File is not editable");
|
|
150
172
|
if (body.revision !== current.resource.revision) throw new FileServiceError(409, "File has changed");
|
|
151
173
|
await writeFile(current.absolutePath, body.content, "utf8");
|
|
152
|
-
return
|
|
174
|
+
return (await files.resource(id)).resource;
|
|
153
175
|
});
|
|
154
176
|
return c.json({ resource });
|
|
155
177
|
} catch (error) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/gateway/hono/routes/html-preview.ts
|
|
2
|
+
const HTML_PREVIEW_PATH = "/api/preview/html";
|
|
3
|
+
const HTML_PREVIEW_SANDBOX = "allow-scripts allow-popups allow-popups-to-escape-sandbox allow-downloads allow-forms allow-modals";
|
|
4
|
+
const HTML_PREVIEW_CSP = [
|
|
5
|
+
"default-src 'none'",
|
|
6
|
+
`sandbox ${HTML_PREVIEW_SANDBOX}`,
|
|
7
|
+
"script-src 'unsafe-inline' https: blob:",
|
|
8
|
+
"style-src 'unsafe-inline' https:",
|
|
9
|
+
"img-src https: data: blob:",
|
|
10
|
+
"media-src https: data: blob:",
|
|
11
|
+
"font-src https: data:",
|
|
12
|
+
"connect-src https:",
|
|
13
|
+
"frame-src 'self' about:",
|
|
14
|
+
"frame-ancestors 'self'",
|
|
15
|
+
"base-uri 'none'",
|
|
16
|
+
"object-src 'none'",
|
|
17
|
+
"form-action https:"
|
|
18
|
+
].join("; ");
|
|
19
|
+
const HTML_PREVIEW_SHELL = `<!doctype html>
|
|
20
|
+
<html><head><meta charset="utf-8"><title>HTML preview</title>
|
|
21
|
+
<style>html,body{height:100%;margin:0;overflow:hidden}iframe{display:block;width:100%;height:100%;border:0}</style>
|
|
22
|
+
</head><body><iframe title="HTML preview" sandbox="${HTML_PREVIEW_SANDBOX}"></iframe>
|
|
23
|
+
<script>
|
|
24
|
+
const preview = document.querySelector('iframe');
|
|
25
|
+
window.addEventListener('message', (event) => {
|
|
26
|
+
if (event.source !== window.parent || window.parent === window) return;
|
|
27
|
+
const data = event.data;
|
|
28
|
+
if (!data || data.type !== 'xopc-html-preview' || typeof data.html !== 'string') return;
|
|
29
|
+
// Repeated load notifications must not restart scripts or discard preview state.
|
|
30
|
+
if (preview.srcdoc !== data.html) preview.srcdoc = data.html;
|
|
31
|
+
});
|
|
32
|
+
<\/script></body></html>`;
|
|
33
|
+
/** Public static shell only: file contents arrive from the authenticated UI, never a URL. */
|
|
34
|
+
function registerPublicHtmlPreviewRoute(app) {
|
|
35
|
+
app.get(HTML_PREVIEW_PATH, (c) => c.html(HTML_PREVIEW_SHELL, 200, {
|
|
36
|
+
"Content-Security-Policy": HTML_PREVIEW_CSP,
|
|
37
|
+
"X-Frame-Options": "SAMEORIGIN",
|
|
38
|
+
"X-Content-Type-Options": "nosniff",
|
|
39
|
+
"Referrer-Policy": "no-referrer",
|
|
40
|
+
"Permissions-Policy": "camera=(), microphone=(), geolocation=()",
|
|
41
|
+
"Cache-Control": "no-store"
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { HTML_PREVIEW_PATH, registerPublicHtmlPreviewRoute };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolveModelsJsonPath } from "../../../config/paths.js";
|
|
2
1
|
import { init_resolve_config_value, testApiKeyResolution } from "../../../config/resolve-config-value.js";
|
|
2
|
+
import { resolveModelsJsonPath } from "../../../config/paths.js";
|
|
3
3
|
import { init_models_json, loadModelsJson, saveModelsJson, validateModelsConfig } from "../../../config/models-json.js";
|
|
4
4
|
import { getModelCatalogStore, init_model_catalog_store } from "../../../providers/model-catalog-store.js";
|
|
5
5
|
import { getModelRegistry } from "../../../providers/model-registry.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadCompactionSourceSnapshot } from "../../../storage/sqlite/transcript-repository.js";
|
|
2
2
|
import "../../../storage/sqlite/index.js";
|
|
3
|
+
import { FileServiceError } from "../../../files/file-service.js";
|
|
3
4
|
import { resolveGatewayEffectiveHost } from "../../../config/gateway-bind.js";
|
|
4
5
|
import { SHARE_CONFIG_DEFAULTS } from "../../../share/share-types.js";
|
|
5
6
|
import { resolveSiteShareConfig } from "../../../share/site-share-config.js";
|
|
@@ -11,6 +12,7 @@ import { resolveReverseProxyPublicUrl } from "../../public-url.js";
|
|
|
11
12
|
import { audienceDefaults, cleanupStagedSite, decideShareKind, forgetStagedSite, makeDescription, makeTitle, probeShareTarget, rememberStagedSite, stageSingleHtmlAsSite } from "../../../share/share-auto.js";
|
|
12
13
|
import { deleteThumbnail, placeholderSvg, readThumbnail, scheduleThumbnail, thumbnailContentType, thumbnailExists } from "../../../share/share-thumbnail.js";
|
|
13
14
|
import { extractToken } from "../../auth.js";
|
|
15
|
+
import { getGatewayFileSpaceService } from "../../file-space-service.js";
|
|
14
16
|
import { getClientIpFromHeaders } from "../../security/loopback.js";
|
|
15
17
|
import { renderFolderLandingPage, renderShareExpiredPage, renderShareLandingPage, renderSnapshotShareLandingPage } from "../../../share/share-landing.js";
|
|
16
18
|
import { SessionShareService, SessionShareSnapshotConflictError } from "../../../share/session-share-service.js";
|
|
@@ -902,10 +904,11 @@ function registerShareRoutes(authenticated, deps) {
|
|
|
902
904
|
error: { message: "Invalid JSON" }
|
|
903
905
|
}, 400);
|
|
904
906
|
}
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
+
const fileId = typeof body.fileId === "string" ? body.fileId.trim() : "";
|
|
908
|
+
let path = typeof body.path === "string" ? body.path.trim() : "";
|
|
909
|
+
if (!fileId && !path) return c.json({
|
|
907
910
|
ok: false,
|
|
908
|
-
error: { message: "Missing path" }
|
|
911
|
+
error: { message: "Missing path or fileId" }
|
|
909
912
|
}, 400);
|
|
910
913
|
const sessionKey = typeof body.sessionKey === "string" ? body.sessionKey.trim() : void 0;
|
|
911
914
|
const agentId = typeof body.agentId === "string" ? body.agentId.trim() : void 0;
|
|
@@ -921,20 +924,25 @@ function registerShareRoutes(authenticated, deps) {
|
|
|
921
924
|
const ttlOverride = typeof body.ttlMs === "number" ? body.ttlMs : void 0;
|
|
922
925
|
const maxViewsOverride = body.maxViews === null ? null : typeof body.maxViews === "number" ? body.maxViews : void 0;
|
|
923
926
|
const wantThumbnail = body.thumbnail !== false;
|
|
924
|
-
|
|
925
|
-
if (!workspaceRoot) return c.json({
|
|
926
|
-
ok: false,
|
|
927
|
-
error: { message: "Workspace not configured" }
|
|
928
|
-
}, 400);
|
|
927
|
+
let workspaceRoot;
|
|
929
928
|
let probe;
|
|
930
929
|
try {
|
|
930
|
+
if (fileId) {
|
|
931
|
+
const target = await getGatewayFileSpaceService(service).resource(fileId);
|
|
932
|
+
workspaceRoot = target.space.root;
|
|
933
|
+
path = target.resource.relativePath || ".";
|
|
934
|
+
} else workspaceRoot = await resolveWorkspaceRootForShare(service, sessionKey, agentId);
|
|
935
|
+
if (!workspaceRoot) return c.json({
|
|
936
|
+
ok: false,
|
|
937
|
+
error: { message: "Workspace not configured" }
|
|
938
|
+
}, 400);
|
|
931
939
|
probe = await probeShareTarget(workspaceRoot, path);
|
|
932
940
|
} catch (err) {
|
|
933
941
|
const message = err instanceof Error ? err.message : String(err);
|
|
934
942
|
return c.json({
|
|
935
943
|
ok: false,
|
|
936
944
|
error: { message }
|
|
937
|
-
}, 400);
|
|
945
|
+
}, err instanceof FileServiceError ? err.status : 400);
|
|
938
946
|
}
|
|
939
947
|
let decision;
|
|
940
948
|
try {
|
|
@@ -1438,7 +1446,7 @@ async function createFileShareResponse(args) {
|
|
|
1438
1446
|
}
|
|
1439
1447
|
function relPathFromAbs(workspaceRoot, abs) {
|
|
1440
1448
|
const root = workspaceRoot.replace(/[\\/]+$/, "");
|
|
1441
|
-
if (abs === root) return "";
|
|
1449
|
+
if (abs === root) return ".";
|
|
1442
1450
|
if (abs.startsWith(`${root}/`)) return abs.slice(root.length + 1);
|
|
1443
1451
|
if (abs.startsWith(`${root}\\`)) return abs.slice(root.length + 1).replace(/\\/g, "/");
|
|
1444
1452
|
return abs.split(/[\\/]/).pop() ?? abs;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { runSqliteWriteTransaction } from "../../../storage/sqlite/transaction.js";
|
|
2
|
+
import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
|
|
3
|
+
import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskHandoffRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
|
|
4
|
+
import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
|
|
2
5
|
import { TaskRepository } from "../../../tasks/task-repository.js";
|
|
3
6
|
import { TaskConversationRepository } from "../../../tasks/task-conversation-repository.js";
|
|
4
7
|
import { enqueueTaskChangedEvent } from "../../../tasks/task-change-events.js";
|
|
5
8
|
import { TaskRunRepository } from "../../../tasks/task-run-repository.js";
|
|
6
9
|
import { TaskReadModelProjector } from "../../../tasks/task-read-model-projector.js";
|
|
7
|
-
import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
|
|
8
|
-
import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskHandoffRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
|
|
9
|
-
import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
|
|
10
10
|
import { resolveProjectAgentId } from "../../../projects/project-agent.js";
|
|
11
11
|
import { TaskContextRepository } from "../../../tasks/task-context-repository.js";
|
|
12
12
|
import { TaskDependencyError, TaskDependencyService } from "../../../tasks/task-dependency-service.js";
|
|
@@ -11,7 +11,7 @@ export declare function computeInlineScriptHashes(html: string): string[];
|
|
|
11
11
|
* - `style-src 'self' 'unsafe-inline'` (Tailwind + runtime style injection)
|
|
12
12
|
* - `media-src 'self' blob:` (chat voice previews / recorded clips use blob URLs)
|
|
13
13
|
* - `frame-ancestors 'none'` (prevent clickjacking)
|
|
14
|
-
* - `frame-src '
|
|
14
|
+
* - `frame-src 'self'` (sandboxed previews and extension UI)
|
|
15
15
|
* - `form-action 'self'` (prevent form submissions to untrusted origins)
|
|
16
16
|
* - `base-uri 'none'` (prevent base tag hijacking)
|
|
17
17
|
* - `object-src 'none'` (prevent plugin execution)
|
|
@@ -29,7 +29,7 @@ function hasSrcAttribute(openTag) {
|
|
|
29
29
|
* - `style-src 'self' 'unsafe-inline'` (Tailwind + runtime style injection)
|
|
30
30
|
* - `media-src 'self' blob:` (chat voice previews / recorded clips use blob URLs)
|
|
31
31
|
* - `frame-ancestors 'none'` (prevent clickjacking)
|
|
32
|
-
* - `frame-src '
|
|
32
|
+
* - `frame-src 'self'` (sandboxed previews and extension UI)
|
|
33
33
|
* - `form-action 'self'` (prevent form submissions to untrusted origins)
|
|
34
34
|
* - `base-uri 'none'` (prevent base tag hijacking)
|
|
35
35
|
* - `object-src 'none'` (prevent plugin execution)
|
|
@@ -41,7 +41,7 @@ function buildGatewayConsoleCspHeader(options) {
|
|
|
41
41
|
"base-uri 'none'",
|
|
42
42
|
"object-src 'none'",
|
|
43
43
|
"frame-ancestors 'none'",
|
|
44
|
-
"frame-src '
|
|
44
|
+
"frame-src 'self'",
|
|
45
45
|
"form-action 'self'",
|
|
46
46
|
hashes?.length ? `script-src 'self' ${hashes.map((hash) => `'${hash}'`).join(" ")}` : "script-src 'self'",
|
|
47
47
|
"style-src 'self' 'unsafe-inline'",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
4
3
|
import { getModelRegistry } from "../../providers/model-registry.js";
|
|
4
|
+
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
5
5
|
import { init_providers } from "../../providers/index.js";
|
|
6
6
|
import { disposeAllSessionMcpRuntimes } from "../../agent/mcp/bundle-mcp-runtime.js";
|
|
7
7
|
import { reloadImageGenerationProviders } from "../../agent/image/generation/provider-registry.js";
|
|
@@ -13,7 +13,6 @@ import { ChatStreamMapper } from "../chat-stream/mapper.js";
|
|
|
13
13
|
import { resolveWebchatSessionKey } from "../resolve-webchat-session-key.js";
|
|
14
14
|
import { recordExplicitRelationshipFollowUp } from "../../user-context/relationship-continuity.js";
|
|
15
15
|
import { coalesceThinkingDeltas } from "../chat-stream/thinking-delta-coalescer.js";
|
|
16
|
-
import { TurnOutcomeCollector } from "../chat-stream/turn-outcome-collector.js";
|
|
17
16
|
import crypto from "crypto";
|
|
18
17
|
//#region src/gateway/service/run-gateway-agent.ts
|
|
19
18
|
init_logger();
|
|
@@ -39,7 +38,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
39
38
|
let webchatSessionKey;
|
|
40
39
|
let webchatSessionId;
|
|
41
40
|
let webchatMetadata;
|
|
42
|
-
let outcomeEnvironment = "workspace";
|
|
43
41
|
if (channel === "webchat") {
|
|
44
42
|
const resolved = resolveWebchatSessionKey({ sessionKey: chatId });
|
|
45
43
|
if (resolved.ok === false) throw new Error(resolved.error);
|
|
@@ -68,8 +66,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
68
66
|
channel,
|
|
69
67
|
metadata: webchatMetadata
|
|
70
68
|
});
|
|
71
|
-
if (/remote/i.test(executionContext.strategy ?? "")) outcomeEnvironment = "remote_runtime";
|
|
72
|
-
else if (/worktree/i.test(executionContext.strategy ?? "")) outcomeEnvironment = "worktree";
|
|
73
69
|
taskRun = TaskRunCoordinator.start({
|
|
74
70
|
runId,
|
|
75
71
|
context: executionContext,
|
|
@@ -81,11 +77,8 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
81
77
|
sessionKey: streamSessionKey,
|
|
82
78
|
channel
|
|
83
79
|
});
|
|
84
|
-
const outcomeCollector = new TurnOutcomeCollector(runId, outcomeEnvironment);
|
|
85
|
-
let outcomeFinalized = false;
|
|
86
80
|
let registeredActiveWebchatRun = false;
|
|
87
81
|
const captureTaskEvent = (event) => {
|
|
88
|
-
outcomeCollector.capture(event);
|
|
89
82
|
if (event.type === "task_plan_updated") {
|
|
90
83
|
taskRun?.capturePlan(event.payload.items);
|
|
91
84
|
return;
|
|
@@ -110,30 +103,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
110
103
|
yield event;
|
|
111
104
|
}
|
|
112
105
|
};
|
|
113
|
-
const finalizeOutcome = async (status, summary) => {
|
|
114
|
-
if (outcomeFinalized) return [];
|
|
115
|
-
outcomeFinalized = true;
|
|
116
|
-
const outcome = outcomeCollector.finalize(status, summary);
|
|
117
|
-
if (webchatSessionKey) try {
|
|
118
|
-
await sessionIndex.appendTranscriptCustomEntry(webchatSessionKey, {
|
|
119
|
-
customType: "turn_outcome",
|
|
120
|
-
data: outcome
|
|
121
|
-
});
|
|
122
|
-
} catch (err) {
|
|
123
|
-
log.warn({
|
|
124
|
-
err,
|
|
125
|
-
runId,
|
|
126
|
-
sessionKey: webchatSessionKey
|
|
127
|
-
}, `Turn outcome persistence failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
128
|
-
}
|
|
129
|
-
return [{
|
|
130
|
-
type: "turn_outcome",
|
|
131
|
-
runId,
|
|
132
|
-
sessionKey: streamSessionKey,
|
|
133
|
-
timestamp: Date.now(),
|
|
134
|
-
payload: outcome
|
|
135
|
-
}];
|
|
136
|
-
};
|
|
137
106
|
try {
|
|
138
107
|
yield* emitAndYield(mapper.start());
|
|
139
108
|
if (channel === "webchat" && webchatSessionKey) {
|
|
@@ -173,7 +142,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
173
142
|
taskRunStatus = mergedSignal.aborted ? "cancelled" : "succeeded";
|
|
174
143
|
terminalStatus = endStatus;
|
|
175
144
|
taskRunSummary = endSummary;
|
|
176
|
-
yield* emitAndYield(await finalizeOutcome(endStatus, endSummary));
|
|
177
145
|
yield* emitAndYield(mapper.end(endStatus, endSummary));
|
|
178
146
|
completeRealtimeTopic(`run:${runId}`);
|
|
179
147
|
runTopicCompleted = true;
|
|
@@ -197,7 +165,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
197
165
|
taskRunSummary = em;
|
|
198
166
|
const errorContent = formatAgentRunErrorForClient(streamError);
|
|
199
167
|
yield* emitAndYield(mapper.error(errorContent));
|
|
200
|
-
yield* emitAndYield(await finalizeOutcome("error", streamError));
|
|
201
168
|
yield* emitAndYield(mapper.end("error", streamError));
|
|
202
169
|
completeRealtimeTopic(`run:${runId}`);
|
|
203
170
|
runTopicCompleted = true;
|
|
@@ -283,7 +250,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
283
250
|
presentation: "answer"
|
|
284
251
|
}
|
|
285
252
|
}]);
|
|
286
|
-
yield* emitAndYield(await finalizeOutcome("success", "Message processed"));
|
|
287
253
|
yield* emitAndYield(mapper.end("success", "Message processed"));
|
|
288
254
|
taskRunStatus = "succeeded";
|
|
289
255
|
taskRunSummary = "Message processed";
|
|
@@ -306,7 +272,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
|
|
|
306
272
|
}, `Agent run failed: ${em}`);
|
|
307
273
|
if (channel === "webchat" && !runTopicCompleted) {
|
|
308
274
|
yield* emitAndYield(mapper.error(formatAgentRunErrorForClient(em)));
|
|
309
|
-
yield* emitAndYield(await finalizeOutcome("error", em));
|
|
310
275
|
yield* emitAndYield(mapper.end("error", em));
|
|
311
276
|
completeRealtimeTopic(`run:${runId}`);
|
|
312
277
|
runTopicCompleted = true;
|