@xopcai/xopc 0.0.283 → 0.0.284
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/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-iPtG94qr.js → activity-store-CwbIMGLY.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DQ74xqLi.js → agent-avatar-dicebear-DjiOld-S.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-SElFkQd6.js → agent-defaults-settings-panel-D9qP10BK.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-DYazUaZ6.js → agents-D-bkOuAl.js} +1 -1
- package/dist/gateway/static/root/assets/{api-KMJzMqvZ.js → api-3lphBVuY.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-B42TpqIb.js → app-management-settings-panel-B2nwCXxE.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-D9Lfe7Sk.js → appearance-settings-BXO4Ej88.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BzrodN8C.js → apps-page-BKwBBwJN.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-BdmQwj4x.js → archive-plugin-6ZRqNjla.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-B4S0_nji.js → attachment-load-CmPZalcW.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-B3TmHNjQ.js → automations-page-Bc3WbK5n.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace--A4I0jMu.js → automations-workspace-B2-f9qZu.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-B3cXF_DP.js → binary-plugins-OWR6cxpc.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-05zyBXBe.js → block-editor-Ca-tDQXm.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-BTIufVbM.js → browser-automation-inputs-Dxy2_p3U.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-DRBA9nwe.js → browser-automations-page-CibQIxpi.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-DDTXUNWK.js → browser-settings-page-Dtn4rMeq.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-Ll5UeXq5.js → capabilities-settings-panel-DiwtR9MD.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-BzL15bkU.js → channels-settings-pTdR1q1K.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-DMYQNs-J.js → channels-status-swr-Bh-G9mdZ.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-Dx5KQyo5.js → chat-terminal-dock-lbzbSpV3.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-RPw1AOMb.js → connectors-page-BSQ7Hs2w.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-CkDROZoe.js → date-picker-tYvbTfZT.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-Bl8HcvzL.js → dependency-picker-B8yo7-7V.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BcknAjbW.js → desktop-pet-BUGkGB4-.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BuCpXQPm.js → desktop-pet-settings-DNeVGZNp.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-D0_tCX49.js → device-pairing-wizard-CYkT7ecp.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-qRpb2FFa.js → extension-debug-page-CiK0QI2p.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C7gs9wu4.js → extension-page-CirPNHJd.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CyJP1CMZ.js → extension-settings-page-Baua1_Le.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Cpmyrr6j.js → gateway-config-swr-Co4hHe0i.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-DOeeTdWE.js → gateway-settings-CzpSYWex.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DoiPeb65.js → gateway-startup-retry-DBPNJG-e.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CV6Dolcn.js → heartbeat-settings-DTM_kYpE.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-Djw1H_Go.js → home-page-CSSJvdd6.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-CqjHWl2T.js → image-generation-api-D0rMTumD.js} +1 -1
- package/dist/gateway/static/root/assets/imports-page-XqVVYbAp.js +1 -0
- package/dist/gateway/static/root/assets/{index-CHgOG1aR.js → index-C9aQA4Dr.js} +9 -9
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B3XqA41D.js → keyboard-shortcuts-settings-CMUaF61L.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-BPqgyYJq.js → local-app-workbench-page-CZSgTYuI.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BcIVYp0J.js → local-apps-page-CHxyT09t.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-1urrBBCQ.js → locale-store-DO6JJ1l2.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-BHax3XlS.js → logs-page-z5ktnEzX.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-DNFCdgzR.js → management-settings-jXbA2S54.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-BZmcFIWa.js → markdown-view-CKInRsDh.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-BDxNhoBC.js → media-plugins-BaPkSvW7.js} +1 -1
- package/dist/gateway/static/root/assets/messages-BgZRsCkG.js +3 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-Cs46LFXO.js → note-detail-panel-DSaZfkna.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-BUkcn_0t.js → notes-page-CePEyv_C.js} +1 -1
- package/dist/gateway/static/root/assets/{page-tabs-agH3a2PI.js → page-tabs-CdYemUOu.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-XEZ7zDOP.js → preview-open-alternatives-DzgF1yYO.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-page-Bn484J9v.js → proactive-page-Cy2Gm2f8.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-lXAbj85Z.js → product-open-page-iQ-eW6Aj.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-LcH21ixD.js → project-detail-page-BliwYpns.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-DelBiwyB.js → projects-page-Dm-BqnQn.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-CxQstD00.js → registry-api-DMNudT60.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-3BjFYXCf.js → remote-access-hub-C_XuQ9XR.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DErKZlVJ.js → runtime-tools-settings-panel-u_wVepf7.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DhCmJQVJ.js → schema-form-YdZj2xdI.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CVFirgVd.js → sessions-page-Drt1aS2G.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BAUd06_9.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-Dg6chotg.js → settings-form-section-Bw6xKOX5.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-LyuN5N3M.js → settings-page-CV8kVdnE.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-CrXhTzHr.js → setup-status-panel-BHI9e5Tw.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-CZvmf4Ob.js → share-preview-page-BHwjHlrV.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-DyM_D8hM.js → shares-settings-DOAOjZBQ.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DU0VQpHj.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DRKdb6O5.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-Eu0WVHTS.js → skills-page.utils-DYfaLtts.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-DQKkM7B2.js → system-settings-panel-CWrWpKV-.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-B18NcZQJ.js → task-detail-page-DiPgMmBM.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-Dc5s20V3.js → theme-store-CDc7Wqy5.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-TKQpPVd6.js → tunnel-api-Bmsy-5Fs.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-CkVlrFk1.js → use-autosave-D-FRib85.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-ul-zr58b.js → user-model-page-DxRaRdA0.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-B12fFN-L.js → voice-api-key-field-iQ7EBzII.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-Mk78BrpP.js → voice-config-api-DNMWA2tx.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-CT9Zy36i.js → work-discovery-overlay-BesuhDlH.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BwefQliE.js → workflow-run-setup-panel-Du81FoCy.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-BY_AWs7S.js → workflows-page-CZikdgNw.js} +1 -1
- package/dist/gateway/static/root/index.html +9 -9
- package/dist/package.js +1 -1
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/import.d.ts +2 -0
- package/dist/src/cli/commands/import.js +30 -0
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/platform.js +1 -1
- package/dist/src/cli/commands/profile.js +1 -1
- package/dist/src/cli/commands/tunnel.js +1 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/routes/imports.d.ts +3 -0
- package/dist/src/gateway/hono/routes/imports.js +44 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service.js +2 -0
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/imports/compatibility.d.ts +7 -0
- package/dist/src/imports/compatibility.js +174 -0
- package/dist/src/imports/contextImport.d.ts +3 -0
- package/dist/src/imports/contextImport.js +56 -0
- package/dist/src/imports/files.d.ts +13 -0
- package/dist/src/imports/files.js +77 -0
- package/dist/src/imports/importService.d.ts +45 -0
- package/dist/src/imports/importService.js +417 -0
- package/dist/src/imports/planner.d.ts +11 -0
- package/dist/src/imports/planner.js +98 -0
- package/dist/src/imports/productImport.d.ts +10 -0
- package/dist/src/imports/productImport.js +148 -0
- package/dist/src/imports/runtime.d.ts +5 -0
- package/dist/src/imports/runtime.js +56 -0
- package/dist/src/imports/scanner.d.ts +14 -0
- package/dist/src/imports/scanner.js +159 -0
- package/dist/src/imports/secrets.d.ts +2 -0
- package/dist/src/imports/secrets.js +9 -0
- package/dist/src/imports/sources.d.ts +37 -0
- package/dist/src/imports/sources.js +75 -0
- package/dist/src/imports/types.d.ts +91 -0
- package/dist/src/imports/types.js +11 -0
- package/dist/src/knowledge-memory/repository.js +4 -2
- package/dist/src/storage/sqlite/import-repository.d.ts +19 -0
- package/dist/src/storage/sqlite/import-repository.js +35 -0
- package/dist/src/storage/sqlite/migrations/174_capability_imports.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/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/package.json +4 -3
- package/dist/gateway/static/root/assets/messages-DYFcjtmG.js +0 -3
- package/dist/gateway/static/root/assets/settings-advanced-gate-DyfBpqkY.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bys58-15.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-sWNvFo6D.js +0 -2
package/dist/package.js
CHANGED
|
@@ -26,6 +26,7 @@ const REGISTRY_COMMAND_MODULES = {
|
|
|
26
26
|
voice: () => import("./commands/voice.js"),
|
|
27
27
|
search: () => import("./commands/search.js"),
|
|
28
28
|
auth: () => import("./commands/auth.js"),
|
|
29
|
+
import: () => import("./commands/import.js"),
|
|
29
30
|
skills: () => import("./commands/skills.js"),
|
|
30
31
|
connectors: () => import("./commands/connectors.js"),
|
|
31
32
|
browser: () => import("./commands/browser.js"),
|
|
@@ -127,6 +127,10 @@ const ROOT_HELP_COMMANDS = [
|
|
|
127
127
|
name: "search",
|
|
128
128
|
description: "Manage web-search providers (brave / tavily / bing / searxng)"
|
|
129
129
|
},
|
|
130
|
+
{
|
|
131
|
+
name: "import [source]",
|
|
132
|
+
description: "Import skills, context and projects from another AI app"
|
|
133
|
+
},
|
|
130
134
|
{
|
|
131
135
|
name: "skills",
|
|
132
136
|
description: "Manage skills"
|
|
@@ -8,10 +8,10 @@ import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
|
|
|
8
8
|
import "../../infra/bus/index.js";
|
|
9
9
|
import { AgentService } from "../../agent/service.js";
|
|
10
10
|
import "../../agent/index.js";
|
|
11
|
-
import { formatExamples, register } from "../registry.js";
|
|
12
|
-
import { getContextWithOpts } from "../context.js";
|
|
13
11
|
import { getWorkspacePath } from "../../config/workspace-path-helpers.js";
|
|
14
12
|
import "../../config/index.js";
|
|
13
|
+
import { formatExamples, register } from "../registry.js";
|
|
14
|
+
import { getContextWithOpts } from "../context.js";
|
|
15
15
|
import { getXopcCloudCatalogCoordinator } from "../../providers/xopc-cloud-catalog-coordinator.js";
|
|
16
16
|
import { listSessions } from "./agent/sessions.js";
|
|
17
17
|
import { renderStreamToTerminal } from "./agent/stream-renderer.js";
|
|
@@ -52,7 +52,7 @@ async function checkDatabaseSchema(ctx) {
|
|
|
52
52
|
id: "database-schema",
|
|
53
53
|
label: "Database schema",
|
|
54
54
|
status: "pass",
|
|
55
|
-
message: `SQLite schema v${status.dbVersion} (current release
|
|
55
|
+
message: `SQLite schema v${status.dbVersion} (current release v174).`,
|
|
56
56
|
hints: []
|
|
57
57
|
};
|
|
58
58
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { init_paths, resolveConfigPath } from "../../../config/paths.js";
|
|
2
2
|
import { loadConfig } from "../../../config/loader.js";
|
|
3
|
-
import "../../../config/index.js";
|
|
4
3
|
import { isRestartEnabled } from "../../../config/commands.flags.js";
|
|
4
|
+
import "../../../config/index.js";
|
|
5
5
|
import { resolveGatewayService } from "../../../daemon/service.js";
|
|
6
6
|
import { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from "../../../infra/restart.js";
|
|
7
7
|
import { findVerifiedGatewayListenerPidsOnPortSync, formatGatewayPidList, signalVerifiedGatewayPidSync } from "../../../infra/gateway-processes.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { closeXopcDatabase, isXopcDatabaseOpen, openXopcDatabase } from "../../storage/sqlite/connection.js";
|
|
2
|
+
import { register } from "../registry.js";
|
|
3
|
+
import { isImportSource } from "../../imports/sources.js";
|
|
4
|
+
import { createRuntimeImportService, recoverCapabilityImports } from "../../imports/runtime.js";
|
|
5
|
+
import { importProduct, listImportSources } from "../../imports/productImport.js";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
//#region src/cli/commands/import.ts
|
|
8
|
+
function createImportCommand() {
|
|
9
|
+
return new Command("import").description("Import skills, context and projects from another AI app").argument("[source]", "codex or claude-code; omit to list detected apps").action(async (source) => {
|
|
10
|
+
const selected = source && isImportSource(source) ? source : void 0;
|
|
11
|
+
if (source && !selected) throw new Error("Unsupported import source");
|
|
12
|
+
const opened = !isXopcDatabaseOpen();
|
|
13
|
+
if (opened) openXopcDatabase();
|
|
14
|
+
try {
|
|
15
|
+
await recoverCapabilityImports();
|
|
16
|
+
console.log(JSON.stringify(selected ? await importProduct(createRuntimeImportService("gateway-owner"), selected) : listImportSources("gateway-owner"), null, 2));
|
|
17
|
+
} finally {
|
|
18
|
+
if (opened) closeXopcDatabase();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
register({
|
|
23
|
+
id: "import",
|
|
24
|
+
name: "import",
|
|
25
|
+
description: "Import from other AI apps",
|
|
26
|
+
factory: createImportCommand,
|
|
27
|
+
metadata: { category: "utility" }
|
|
28
|
+
});
|
|
29
|
+
//#endregion
|
|
30
|
+
export { createImportCommand };
|
|
@@ -2,9 +2,9 @@ import { getAgentDefaultModelRef, init_schema } from "../../config/schema.js";
|
|
|
2
2
|
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
3
3
|
import { CredentialResolver, init_credentials } from "../../auth/credentials.js";
|
|
4
4
|
import { getAllModels, getAvailableModels, getConfiguredProviders, getProviderAuthState, getProviderDisplayName, init_providers, isProviderConfigured, providerSupportsApiKey, providerSupportsOAuth, resolveModel } from "../../providers/index.js";
|
|
5
|
+
import "../../config/index.js";
|
|
5
6
|
import { formatExamples, register } from "../registry.js";
|
|
6
7
|
import { getContextWithOpts } from "../context.js";
|
|
7
|
-
import "../../config/index.js";
|
|
8
8
|
import { prepareUpdateGlobalDefaults } from "../../gateway/global-defaults-admin.js";
|
|
9
9
|
import { disconnectProvider } from "../../providers/provider-disconnect.js";
|
|
10
10
|
import { colors } from "../utils/colors.js";
|
|
@@ -2,8 +2,8 @@ import { init_paths_state, resolveStateDir } from "../../config/paths-state.js";
|
|
|
2
2
|
import { ConfigSchema, init_schema } from "../../config/schema.js";
|
|
3
3
|
import { saveConfig } from "../../config/loader.js";
|
|
4
4
|
import { resolveGatewayLocalClientHost } from "../../config/gateway-bind.js";
|
|
5
|
-
import { formatExamples, register } from "../registry.js";
|
|
6
5
|
import "../../config/index.js";
|
|
6
|
+
import { formatExamples, register } from "../registry.js";
|
|
7
7
|
import { provisionEagerRuntimes } from "../../runtime-tools/bootstrap.js";
|
|
8
8
|
import { colors } from "../utils/colors.js";
|
|
9
9
|
import { initWorkspace } from "../utils/init-workspace.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
2
|
-
import { formatExamples, register } from "../registry.js";
|
|
3
2
|
import "../../config/index.js";
|
|
3
|
+
import { formatExamples, register } from "../registry.js";
|
|
4
4
|
import { PLATFORM_RUNTIME_PROFILE, PLATFORM_RUNTIME_PROVIDER, PlatformRuntimeClient, discoverPlatform } from "../../platform/client.js";
|
|
5
5
|
import "../../platform/index.js";
|
|
6
6
|
import { getProfile, upsertAuthProfile } from "../../auth/profiles/profiles.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { formatExamples, register } from "../registry.js";
|
|
4
3
|
import { createProfile, deleteProfile, getCurrentProfile, getSwitchCommand, listProfiles, resolveProfileStateDir } from "../../config/profile.js";
|
|
4
|
+
import { formatExamples, register } from "../registry.js";
|
|
5
5
|
import { colors } from "../utils/colors.js";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import Table from "cli-table3";
|
|
@@ -7,8 +7,8 @@ import { ensureFrpcBinary } from "../../tunnel/frpc-binary.js";
|
|
|
7
7
|
import { resolveTunnelBrokerUrl, resolveTunnelRegistrationSecret } from "../../tunnel/env.js";
|
|
8
8
|
import { loadTunnelState } from "../../tunnel/tunnel-state.js";
|
|
9
9
|
import { getTunnelService } from "../../tunnel/tunnel-service.js";
|
|
10
|
-
import { formatExamples, register } from "../registry.js";
|
|
11
10
|
import "../../config/index.js";
|
|
11
|
+
import { formatExamples, register } from "../registry.js";
|
|
12
12
|
import { resolveGatewayAuth } from "../../gateway/auth.js";
|
|
13
13
|
import { requireTunnelGatewayToken } from "../../tunnel/auth-policy.js";
|
|
14
14
|
import { TUNNEL_RISK_SUMMARY_LINES, TunnelConsentError, assertTunnelMayStart, hasValidTunnelConsent } from "../../tunnel/consent.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PACKAGE_VERSION, init_package_version } from "../../package-version.js";
|
|
2
2
|
import { loadConfig } from "../../config/loader.js";
|
|
3
|
-
import { formatExamples, register } from "../registry.js";
|
|
4
3
|
import "../../config/index.js";
|
|
4
|
+
import { formatExamples, register } from "../registry.js";
|
|
5
5
|
import { acquireUpdateLock } from "../../infra/update-lock.js";
|
|
6
6
|
import { normalizeUpdateChannel } from "../../infra/update-channels.js";
|
|
7
7
|
import { compareSemver, resolveNpmChannelTag } from "../../infra/update-check.js";
|
|
@@ -4,9 +4,9 @@ import { ExtensionLoader } from "../extensions/loader.js";
|
|
|
4
4
|
import "../extensions/index.js";
|
|
5
5
|
import { MessageBus } from "../infra/bus/queue.js";
|
|
6
6
|
import "../infra/bus/index.js";
|
|
7
|
-
import { createDefaultContext } from "./registry.js";
|
|
8
7
|
import { getWorkspacePath } from "../config/workspace-path-helpers.js";
|
|
9
8
|
import "../config/index.js";
|
|
9
|
+
import { createDefaultContext } from "./registry.js";
|
|
10
10
|
import { registerExtensionCliProgram } from "../extensions/sdk/channel-helpers.js";
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
//#region src/cli/extension-cli-register.ts
|
package/dist/src/config/index.js
CHANGED
|
@@ -13,10 +13,10 @@ import { clearConfigValueCache, getAllowedCommands, getCacheStats, init_resolve_
|
|
|
13
13
|
import { CustomModelSchema, ImageGenerationCapabilitiesSchema, ImageGenerationModelSchema, ImageGenerationProviderSchema, ModelOverrideSchema, ModelsJsonSchema, OpenAICompatSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenRouterRoutingSchema, ProviderConfigSchema, VercelGatewayRoutingSchema, getDefaultModelValues, init_models_json, loadModelsJson, modelsJsonExists, saveModelsJson, validateModelsConfig } from "./models-json.js";
|
|
14
14
|
import { canonicalizeConfiguredMcpServer, isKnownCliMcpTypeAlias, normalizeConfiguredMcpServers, resolveXopcMcpTransportAlias } from "./mcp-config-normalize.js";
|
|
15
15
|
import { formatThinkingLevels, listThinkingLevels, normalizeElevatedMode, normalizeReasoningLevel, normalizeThinkLevel, normalizeVerboseLevel, thinkLevelToNumber } from "../agent/transcript/thinking-types.js";
|
|
16
|
+
import { diffConfigPaths } from "./diff.js";
|
|
16
17
|
import { getWorkspacePath } from "./workspace-path-helpers.js";
|
|
17
18
|
import { listAgentProfileMarkdownDirs, listAgentWorkspaceDirs } from "./workspace-dirs.js";
|
|
18
19
|
import { ProfileManager, createProfile, deleteProfile, getCurrentProfile, getProfileManager, getProfileNameFromDir, getSwitchCommand, listProfiles, resolveProfileStateDir } from "./profile.js";
|
|
19
|
-
import { diffConfigPaths } from "./diff.js";
|
|
20
20
|
import { BASE_RELOAD_RULES, buildReloadPlan, matchReloadRule } from "./rules.js";
|
|
21
21
|
import { ConfigHotReloader } from "./reload.js";
|
|
22
22
|
//#region src/config/index.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { shouldSilence, stripHeartbeatToken } from "../../heartbeat/tokens.js";
|
|
4
|
-
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
5
4
|
import { isWithinActiveHours } from "../../heartbeat/active-hours.js";
|
|
6
5
|
import { isHeartbeatContentEmpty } from "../../heartbeat/content-check.js";
|
|
6
|
+
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
7
7
|
import { createHeartbeatWake } from "../../heartbeat/wake.js";
|
|
8
8
|
import { resolveHeartbeatMdPath } from "../workspace-heartbeat-path.js";
|
|
9
9
|
import { readFile } from "fs/promises";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
2
|
+
import { ImportError } from "../../../imports/types.js";
|
|
3
|
+
import { isImportSource } from "../../../imports/sources.js";
|
|
4
|
+
import { createRuntimeImportService } from "../../../imports/runtime.js";
|
|
5
|
+
import { importProduct, listImportSources } from "../../../imports/productImport.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
//#region src/gateway/hono/routes/imports.ts
|
|
8
|
+
const requestSchema = z.object({ requestId: z.string().uuid() }).strict();
|
|
9
|
+
function registerImportRoutes(app, deps) {
|
|
10
|
+
const respond = async (c, run) => {
|
|
11
|
+
try {
|
|
12
|
+
if (getGatewayPrincipal(c).kind !== "owner") throw new ImportError("owner_required", "Only the Gateway owner can import local capabilities", 403);
|
|
13
|
+
return c.json({
|
|
14
|
+
ok: true,
|
|
15
|
+
data: await run()
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error instanceof z.ZodError) return c.json({
|
|
19
|
+
ok: false,
|
|
20
|
+
code: "invalid_input",
|
|
21
|
+
error: "Invalid import request"
|
|
22
|
+
}, 400);
|
|
23
|
+
if (error instanceof ImportError) return c.json({
|
|
24
|
+
ok: false,
|
|
25
|
+
code: error.code,
|
|
26
|
+
error: error.message
|
|
27
|
+
}, error.status);
|
|
28
|
+
return c.json({
|
|
29
|
+
ok: false,
|
|
30
|
+
code: "import_failed",
|
|
31
|
+
error: "Import could not finish. Try again; existing content is preserved."
|
|
32
|
+
}, 400);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
app.get("/api/imports/sources", (c) => respond(c, () => ({ sources: listImportSources("gateway-owner") })));
|
|
36
|
+
app.post("/api/imports/sources/:source/import", deps.strictRateLimitMiddleware, (c) => respond(c, async () => {
|
|
37
|
+
const source = c.req.param("source");
|
|
38
|
+
if (!isImportSource(source)) throw new ImportError("unsupported_source", "Unsupported source");
|
|
39
|
+
const { requestId } = requestSchema.parse(await c.req.json());
|
|
40
|
+
return importProduct(createRuntimeImportService("gateway-owner", () => deps.service.agentService.refreshSkillsAfterDiskChange(), (root) => deps.service.agentService.getWorkspaceTrust(root).trusted), source, requestId);
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { registerImportRoutes };
|
|
@@ -3,6 +3,14 @@ function startsWithAny(path, prefixes) {
|
|
|
3
3
|
return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
|
|
4
4
|
}
|
|
5
5
|
const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
|
|
6
|
+
{
|
|
7
|
+
id: "imports",
|
|
8
|
+
match: (path) => startsWithAny(path, ["/api/imports"]),
|
|
9
|
+
load: async () => {
|
|
10
|
+
const { registerImportRoutes } = await import("./imports.js");
|
|
11
|
+
return { register: registerImportRoutes };
|
|
12
|
+
}
|
|
13
|
+
},
|
|
6
14
|
{
|
|
7
15
|
id: "project-understanding",
|
|
8
16
|
match: (path) => /^\/api\/projects\/[^/]+\/understanding$/.test(path),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from "../config/loader.js";
|
|
2
|
-
import "../config/index.js";
|
|
3
2
|
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
3
|
+
import "../config/index.js";
|
|
4
4
|
import { runBootstrapMigrationsSync } from "../migrations/runner.js";
|
|
5
5
|
import { restartGatewayProcessWithFreshPid } from "./respawn.js";
|
|
6
6
|
import { acquireGatewayLock } from "./lock.js";
|
|
@@ -955,6 +955,8 @@ var GatewayService = class {
|
|
|
955
955
|
});
|
|
956
956
|
log.debug("Starting gateway service...");
|
|
957
957
|
openXopcDatabase();
|
|
958
|
+
const { recoverCapabilityImports } = await import("../imports/runtime.js");
|
|
959
|
+
await recoverCapabilityImports();
|
|
958
960
|
this.createNotificationService().start();
|
|
959
961
|
this.startTime = Date.now();
|
|
960
962
|
this.running = true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, shouldSilence, stripHeartbeatToken } from "./tokens.js";
|
|
2
|
-
import { appendCronEventLines } from "./event-prompt.js";
|
|
3
2
|
import { isWithinActiveHours } from "./active-hours.js";
|
|
4
3
|
import { isHeartbeatContentEmpty } from "./content-check.js";
|
|
4
|
+
import { appendCronEventLines } from "./event-prompt.js";
|
|
5
5
|
import { createHeartbeatWake } from "./wake.js";
|
|
6
6
|
export { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, appendCronEventLines, createHeartbeatWake, isHeartbeatContentEmpty, isWithinActiveHours, shouldSilence, stripHeartbeatToken };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ImportCandidate, ImportFile } from './types.js';
|
|
2
|
+
export declare function parseSkill(raw: string): {
|
|
3
|
+
metadata: Record<string, unknown>;
|
|
4
|
+
body: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function inspectSkill(base: Pick<ImportCandidate, 'id' | 'source' | 'scope' | 'location' | 'shared'>, files: ImportFile[]): ImportCandidate;
|
|
7
|
+
export declare function inspectMcp(base: Pick<ImportCandidate, 'id' | 'source' | 'scope' | 'location' | 'shared'>, name: string, raw: unknown): ImportCandidate;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { McpServerSchema, init_schema } from "../config/schema.js";
|
|
2
|
+
import { isValidSkillId } from "../agent/skills/managed-store.js";
|
|
3
|
+
import { containsSecret, isCredentialPath } from "./secrets.js";
|
|
4
|
+
import { digest, fileHash } from "./files.js";
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
|
+
//#region src/imports/compatibility.ts
|
|
7
|
+
init_schema();
|
|
8
|
+
function parseSkill(raw) {
|
|
9
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(raw);
|
|
10
|
+
if (!match || match[1].length > 64 * 1024) throw new Error("A valid YAML header is required");
|
|
11
|
+
if (/(?:^|\s)[&*][\w-]+/.test(match[1])) throw new Error("YAML aliases are not supported");
|
|
12
|
+
let metadata;
|
|
13
|
+
try {
|
|
14
|
+
metadata = yaml.load(match[1], { schema: yaml.JSON_SCHEMA });
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error("Invalid YAML header");
|
|
17
|
+
}
|
|
18
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) throw new Error("Invalid skill metadata");
|
|
19
|
+
return {
|
|
20
|
+
metadata,
|
|
21
|
+
body: match[2]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function inspectSkill(base, files) {
|
|
25
|
+
const item = {
|
|
26
|
+
...base,
|
|
27
|
+
kind: "skill",
|
|
28
|
+
name: base.id,
|
|
29
|
+
description: "",
|
|
30
|
+
files,
|
|
31
|
+
hash: fileHash(files),
|
|
32
|
+
compatibility: "compatible",
|
|
33
|
+
findings: [],
|
|
34
|
+
requiredEnv: []
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
if (files.reduce((n, f) => n + Buffer.byteLength(f.data, "base64"), 0) > 15 * 1024 * 1024) throw new Error("Skill exceeds 15 MiB");
|
|
38
|
+
if (files.some((f) => f.path.endsWith("/SKILL.md"))) throw new Error("Nested skill scopes need separate imports");
|
|
39
|
+
const primary = files.find((f) => f.path === "SKILL.md");
|
|
40
|
+
if (!primary || Buffer.byteLength(primary.data, "base64") > 1024 * 1024) throw new Error("SKILL.md is missing or exceeds 1 MiB");
|
|
41
|
+
const { metadata, body } = parseSkill(Buffer.from(primary.data, "base64").toString("utf8"));
|
|
42
|
+
if (typeof metadata.name !== "string" || !isValidSkillId(metadata.name)) throw new Error("Skill name must be a portable identifier");
|
|
43
|
+
item.name = metadata.name;
|
|
44
|
+
if (typeof metadata.description !== "string" || !metadata.description.trim() || metadata.description.length > 1024) throw new Error("Skill description must contain 1–1024 characters");
|
|
45
|
+
item.description = metadata.description;
|
|
46
|
+
const supported = new Set([
|
|
47
|
+
"name",
|
|
48
|
+
"description",
|
|
49
|
+
"license",
|
|
50
|
+
"compatibility",
|
|
51
|
+
"metadata",
|
|
52
|
+
"disable-model-invocation",
|
|
53
|
+
"allowed-tools"
|
|
54
|
+
]);
|
|
55
|
+
for (const key of Object.keys(metadata)) if (!supported.has(key)) item.findings.push(`Unsupported skill field: ${key}`);
|
|
56
|
+
if (metadata["disable-model-invocation"] !== void 0 && typeof metadata["disable-model-invocation"] !== "boolean") item.findings.push("disable-model-invocation must be boolean");
|
|
57
|
+
if (/\$ARGUMENTS|\$\{?(?:CLAUDE_|CODEX_)|!`/.test(body)) item.findings.push("Product-specific variables or command expansion require adaptation");
|
|
58
|
+
const meta = metadata.metadata;
|
|
59
|
+
if (meta && (typeof meta !== "object" || Array.isArray(meta))) item.findings.push("Invalid metadata");
|
|
60
|
+
if (meta && Object.keys(meta).some((k) => ![
|
|
61
|
+
"author",
|
|
62
|
+
"version",
|
|
63
|
+
"short-description",
|
|
64
|
+
"short_description",
|
|
65
|
+
"category",
|
|
66
|
+
"tags",
|
|
67
|
+
"license",
|
|
68
|
+
"homepage"
|
|
69
|
+
].includes(k))) item.findings.push("Extended metadata needs manual compatibility review");
|
|
70
|
+
for (const match of body.matchAll(/\[[^\]]*\]\(([^)\s]+)\)/g)) {
|
|
71
|
+
const ref = match[1].split("#")[0];
|
|
72
|
+
if (ref && !/^(?:[a-z]+:|#)/i.test(ref) && !files.some((f) => f.path === ref.replace(/^\.\//, ""))) item.findings.push("A referenced local file is missing or outside the skill");
|
|
73
|
+
}
|
|
74
|
+
if (item.findings.length) item.compatibility = "blocked";
|
|
75
|
+
if (metadata["allowed-tools"]) item.findings.push("Source tool pre-approvals are not transferred; xopc permissions apply");
|
|
76
|
+
for (const file of files) if (isCredentialPath(file.path) || containsSecret(Buffer.from(file.data, "base64").toString("utf8"))) {
|
|
77
|
+
item.compatibility = "blocked";
|
|
78
|
+
item.findings.push("Possible credentials found; remove them from the source and scan again");
|
|
79
|
+
item.files = [];
|
|
80
|
+
item.description = "";
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
item.compatibility = "blocked";
|
|
85
|
+
item.findings.push(error instanceof Error ? error.message : "Invalid skill");
|
|
86
|
+
}
|
|
87
|
+
if (files.some((f) => containsSecret(Buffer.from(f.data, "base64").toString("utf8")))) {
|
|
88
|
+
item.files = [];
|
|
89
|
+
item.description = "";
|
|
90
|
+
item.compatibility = "blocked";
|
|
91
|
+
item.findings = ["Possible credentials found; remove them and scan again"];
|
|
92
|
+
}
|
|
93
|
+
return item;
|
|
94
|
+
}
|
|
95
|
+
function inspectMcp(base, name, raw) {
|
|
96
|
+
const item = {
|
|
97
|
+
...base,
|
|
98
|
+
kind: "mcp",
|
|
99
|
+
name,
|
|
100
|
+
description: "MCP connection draft",
|
|
101
|
+
files: [],
|
|
102
|
+
hash: "",
|
|
103
|
+
compatibility: "needs_setup",
|
|
104
|
+
findings: [],
|
|
105
|
+
requiredEnv: []
|
|
106
|
+
};
|
|
107
|
+
const value = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
108
|
+
const draft = {};
|
|
109
|
+
const supported = new Set([
|
|
110
|
+
"command",
|
|
111
|
+
"args",
|
|
112
|
+
"cwd",
|
|
113
|
+
"url",
|
|
114
|
+
"type",
|
|
115
|
+
"env",
|
|
116
|
+
"headers",
|
|
117
|
+
"http_headers",
|
|
118
|
+
"startup_timeout_sec",
|
|
119
|
+
"tool_timeout_sec"
|
|
120
|
+
]);
|
|
121
|
+
for (const [key, v] of Object.entries(value)) {
|
|
122
|
+
if (!supported.has(key)) {
|
|
123
|
+
item.compatibility = "blocked";
|
|
124
|
+
item.findings.push(`Unsupported MCP field: ${key}`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if ([
|
|
128
|
+
"env",
|
|
129
|
+
"headers",
|
|
130
|
+
"http_headers"
|
|
131
|
+
].includes(key)) {
|
|
132
|
+
if (v && typeof v === "object") item.requiredEnv.push(...Object.keys(v));
|
|
133
|
+
item.findings.push(`${key} values omitted; configure them in xopc`);
|
|
134
|
+
} else if (key === "type") {
|
|
135
|
+
if (v === "http") draft.transport = "streamable-http";
|
|
136
|
+
else if (v === "sse") draft.transport = "sse";
|
|
137
|
+
else if (v !== "stdio") {
|
|
138
|
+
item.compatibility = "blocked";
|
|
139
|
+
item.findings.push("Unsupported MCP transport");
|
|
140
|
+
}
|
|
141
|
+
} else if (key === "startup_timeout_sec" || key === "tool_timeout_sec") if (typeof v === "number") draft[key === "startup_timeout_sec" ? "connectionTimeoutMs" : "requestTimeoutMs"] = v * 1e3;
|
|
142
|
+
else {
|
|
143
|
+
item.compatibility = "blocked";
|
|
144
|
+
item.findings.push("Invalid MCP timeout");
|
|
145
|
+
}
|
|
146
|
+
else draft[key] = v;
|
|
147
|
+
}
|
|
148
|
+
if (typeof draft.url === "string") try {
|
|
149
|
+
const url = new URL(draft.url);
|
|
150
|
+
if (url.username || url.password || url.search || url.hash) {
|
|
151
|
+
delete draft.url;
|
|
152
|
+
item.findings.push("URL credentials or query parameters omitted; enter a clean endpoint");
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
delete draft.url;
|
|
156
|
+
}
|
|
157
|
+
if (containsSecret(JSON.stringify(draft)) || /--?[a-z-]*(?:token|password|secret|api-key|authorization)(?:[=\s\"]|$)/i.test(JSON.stringify(draft))) {
|
|
158
|
+
Object.keys(draft).forEach((k) => delete draft[k]);
|
|
159
|
+
item.findings.push("Possible credentials omitted from connection fields");
|
|
160
|
+
}
|
|
161
|
+
if (!McpServerSchema.safeParse(draft).success || !draft.command && !draft.url) {
|
|
162
|
+
item.compatibility = "blocked";
|
|
163
|
+
item.findings.push("A valid command or URL is required");
|
|
164
|
+
}
|
|
165
|
+
if (base.scope === "project") {
|
|
166
|
+
item.compatibility = "blocked";
|
|
167
|
+
item.findings.push("Project-scoped MCP activation is not supported");
|
|
168
|
+
}
|
|
169
|
+
item.mcp = draft;
|
|
170
|
+
item.hash = digest(draft);
|
|
171
|
+
return item;
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { inspectMcp, inspectSkill, parseSkill };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ImportCandidate } from './types.js';
|
|
2
|
+
/** Small source records fit the existing retrieval budget without injecting entire documents. */
|
|
3
|
+
export declare function importContextDocument(item: ImportCandidate, projectId?: string): 'created' | 'unchanged' | 'kept';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
2
|
+
import { listKnowledgeItems, writeKnowledgeItem } from "../knowledge-memory/repository.js";
|
|
3
|
+
import "../knowledge-memory/index.js";
|
|
4
|
+
import { digest } from "./files.js";
|
|
5
|
+
//#region src/imports/contextImport.ts
|
|
6
|
+
/** Small source records fit the existing retrieval budget without injecting entire documents. */
|
|
7
|
+
function importContextDocument(item, projectId) {
|
|
8
|
+
const content = item.content?.trim();
|
|
9
|
+
if (!content) throw new Error("Missing context");
|
|
10
|
+
const scope = projectId ? {
|
|
11
|
+
type: "project",
|
|
12
|
+
id: projectId
|
|
13
|
+
} : { type: "global" };
|
|
14
|
+
const canonicalKey = `product-import:${item.source}:${digest(item.location)}`;
|
|
15
|
+
return runSqliteWriteTransaction(() => {
|
|
16
|
+
const existing = listKnowledgeItems({
|
|
17
|
+
canonicalKey,
|
|
18
|
+
scope,
|
|
19
|
+
statuses: [
|
|
20
|
+
"active",
|
|
21
|
+
"candidate",
|
|
22
|
+
"needs_review",
|
|
23
|
+
"stale",
|
|
24
|
+
"archived",
|
|
25
|
+
"rejected"
|
|
26
|
+
]
|
|
27
|
+
})[0];
|
|
28
|
+
if (existing) return existing.source.hash === item.hash ? "unchanged" : "kept";
|
|
29
|
+
for (let offset = 0, part = 0; offset < content.length; offset += 2e3, part++) {
|
|
30
|
+
const chunk = content.slice(offset, offset + 2e3).trim();
|
|
31
|
+
if (!chunk) continue;
|
|
32
|
+
writeKnowledgeItem({
|
|
33
|
+
kind: "note",
|
|
34
|
+
scope,
|
|
35
|
+
content: chunk,
|
|
36
|
+
canonicalKey: part === 0 ? canonicalKey : `${canonicalKey}:part:${part}`,
|
|
37
|
+
recordClass: "source_index",
|
|
38
|
+
status: "active",
|
|
39
|
+
confidence: .7,
|
|
40
|
+
importance: .7,
|
|
41
|
+
originClass: "system",
|
|
42
|
+
source: {
|
|
43
|
+
kind: "product_import",
|
|
44
|
+
product: item.source,
|
|
45
|
+
path: item.location,
|
|
46
|
+
hash: item.hash,
|
|
47
|
+
part,
|
|
48
|
+
importedAt: Date.now()
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return "created";
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { importContextDocument };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ImportFile } from './types.js';
|
|
2
|
+
export declare const IMPORT_LIMITS: {
|
|
3
|
+
file: number;
|
|
4
|
+
total: number;
|
|
5
|
+
count: number;
|
|
6
|
+
depth: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function digest(value: unknown): string;
|
|
9
|
+
export declare function fileHash(files: ImportFile[]): string;
|
|
10
|
+
export declare function inside(root: string, path: string): boolean;
|
|
11
|
+
export declare function safePath(path: string): boolean;
|
|
12
|
+
export declare function readRegularFile(path: string, limit?: number): Buffer;
|
|
13
|
+
export declare function readTree(rootPath: string): ImportFile[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ImportError } from "./types.js";
|
|
2
|
+
import { isAbsolute, join, relative } from "node:path";
|
|
3
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
//#region src/imports/files.ts
|
|
6
|
+
const IMPORT_LIMITS = {
|
|
7
|
+
file: 15 * 1024 * 1024,
|
|
8
|
+
total: 100 * 1024 * 1024,
|
|
9
|
+
count: 5e3,
|
|
10
|
+
depth: 16
|
|
11
|
+
};
|
|
12
|
+
function digest(value) {
|
|
13
|
+
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
14
|
+
}
|
|
15
|
+
function fileHash(files) {
|
|
16
|
+
return digest([...files].sort((a, b) => a.path.localeCompare(b.path, "en")));
|
|
17
|
+
}
|
|
18
|
+
function inside(root, path) {
|
|
19
|
+
const rel = relative(root, path);
|
|
20
|
+
return rel === "" || !isAbsolute(rel) && rel !== ".." && !rel.startsWith("../") && !rel.startsWith("..\\");
|
|
21
|
+
}
|
|
22
|
+
function safePath(path) {
|
|
23
|
+
return !!path && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") && path.split("/").every((p) => !!p && p !== "." && p !== ".." && !/[<>:"|?*]/.test(p) && !/[. ]$/.test(p) && !/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(p));
|
|
24
|
+
}
|
|
25
|
+
function readRegularFile(path, limit = IMPORT_LIMITS.file) {
|
|
26
|
+
if (!lstatSync(path).isFile()) throw new ImportError("unsafe_file", "Only regular files are supported");
|
|
27
|
+
const fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
28
|
+
try {
|
|
29
|
+
const before = fstatSync(fd);
|
|
30
|
+
if (!before.isFile() || before.size > limit) throw new ImportError("limit_exceeded", "File exceeds import limit", 413);
|
|
31
|
+
const data = readFileSync(fd);
|
|
32
|
+
const after = fstatSync(fd);
|
|
33
|
+
if (data.length > limit || before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new ImportError("source_changed", "Source changed while reading", 409);
|
|
34
|
+
return data;
|
|
35
|
+
} finally {
|
|
36
|
+
closeSync(fd);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function readTree(rootPath) {
|
|
40
|
+
const root = realpathSync(rootPath);
|
|
41
|
+
const files = [];
|
|
42
|
+
let total = 0;
|
|
43
|
+
const names = /* @__PURE__ */ new Set();
|
|
44
|
+
function walk(dir, prefix, depth) {
|
|
45
|
+
if (depth > IMPORT_LIMITS.depth) throw new ImportError("limit_exceeded", "Directory is too deep", 413);
|
|
46
|
+
for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
47
|
+
if ([
|
|
48
|
+
".git",
|
|
49
|
+
".DS_Store",
|
|
50
|
+
"__MACOSX"
|
|
51
|
+
].includes(entry.name)) continue;
|
|
52
|
+
const rel = prefix + entry.name;
|
|
53
|
+
if (!safePath(rel)) throw new ImportError("unsafe_file", "Unsupported file name");
|
|
54
|
+
const key = rel.normalize("NFC").toLowerCase();
|
|
55
|
+
if (names.has(key)) throw new ImportError("conflict", "File names collide on the target filesystem", 409);
|
|
56
|
+
names.add(key);
|
|
57
|
+
if (names.size > IMPORT_LIMITS.count) throw new ImportError("limit_exceeded", "Too many files", 413);
|
|
58
|
+
const path = join(dir, entry.name);
|
|
59
|
+
if (entry.isSymbolicLink() || !inside(root, realpathSync(path))) throw new ImportError("unsafe_file", "Symbolic links must be replaced with local files before importing");
|
|
60
|
+
if (entry.isDirectory()) walk(path, rel + "/", depth + 1);
|
|
61
|
+
else {
|
|
62
|
+
const data = readRegularFile(path);
|
|
63
|
+
total += data.length;
|
|
64
|
+
if (total > IMPORT_LIMITS.total) throw new ImportError("limit_exceeded", "Import is too large", 413);
|
|
65
|
+
files.push({
|
|
66
|
+
path: rel,
|
|
67
|
+
data: data.toString("base64"),
|
|
68
|
+
executable: (lstatSync(path).mode & 73) !== 0
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
walk(root, "", 0);
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { IMPORT_LIMITS, digest, fileHash, inside, readRegularFile, readTree, safePath };
|