@xopcai/xopc 0.0.284 → 0.0.285
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-CwbIMGLY.js → activity-store-B0BTIk9j.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DjiOld-S.js → agent-avatar-dicebear-Dc-0QZTQ.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CcmqK1Sc.js +1 -0
- package/dist/gateway/static/root/assets/agents-B_7PVt7S.js +1 -0
- package/dist/gateway/static/root/assets/api-COG_Tzpv.js +1 -0
- package/dist/gateway/static/root/assets/{api-3lphBVuY.js → api-Coly9ab6.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-B2nwCXxE.js → app-management-settings-panel-TWBO-msg.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BXO4Ej88.js → appearance-settings-CpTZ2KuT.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-6qxxKFsK.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-6ZRqNjla.js → archive-plugin-z_u6alob.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CmPZalcW.js → attachment-load-C2nS04TC.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-Bc3WbK5n.js → automations-page-aIBsw9zR.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BRQlU4qk.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-OWR6cxpc.js → binary-plugins-CGmXoZnl.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Ca-tDQXm.js → block-editor-CtvzCZhC.js} +3 -3
- package/dist/gateway/static/root/assets/{browser-automation-inputs-Dxy2_p3U.js → browser-automation-inputs-BOTU1isH.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-CibQIxpi.js → browser-automations-page-BwU5V8J0.js} +2 -2
- package/dist/gateway/static/root/assets/browser-settings-page-DDjbDeHQ.js +8 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DM1Bcf1x.js +8 -0
- package/dist/gateway/static/root/assets/channels-settings-Bq36CUxL.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-Bh-G9mdZ.js → channels-status-swr-Dfi0Desh.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-lbzbSpV3.js → chat-terminal-dock-B0MvcO6j.js} +2 -2
- package/dist/gateway/static/root/assets/connectors-page-L8JehrxF.js +2 -0
- package/dist/gateway/static/root/assets/{date-picker-tYvbTfZT.js → date-picker-DXOvbHCr.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-B8yo7-7V.js → dependency-picker-BEnC-eS0.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BUGkGB4-.js → desktop-pet-DtPNvznL.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DNeVGZNp.js → desktop-pet-settings-CYyjcSn6.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-BPMGvAKE.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CiK0QI2p.js → extension-debug-page-H_aPMGXW.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CirPNHJd.js → extension-page-C1JnpeMt.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-Baua1_Le.js → extension-settings-page-C75kCOqe.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Co4hHe0i.js → gateway-config-swr-G2lQt7Ak.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-BCF732TU.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DBPNJG-e.js → gateway-startup-retry-GP31PUrG.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-DTM_kYpE.js → heartbeat-settings-C7fjiowt.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-DZXeXdUL.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-D0rMTumD.js → image-generation-api-CKFhfHSw.js} +1 -1
- package/dist/gateway/static/root/assets/imports-page-GROumhj8.js +1 -0
- package/dist/gateway/static/root/assets/{index-C9aQA4Dr.js → index-ChIrvv3t.js} +47 -47
- package/dist/gateway/static/root/assets/index-R1VNlHjj.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CMUaF61L.js → keyboard-shortcuts-settings-zcI822m_.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CAEKGTAg.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-CHxyT09t.js → local-apps-page-BtgDJ92Q.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-DO6JJ1l2.js → locale-store-DlOv7896.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DHZ4BEkG.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-jXbA2S54.js → management-settings-DasZxCyD.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-CKInRsDh.js → markdown-view-6oYdBahT.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-BaPkSvW7.js → media-plugins-CxthbBvk.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-BgZRsCkG.js → messages-Ls1Aaiqd.js} +1 -1
- package/dist/gateway/static/root/assets/note-detail-panel-BcWq3ddw.js +5 -0
- package/dist/gateway/static/root/assets/notes-page-DfZ8QWyv.js +8 -0
- package/dist/gateway/static/root/assets/{page-tabs-CdYemUOu.js → page-tabs-BG39KidE.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DzgF1yYO.js → preview-open-alternatives-Bevpbq9f.js} +1 -1
- package/dist/gateway/static/root/assets/proactive-page-D2U_xQaS.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-iQ-eW6Aj.js → product-open-page-Bi1seZpM.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CzlGTW4F.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-mQsM-TsP.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-DMNudT60.js → registry-api-D0ddwBFj.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Cb56JcO1.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-u_wVepf7.js → runtime-tools-settings-panel-Wkru8_My.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-YdZj2xdI.js → schema-form-CSMRzj9Y.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Hj9uFcIO.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CV4-_EVA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-Bw6xKOX5.js → settings-form-section-wcAdCkK8.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-CV8kVdnE.js → settings-page-BcQGAnCs.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DzxmsaQG.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BHwjHlrV.js → share-preview-page-DQocUl-1.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-xlJh1ccG.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-wSXJRSxX.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-DRKdb6O5.js → skills-page-AmqtBJo7.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-DYfaLtts.js → skills-page.utils-DtlfCgJp.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-CWrWpKV-.js → system-settings-panel-Ct698Nf-.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-sQpwq5-c.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-CDc7Wqy5.js → theme-store-ng8KJsAT.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-Bmsy-5Fs.js → tunnel-api-Boajol0M.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-D-FRib85.js → use-autosave-BSyxRHt2.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-DIEHnAER.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-iQ7EBzII.js → voice-api-key-field-SE3OQI1s.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-DNMWA2tx.js → voice-config-api-VNuCmdFe.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BesuhDlH.js → work-discovery-overlay-Dn1vy2oq.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Du81FoCy.js → workflow-run-setup-panel-BJEfGY-k.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DgRNIyr7.js +6 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/proactive.js +13 -4
- package/dist/src/agent/coding/repository-instructions.js +1 -1
- package/dist/src/agent/coding/workspace-revision.js +1 -1
- package/dist/src/agent/commands/command-isolation.js +1 -1
- package/dist/src/agent/tools/factory.js +7 -3
- package/dist/src/agent/tools/review-workspace.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +10 -2
- package/dist/src/cli/command-manifest.js +2 -2
- 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 +2 -2
- package/dist/src/cli/commands/gateway/run-foreground.js +3 -0
- package/dist/src/cli/commands/import.js +54 -6
- 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/discussions/action-tasks.js +1 -1
- package/dist/src/discussions/edits.js +1 -1
- package/dist/src/discussions/index.js +1 -1
- package/dist/src/discussions/organizer.js +1 -1
- package/dist/src/discussions/recording.js +1 -1
- package/dist/src/discussions/sealer.js +1 -1
- package/dist/src/discussions/service.js +2 -2
- package/dist/src/endpoint-tools/invocation-service.d.ts +2 -0
- package/dist/src/endpoint-tools/invocation-service.js +48 -19
- package/dist/src/gateway/heartbeat/check-store.d.ts +18 -0
- package/dist/src/gateway/heartbeat/check-store.js +73 -0
- package/dist/src/gateway/heartbeat/service.d.ts +12 -3
- package/dist/src/gateway/heartbeat/service.js +62 -45
- package/dist/src/gateway/hono/routes/discussions.js +2 -2
- package/dist/src/gateway/hono/routes/imports.js +18 -6
- package/dist/src/gateway/hono/routes/proactive-control.js +2 -1
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service.d.ts +7 -0
- package/dist/src/gateway/service.js +24 -9
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/imports/importService.d.ts +17 -12
- package/dist/src/imports/importService.js +67 -70
- package/dist/src/imports/inventory.d.ts +11 -0
- package/dist/src/imports/inventory.js +202 -0
- package/dist/src/imports/productImport.d.ts +4 -4
- package/dist/src/imports/productImport.js +201 -108
- package/dist/src/imports/runtime.js +7 -2
- package/dist/src/imports/scanner.d.ts +1 -1
- package/dist/src/imports/scanner.js +4 -4
- package/dist/src/imports/types.d.ts +53 -0
- package/dist/src/infra/background-task.d.ts +2 -0
- package/dist/src/infra/background-task.js +25 -0
- package/dist/src/infra/process-diagnostics.d.ts +5 -0
- package/dist/src/infra/process-diagnostics.js +97 -0
- package/dist/src/infra/unhandled-rejections.js +6 -3
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/knowledge/ingestion-service.js +1 -0
- package/dist/src/media-shared/http/ssrf-guard.js +1 -1
- package/dist/src/notifications/proactive-policy.js +7 -5
- package/dist/src/proactive/execution/authorization.d.ts +2 -0
- package/dist/src/proactive/execution/authorization.js +10 -1
- package/dist/src/proactive/execution/context.js +1 -0
- package/dist/src/proactive/execution/repository.d.ts +2 -0
- package/dist/src/proactive/execution/repository.js +19 -2
- package/dist/src/proactive/execution/worker.js +14 -4
- package/dist/src/proactive/experience.d.ts +4 -1
- package/dist/src/proactive/experience.js +3 -0
- package/dist/src/proactive/follow-ups.d.ts +5 -0
- package/dist/src/proactive/follow-ups.js +18 -7
- package/dist/src/proactive/inbox/cards.js +2 -1
- package/dist/src/proactive/inbox/delivery.js +6 -7
- package/dist/src/proactive/inbox/digest.d.ts +1 -1
- package/dist/src/proactive/inbox/digest.js +23 -13
- package/dist/src/proactive/inbox/notification-policy.d.ts +1 -1
- package/dist/src/proactive/inbox/notification-policy.js +9 -6
- package/dist/src/proactive/inbox/repository.js +3 -2
- package/dist/src/proactive/inbox/service.d.ts +6 -0
- package/dist/src/proactive/inbox/service.js +18 -0
- package/dist/src/proactive/inbox/types.d.ts +1 -0
- package/dist/src/proactive/policy/presence.d.ts +1 -1
- package/dist/src/proactive/policy/presence.js +7 -5
- package/dist/src/proactive/policy/service.d.ts +8 -5
- package/dist/src/proactive/policy/service.js +12 -12
- package/dist/src/proactive/scenarios/control.d.ts +3 -3
- package/dist/src/proactive/scenarios/control.js +12 -2
- package/dist/src/proactive/source-freshness.d.ts +8 -0
- package/dist/src/proactive/source-freshness.js +27 -0
- package/dist/src/proactive/status.d.ts +2 -0
- package/dist/src/proactive/status.js +21 -0
- package/dist/src/proactive/temporal/worker.js +2 -1
- package/dist/src/realtime/runtime.js +31 -5
- package/dist/src/realtime/writer.js +16 -9
- package/dist/src/storage/sqlite/import-repository.d.ts +2 -3
- package/dist/src/storage/sqlite/import-repository.js +3 -3
- package/dist/src/storage/sqlite/import-selection-repository.d.ts +15 -0
- package/dist/src/storage/sqlite/import-selection-repository.js +28 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +2 -2
- package/dist/src/storage/sqlite/migrations/175_import_selections.sql +8 -0
- package/dist/src/storage/sqlite/migrations/176_proactive_quality.sql +5 -0
- package/dist/src/storage/sqlite/migrations/177_proactive_check_controls.sql +25 -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/dist/src/work-discovery/candidate-discovery.js +1 -1
- package/dist/src/work-discovery/probe.js +1 -1
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-D9qP10BK.js +0 -1
- package/dist/gateway/static/root/assets/agents-D-bkOuAl.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-BKwBBwJN.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-B2-f9qZu.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-Dtn4rMeq.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DiwtR9MD.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-pTdR1q1K.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BSQ7Hs2w.js +0 -2
- package/dist/gateway/static/root/assets/device-pairing-wizard-CYkT7ecp.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CzpSYWex.js +0 -1
- package/dist/gateway/static/root/assets/home-page-CSSJvdd6.js +0 -1
- package/dist/gateway/static/root/assets/imports-page-XqVVYbAp.js +0 -1
- package/dist/gateway/static/root/assets/index-B_Sg5BKz.css +0 -2
- package/dist/gateway/static/root/assets/local-app-workbench-page-CZSgTYuI.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-z5ktnEzX.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-panel-DSaZfkna.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-CePEyv_C.js +0 -8
- package/dist/gateway/static/root/assets/proactive-page-Cy2Gm2f8.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-BliwYpns.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-Dm-BqnQn.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-C_XuQ9XR.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Drt1aS2G.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BAUd06_9.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BHI9e5Tw.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DOAOjZBQ.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DU0VQpHj.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-DiPgMmBM.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-DxRaRdA0.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-CZikdgNw.js +0 -6
|
@@ -1,9 +1,11 @@
|
|
|
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 { proactiveChecksAllowed, proactivePreferences } from "../../proactive/policy/service.js";
|
|
5
|
+
import { beginHeartbeatCheck, completeHeartbeatCheck, deliverHeartbeatChecks, recentHeartbeatChecks, recoverHeartbeatChecks } from "./check-store.js";
|
|
6
|
+
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
4
7
|
import { isWithinActiveHours } from "../../heartbeat/active-hours.js";
|
|
5
8
|
import { isHeartbeatContentEmpty } from "../../heartbeat/content-check.js";
|
|
6
|
-
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
7
9
|
import { createHeartbeatWake } from "../../heartbeat/wake.js";
|
|
8
10
|
import { resolveHeartbeatMdPath } from "../workspace-heartbeat-path.js";
|
|
9
11
|
import { readFile } from "fs/promises";
|
|
@@ -31,22 +33,41 @@ function heartbeatRunnerConfigFromConfig(cfg) {
|
|
|
31
33
|
var HeartbeatService = class {
|
|
32
34
|
intervalId = null;
|
|
33
35
|
wake;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
nextCheckAt = null;
|
|
37
|
+
deliveryTimer = null;
|
|
38
|
+
delivering = false;
|
|
39
|
+
recovered = false;
|
|
36
40
|
runnerConfig = null;
|
|
37
41
|
constructor(deps) {
|
|
38
42
|
this.deps = deps;
|
|
39
|
-
this.wake = createHeartbeatWake((reasons) =>
|
|
43
|
+
this.wake = createHeartbeatWake(async (reasons) => {
|
|
44
|
+
try {
|
|
45
|
+
await this.runHeartbeatOnce(reasons);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
log.error({ err }, "Heartbeat check failed unexpectedly");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
40
50
|
}
|
|
41
51
|
start(config) {
|
|
52
|
+
if (this.runnerConfig) this.stop();
|
|
42
53
|
if (!config.enabled) {
|
|
43
54
|
log.info("Heartbeat disabled");
|
|
44
55
|
this.runnerConfig = null;
|
|
45
56
|
return;
|
|
46
57
|
}
|
|
47
58
|
this.runnerConfig = config;
|
|
59
|
+
if (!this.recovered) {
|
|
60
|
+
recoverHeartbeatChecks(this.deps.getWorkspace());
|
|
61
|
+
this.recovered = true;
|
|
62
|
+
}
|
|
63
|
+
this.deliveryTimer = setInterval(() => {
|
|
64
|
+
this.drainDeliveries().catch((err) => log.warn({ err }, "Heartbeat delivery check failed"));
|
|
65
|
+
}, 1e4);
|
|
66
|
+
this.deliveryTimer.unref?.();
|
|
67
|
+
this.nextCheckAt = new Date(Date.now() + config.intervalMs).toISOString();
|
|
48
68
|
log.info({ intervalMs: config.intervalMs }, "Heartbeat timer started (interval wake)");
|
|
49
69
|
this.intervalId = setInterval(() => {
|
|
70
|
+
this.nextCheckAt = new Date(Date.now() + config.intervalMs).toISOString();
|
|
50
71
|
this.wake.request({ reason: "interval" });
|
|
51
72
|
}, config.intervalMs);
|
|
52
73
|
this.intervalId.unref?.();
|
|
@@ -56,6 +77,9 @@ var HeartbeatService = class {
|
|
|
56
77
|
this.wake.request({ reason: opts?.reason ?? "manual" });
|
|
57
78
|
}
|
|
58
79
|
stop() {
|
|
80
|
+
if (this.deliveryTimer) clearInterval(this.deliveryTimer);
|
|
81
|
+
this.deliveryTimer = null;
|
|
82
|
+
this.nextCheckAt = null;
|
|
59
83
|
if (this.intervalId) {
|
|
60
84
|
clearInterval(this.intervalId);
|
|
61
85
|
this.intervalId = null;
|
|
@@ -73,6 +97,24 @@ var HeartbeatService = class {
|
|
|
73
97
|
isRunning() {
|
|
74
98
|
return this.intervalId !== null;
|
|
75
99
|
}
|
|
100
|
+
status() {
|
|
101
|
+
const preferences = proactivePreferences(this.deps.getWorkspace());
|
|
102
|
+
return {
|
|
103
|
+
enabled: Boolean(this.runnerConfig?.enabled),
|
|
104
|
+
checksAllowed: proactiveChecksAllowed(preferences),
|
|
105
|
+
nextCheckAt: proactiveChecksAllowed(preferences) ? this.nextCheckAt : null,
|
|
106
|
+
recent: recentHeartbeatChecks(this.deps.getWorkspace())
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async drainDeliveries() {
|
|
110
|
+
if (this.delivering || !this.runnerConfig?.enabled) return;
|
|
111
|
+
this.delivering = true;
|
|
112
|
+
try {
|
|
113
|
+
await deliverHeartbeatChecks(this.deps.getWorkspace(), this.deps.messageBus, this.runnerConfig.target?.trim(), this.runnerConfig.targetChatId?.trim());
|
|
114
|
+
} finally {
|
|
115
|
+
this.delivering = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
76
118
|
async runHeartbeatOnce(reasons) {
|
|
77
119
|
const reasonSummary = [...new Set(reasons)].join(", ") || "unknown";
|
|
78
120
|
const cfg = this.runnerConfig;
|
|
@@ -80,36 +122,34 @@ var HeartbeatService = class {
|
|
|
80
122
|
log.debug({ reasons: reasonSummary }, "Heartbeat: skip (disabled)");
|
|
81
123
|
return;
|
|
82
124
|
}
|
|
125
|
+
if (!proactiveChecksAllowed(proactivePreferences(this.deps.getWorkspace()))) return;
|
|
83
126
|
if (cfg.activeHours && !isWithinActiveHours(cfg.activeHours)) {
|
|
84
127
|
log.debug({ reasons: reasonSummary }, "Heartbeat: skip (outside active hours)");
|
|
85
128
|
return;
|
|
86
129
|
}
|
|
130
|
+
const checkId = beginHeartbeatCheck(this.deps.getWorkspace());
|
|
87
131
|
const heartbeatPath = resolveHeartbeatMdPath(this.deps.getConfig());
|
|
88
132
|
if (!heartbeatPath) {
|
|
89
|
-
|
|
133
|
+
completeHeartbeatCheck(checkId, "blocked", "No workspace checklist path");
|
|
90
134
|
return;
|
|
91
135
|
}
|
|
92
136
|
let heartbeatContent;
|
|
93
137
|
try {
|
|
94
138
|
const raw = await readFile(heartbeatPath, "utf-8");
|
|
95
139
|
if (isHeartbeatContentEmpty(raw)) {
|
|
96
|
-
|
|
97
|
-
path: heartbeatPath,
|
|
98
|
-
reasons: reasonSummary
|
|
99
|
-
}, "Heartbeat: skip (HEARTBEAT.md empty)");
|
|
140
|
+
completeHeartbeatCheck(checkId, "empty_checklist");
|
|
100
141
|
return;
|
|
101
142
|
}
|
|
102
143
|
heartbeatContent = raw.trim();
|
|
103
144
|
} catch {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
reasons: reasonSummary
|
|
107
|
-
}, "Heartbeat: HEARTBEAT.md missing; continuing");
|
|
145
|
+
completeHeartbeatCheck(checkId, "blocked", "Checklist could not be read");
|
|
146
|
+
return;
|
|
108
147
|
}
|
|
109
148
|
const sessionKey = cfg.isolatedSession ? `heartbeat:isolated:${Date.now()}` : "heartbeat:main";
|
|
110
149
|
let basePrompt = (cfg.prompt?.trim() || DEFAULT_PROMPT).trim();
|
|
111
150
|
if (heartbeatContent) basePrompt = `${basePrompt}\n\n---\nHEARTBEAT.md:\n${heartbeatContent}\n---`;
|
|
112
151
|
basePrompt = appendCronEventLines(basePrompt, reasons);
|
|
152
|
+
basePrompt += "\nReturn notifications in your final response. Do not send messages, media or voice directly; delivery follows the user notification policy.";
|
|
113
153
|
const prompt = `${basePrompt}\n\nCurrent time: ${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
114
154
|
const ackMax = cfg.ackMaxChars ?? 300;
|
|
115
155
|
log.debug({
|
|
@@ -143,14 +183,21 @@ var HeartbeatService = class {
|
|
|
143
183
|
source: "heartbeat"
|
|
144
184
|
});
|
|
145
185
|
} catch (error) {
|
|
186
|
+
completeHeartbeatCheck(checkId, "failed", "Agent check failed; see gateway logs");
|
|
146
187
|
log.error({ err: error }, "Heartbeat: agent call failed");
|
|
147
188
|
return;
|
|
148
189
|
}
|
|
190
|
+
if (cfg !== this.runnerConfig || !proactiveChecksAllowed(proactivePreferences(this.deps.getWorkspace()))) {
|
|
191
|
+
completeHeartbeatCheck(checkId, "cancelled", "Checks paused or configuration changed");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
149
194
|
if (!reply?.trim()) {
|
|
195
|
+
completeHeartbeatCheck(checkId, "no_change");
|
|
150
196
|
log.debug({ reasons: reasonSummary }, "Heartbeat: skip (empty model reply)");
|
|
151
197
|
return;
|
|
152
198
|
}
|
|
153
199
|
if (shouldSilence(reply, ackMax)) {
|
|
200
|
+
completeHeartbeatCheck(checkId, "no_change");
|
|
154
201
|
log.info({
|
|
155
202
|
ackMax,
|
|
156
203
|
replyChars: reply.length,
|
|
@@ -159,38 +206,8 @@ var HeartbeatService = class {
|
|
|
159
206
|
return;
|
|
160
207
|
}
|
|
161
208
|
const { stripped } = stripHeartbeatToken(reply);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
log.info({
|
|
165
|
-
finalTextChars: finalText.length,
|
|
166
|
-
reasons: reasonSummary
|
|
167
|
-
}, "Heartbeat: not sent — duplicate within 24h");
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const target = cfg.target?.trim();
|
|
171
|
-
const targetChatId = cfg.targetChatId?.trim();
|
|
172
|
-
if (Boolean(target && targetChatId)) {
|
|
173
|
-
await this.deps.messageBus.publishOutbound({
|
|
174
|
-
channel: target,
|
|
175
|
-
chat_id: targetChatId,
|
|
176
|
-
content: finalText,
|
|
177
|
-
type: "message"
|
|
178
|
-
});
|
|
179
|
-
log.info({
|
|
180
|
-
reasons: reasonSummary,
|
|
181
|
-
channel: target,
|
|
182
|
-
chatId: targetChatId,
|
|
183
|
-
contentChars: finalText.length
|
|
184
|
-
}, "Heartbeat: sent — outbound queued");
|
|
185
|
-
} else log.info({
|
|
186
|
-
reasons: reasonSummary,
|
|
187
|
-
finalTextChars: finalText.length
|
|
188
|
-
}, "Heartbeat: not sent — no delivery target (set gateway.heartbeat.target + targetChatId)");
|
|
189
|
-
this.lastHeartbeatText = finalText;
|
|
190
|
-
this.lastHeartbeatAt = Date.now();
|
|
191
|
-
}
|
|
192
|
-
isDuplicate(text) {
|
|
193
|
-
return text.trim() === this.lastHeartbeatText.trim() && Date.now() - this.lastHeartbeatAt < 1440 * 60 * 1e3;
|
|
209
|
+
completeHeartbeatCheck(checkId, "prepared", void 0, stripped || reply.trim(), cfg.target?.trim(), cfg.targetChatId?.trim());
|
|
210
|
+
await this.drainDeliveries();
|
|
194
211
|
}
|
|
195
212
|
};
|
|
196
213
|
//#endregion
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DISCUSSION_STATUSES } from "../../../../packages/gateway-contract/src/discussions.js";
|
|
2
2
|
import { hasGatewayScope } from "../../security/gateway-scopes.js";
|
|
3
3
|
import { DiscussionServiceError } from "../../../discussions/errors.js";
|
|
4
|
-
import { convertMeetingAction, meetingActionTasks } from "../../../discussions/action-tasks.js";
|
|
5
4
|
import { MeetingEditSchema } from "../../../discussions/edits.js";
|
|
6
|
-
import { exportDiscussion } from "../../../discussions/export.js";
|
|
7
5
|
import "../../../discussions/index.js";
|
|
8
6
|
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
7
|
+
import { convertMeetingAction, meetingActionTasks } from "../../../discussions/action-tasks.js";
|
|
8
|
+
import { exportDiscussion } from "../../../discussions/export.js";
|
|
9
9
|
import { createReadStream } from "node:fs";
|
|
10
10
|
import { stat } from "node:fs/promises";
|
|
11
11
|
import { Readable } from "node:stream";
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
2
2
|
import { ImportError } from "../../../imports/types.js";
|
|
3
3
|
import { isImportSource } from "../../../imports/sources.js";
|
|
4
|
+
import { ImportSelectionRepository } from "../../../storage/sqlite/import-selection-repository.js";
|
|
5
|
+
import { getLiveInventory, previewInventoryItem, publicInventory, scanProduct } from "../../../imports/inventory.js";
|
|
6
|
+
import { importSelection, listImportSources } from "../../../imports/productImport.js";
|
|
4
7
|
import { createRuntimeImportService } from "../../../imports/runtime.js";
|
|
5
|
-
import { importProduct, listImportSources } from "../../../imports/productImport.js";
|
|
6
8
|
import { z } from "zod";
|
|
7
9
|
//#region src/gateway/hono/routes/imports.ts
|
|
8
|
-
const
|
|
10
|
+
const selectionSchema = z.object({
|
|
11
|
+
inventoryId: z.string().uuid(),
|
|
12
|
+
candidateIds: z.array(z.string().uuid()).min(1).max(5e3),
|
|
13
|
+
requestId: z.string().uuid(),
|
|
14
|
+
retryOf: z.string().uuid().optional()
|
|
15
|
+
}).strict();
|
|
9
16
|
function registerImportRoutes(app, deps) {
|
|
17
|
+
const service = () => createRuntimeImportService("gateway-owner", () => deps.service.agentService.refreshSkillsAfterDiskChange(), (root) => deps.service.agentService.getWorkspaceTrust(root).trusted);
|
|
10
18
|
const respond = async (c, run) => {
|
|
11
19
|
try {
|
|
12
20
|
if (getGatewayPrincipal(c).kind !== "owner") throw new ImportError("owner_required", "Only the Gateway owner can import local capabilities", 403);
|
|
@@ -28,17 +36,21 @@ function registerImportRoutes(app, deps) {
|
|
|
28
36
|
return c.json({
|
|
29
37
|
ok: false,
|
|
30
38
|
code: "import_failed",
|
|
31
|
-
error: "Import could not finish.
|
|
39
|
+
error: "Import could not finish. Existing content is preserved."
|
|
32
40
|
}, 400);
|
|
33
41
|
}
|
|
34
42
|
};
|
|
35
43
|
app.get("/api/imports/sources", (c) => respond(c, () => ({ sources: listImportSources("gateway-owner") })));
|
|
36
|
-
app.post("/api/imports/sources/:source/
|
|
44
|
+
app.post("/api/imports/sources/:source/scan", deps.strictRateLimitMiddleware, (c) => respond(c, async () => {
|
|
37
45
|
const source = c.req.param("source");
|
|
38
46
|
if (!isImportSource(source)) throw new ImportError("unsupported_source", "Unsupported source");
|
|
39
|
-
|
|
40
|
-
return
|
|
47
|
+
z.object({}).strict().parse(await c.req.json());
|
|
48
|
+
return scanProduct(service(), source);
|
|
41
49
|
}));
|
|
50
|
+
app.get("/api/imports/inventories/:id", (c) => respond(c, () => publicInventory(getLiveInventory("gateway-owner", c.req.param("id")))));
|
|
51
|
+
app.get("/api/imports/inventories/:id/items/:itemId/preview", (c) => respond(c, () => previewInventoryItem(service(), "gateway-owner", c.req.param("id"), c.req.param("itemId"))));
|
|
52
|
+
app.post("/api/imports/runs", deps.strictRateLimitMiddleware, (c) => respond(c, async () => importSelection(service(), selectionSchema.parse(await c.req.json()))));
|
|
53
|
+
app.get("/api/imports/runs/:id", (c) => respond(c, () => new ImportSelectionRepository("gateway-owner").get("run", c.req.param("id"))));
|
|
42
54
|
}
|
|
43
55
|
//#endregion
|
|
44
56
|
export { registerImportRoutes };
|
|
@@ -33,7 +33,8 @@ function registerProactiveControlRoutes(app, deps) {
|
|
|
33
33
|
}));
|
|
34
34
|
app.get("/api/proactive/overview", (c) => c.json({
|
|
35
35
|
ok: true,
|
|
36
|
-
...delegationOverview(workspace())
|
|
36
|
+
...delegationOverview(workspace()),
|
|
37
|
+
heartbeat: deps.service.heartbeatStatus()
|
|
37
38
|
}));
|
|
38
39
|
app.post("/api/proactive/delegations", deps.strictRateLimitMiddleware, async (c) => c.json({
|
|
39
40
|
ok: true,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from "../config/loader.js";
|
|
2
|
-
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
3
2
|
import "../config/index.js";
|
|
3
|
+
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
4
4
|
import { runBootstrapMigrationsSync } from "../migrations/runner.js";
|
|
5
5
|
import { restartGatewayProcessWithFreshPid } from "./respawn.js";
|
|
6
6
|
import { acquireGatewayLock } from "./lock.js";
|
|
@@ -124,6 +124,7 @@ export declare class GatewayService {
|
|
|
124
124
|
private ensureHeartbeatService;
|
|
125
125
|
private createTaskRunDispatcher;
|
|
126
126
|
private createNotificationService;
|
|
127
|
+
private readonly taskRunDispatch;
|
|
127
128
|
dispatchTaskRuns(): void;
|
|
128
129
|
ensureTaskConversation(taskId: string, options?: {
|
|
129
130
|
runId?: string;
|
|
@@ -255,6 +256,12 @@ export declare class GatewayService {
|
|
|
255
256
|
manageable: boolean;
|
|
256
257
|
order: number;
|
|
257
258
|
}>;
|
|
259
|
+
heartbeatStatus(): {
|
|
260
|
+
enabled: boolean;
|
|
261
|
+
checksAllowed: boolean;
|
|
262
|
+
nextCheckAt: string;
|
|
263
|
+
recent: import("./heartbeat/check-store.js").HeartbeatCheck[];
|
|
264
|
+
};
|
|
258
265
|
/**
|
|
259
266
|
* Request an immediate heartbeat run (coalesced like interval/cron wakes).
|
|
260
267
|
*/
|
|
@@ -63,23 +63,24 @@ import { ConnectionRecoveryService } from "../connectors/connection-recovery-ser
|
|
|
63
63
|
import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
|
|
64
64
|
import { ConnectedSourceChangePublisher } from "../connectors/source-change-publisher.js";
|
|
65
65
|
import { startConnectorSupervisor } from "../connectors/supervisor.js";
|
|
66
|
-
import { DiscussionService } from "../discussions/service.js";
|
|
67
|
-
import { DiscussionOrganizer } from "../discussions/organizer.js";
|
|
68
|
-
import { DiscussionLiveWorker } from "../discussions/live-worker.js";
|
|
69
|
-
import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
|
|
70
|
-
import { DiscussionSealer } from "../discussions/sealer.js";
|
|
71
|
-
import "../discussions/index.js";
|
|
72
66
|
import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
|
|
73
67
|
import "../endpoint-tools/index.js";
|
|
74
68
|
import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
|
|
75
69
|
import { prepareAutomationAgentSession } from "./automation-agent-session.js";
|
|
76
70
|
import { stopTailscaleExposure } from "./tailscale-lifecycle.js";
|
|
71
|
+
import { createBackgroundTask } from "../infra/background-task.js";
|
|
77
72
|
import { buildTaskAgentContext } from "../agent/source-context/task-context.js";
|
|
78
73
|
import { deliverProactiveCard } from "../proactive/inbox/delivery.js";
|
|
79
74
|
import { WorkDiscoveryService } from "../work-discovery/service.js";
|
|
80
75
|
import { buildVoiceMemoryContext } from "../voice/realtime/memory-context.js";
|
|
81
76
|
import { buildVoicePersonaContext } from "../voice/realtime/persona-context.js";
|
|
82
77
|
import { voiceMemoryBudget, voicePersonaBudget } from "../voice/realtime/conversation-context.js";
|
|
78
|
+
import { DiscussionService } from "../discussions/service.js";
|
|
79
|
+
import { DiscussionOrganizer } from "../discussions/organizer.js";
|
|
80
|
+
import { DiscussionLiveWorker } from "../discussions/live-worker.js";
|
|
81
|
+
import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
|
|
82
|
+
import { DiscussionSealer } from "../discussions/sealer.js";
|
|
83
|
+
import "../discussions/index.js";
|
|
83
84
|
import { NotesStore } from "../notes/store.js";
|
|
84
85
|
import { buildNoteAgentContext } from "../notes/agent-context.js";
|
|
85
86
|
import { NotesService } from "../notes/service.js";
|
|
@@ -665,7 +666,8 @@ var GatewayService = class {
|
|
|
665
666
|
agentService: this.ensureAgentService(),
|
|
666
667
|
messageBus: this.bus,
|
|
667
668
|
sessionStore: this.sessionIndex.getStore(),
|
|
668
|
-
getConfig: () => this.config
|
|
669
|
+
getConfig: () => this.config,
|
|
670
|
+
getWorkspace: () => this.workspacePath
|
|
669
671
|
});
|
|
670
672
|
return this.heartbeatService;
|
|
671
673
|
}
|
|
@@ -721,11 +723,21 @@ var GatewayService = class {
|
|
|
721
723
|
});
|
|
722
724
|
return this.notificationService;
|
|
723
725
|
}
|
|
724
|
-
|
|
726
|
+
taskRunDispatch = createBackgroundTask(async () => {
|
|
725
727
|
new TaskSignalService().tick();
|
|
726
728
|
this.dispatchTaskEvents();
|
|
727
729
|
this.createTaskRunDispatcher().dispatch();
|
|
728
|
-
this.createWorkflowRunService().dispatchTaskRuns();
|
|
730
|
+
await this.createWorkflowRunService().dispatchTaskRuns();
|
|
731
|
+
}, (err) => {
|
|
732
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
733
|
+
log.error({
|
|
734
|
+
err,
|
|
735
|
+
errorMessage,
|
|
736
|
+
phase: "task_run_dispatch"
|
|
737
|
+
}, `Task dispatch failed: ${errorMessage}`);
|
|
738
|
+
});
|
|
739
|
+
dispatchTaskRuns() {
|
|
740
|
+
this.taskRunDispatch();
|
|
729
741
|
}
|
|
730
742
|
async ensureTaskConversation(taskId, options = {}) {
|
|
731
743
|
const task = new TaskRepository().require(taskId);
|
|
@@ -1431,6 +1443,9 @@ var GatewayService = class {
|
|
|
1431
1443
|
return a.id.localeCompare(b.id);
|
|
1432
1444
|
});
|
|
1433
1445
|
}
|
|
1446
|
+
heartbeatStatus() {
|
|
1447
|
+
return this.heartbeatService?.status() ?? null;
|
|
1448
|
+
}
|
|
1434
1449
|
/**
|
|
1435
1450
|
* Request an immediate heartbeat run (coalesced like interval/cron wakes).
|
|
1436
1451
|
*/
|
|
@@ -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";
|
|
2
3
|
import { isWithinActiveHours } from "./active-hours.js";
|
|
3
4
|
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 };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type ScanInput } from './scanner.js';
|
|
2
|
-
import { type ImportAction, type ImportJob, type ImportScan, type ImportTarget } from './types.js';
|
|
2
|
+
import { type ImportAction, type ImportFile, type ImportJob, type ImportScan, type ImportTarget } from './types.js';
|
|
3
3
|
export interface ImportServiceOptions {
|
|
4
4
|
stateDir: string;
|
|
5
5
|
owner: string;
|
|
6
6
|
validateTarget?: (target: ImportTarget) => void;
|
|
7
|
-
isConnected?: (candidate: ImportScan['candidates'][number]) => boolean;
|
|
7
|
+
isConnected?: (candidate: ImportScan['candidates'][number], workspaceRoot?: string) => boolean;
|
|
8
8
|
reservedNames?: (target: ImportTarget) => string[];
|
|
9
|
+
isWorkspaceTrusted?: (root: string) => boolean;
|
|
9
10
|
refreshSkills?: () => void | Promise<void>;
|
|
10
11
|
}
|
|
11
12
|
export declare class ImportService {
|
|
@@ -13,20 +14,24 @@ export declare class ImportService {
|
|
|
13
14
|
private readonly repo;
|
|
14
15
|
private readonly root;
|
|
15
16
|
constructor(options: ImportServiceOptions);
|
|
16
|
-
scan(input: ScanInput): ImportScan
|
|
17
|
+
scan(input: ScanInput): Promise<ImportScan>;
|
|
17
18
|
getScan(id: string): ImportScan;
|
|
18
|
-
|
|
19
|
+
discardUnusedScan(id: string): void;
|
|
20
|
+
storeScan(scan: ImportScan, workspaceRoot?: string): ImportScan;
|
|
19
21
|
plan(scanId: string, target: ImportTarget, choices: Array<Pick<ImportAction, 'candidateId' | 'operation'> & {
|
|
20
22
|
name?: string;
|
|
21
23
|
}>): import("./types.js").ImportPlan;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
describeSkills(scan: ImportScan, target: ImportTarget): {
|
|
25
|
+
candidate: import("./types.js").ImportCandidate;
|
|
26
|
+
status: import("./types.js").InventoryStatus;
|
|
27
|
+
reason: string;
|
|
28
|
+
targetName: string;
|
|
29
|
+
}[];
|
|
30
|
+
readSnapshot(scanId: string, candidateId: string): ImportFile[];
|
|
31
|
+
jobByKey(key: string): ImportJob;
|
|
32
|
+
validateTarget(target: ImportTarget): void;
|
|
33
|
+
reservedNames(target: ImportTarget): string[];
|
|
34
|
+
isWorkspaceTrusted(root: string): boolean;
|
|
30
35
|
getPlan(id: string): import("./types.js").ImportPlan;
|
|
31
36
|
apply(planId: string, key: string): ImportJob;
|
|
32
37
|
private renameSkill;
|
|
@@ -30,13 +30,23 @@ var ImportService = class {
|
|
|
30
30
|
mode: 448
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
-
scan(input) {
|
|
34
|
-
return this.storeScan(scanLocal(input));
|
|
33
|
+
async scan(input) {
|
|
34
|
+
return this.storeScan(await scanLocal(input), input.projectRoot);
|
|
35
35
|
}
|
|
36
36
|
getScan(id) {
|
|
37
37
|
return this.repo.get("scan", id);
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
discardUnusedScan(id) {
|
|
40
|
+
this.lock(() => {
|
|
41
|
+
if (this.repo.jobs(-1).some((job) => job.plan.scanId === id)) return;
|
|
42
|
+
rmSync(join(this.root, id), {
|
|
43
|
+
recursive: true,
|
|
44
|
+
force: true
|
|
45
|
+
});
|
|
46
|
+
this.repo.deleteScan(id);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
storeScan(scan, workspaceRoot) {
|
|
40
50
|
const dir = join(this.root, scan.id);
|
|
41
51
|
mkdirSync(dir, { mode: 448 });
|
|
42
52
|
for (const item of scan.candidates) if (item.kind === "skill" && item.compatibility === "compatible") writeTextAtomicSync(join(dir, `${item.id}.json`), JSON.stringify(item.files));
|
|
@@ -44,7 +54,7 @@ var ImportService = class {
|
|
|
44
54
|
...scan,
|
|
45
55
|
candidates: scan.candidates.map((c) => ({
|
|
46
56
|
...c,
|
|
47
|
-
connected: this.options.isConnected?.(c) ?? false,
|
|
57
|
+
connected: this.options.isConnected?.(c, workspaceRoot) ?? false,
|
|
48
58
|
files: c.files.map((f) => ({
|
|
49
59
|
...f,
|
|
50
60
|
data: ""
|
|
@@ -60,84 +70,71 @@ var ImportService = class {
|
|
|
60
70
|
this.repo.save("plan", plan);
|
|
61
71
|
return plan;
|
|
62
72
|
}
|
|
63
|
-
|
|
73
|
+
describeSkills(scan, target) {
|
|
64
74
|
const inventory = targetInventory(target.root);
|
|
65
75
|
const names = new Set([...this.options.reservedNames?.(target) ?? [], ...inventory.flatMap((i) => [i.directory, i.name])].map((n) => n.toLowerCase()));
|
|
66
|
-
const choices = [];
|
|
67
|
-
const result = {
|
|
68
|
-
imported: 0,
|
|
69
|
-
skipped: 0,
|
|
70
|
-
issues: []
|
|
71
|
-
};
|
|
72
76
|
const previous = /* @__PURE__ */ new Map();
|
|
73
77
|
for (const job of this.repo.jobs(-1)) {
|
|
74
78
|
if (job.plan.target.root !== canonicalTarget(target.root)) continue;
|
|
75
79
|
const origin = this.getScan(job.plan.scanId);
|
|
76
80
|
for (const item of job.items.filter((i) => i.status === "active")) {
|
|
77
81
|
const candidate = origin.candidates.find((c) => c.id === item.action.candidateId);
|
|
78
|
-
if (candidate?.source === scan.source && !previous.has(candidate.location)) previous.set(candidate.location,
|
|
82
|
+
if (candidate?.source === scan.source && !previous.has(candidate.location)) previous.set(candidate.location, item.action.name);
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (candidate.compatibility !== "compatible") {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
else
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
let name = candidate.name;
|
|
109
|
-
if (names.has(name.toLowerCase())) name = `${candidate.source}-${candidate.name}`.slice(0, 63);
|
|
110
|
-
const files = JSON.parse(readFileSync(join(this.root, scan.id, `${candidate.id}.json`), "utf8"));
|
|
111
|
-
const hash = fileHash(this.renameSkill(files, name));
|
|
112
|
-
if (inventory.some((i) => i.name === name && i.hash === hash)) {
|
|
113
|
-
result.skipped++;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (names.has(name.toLowerCase())) {
|
|
117
|
-
result.issues.push({
|
|
118
|
-
name: candidate.name,
|
|
119
|
-
reason: "An existing skill was kept. Manage it in Skills to make changes."
|
|
120
|
-
});
|
|
121
|
-
continue;
|
|
85
|
+
return scan.candidates.filter((c) => c.kind === "skill").map((candidate) => {
|
|
86
|
+
let status = "ready";
|
|
87
|
+
let reason;
|
|
88
|
+
let targetName = candidate.name;
|
|
89
|
+
if (candidate.connected) status = "existing";
|
|
90
|
+
else if (candidate.compatibility !== "compatible") {
|
|
91
|
+
status = "blocked";
|
|
92
|
+
reason = candidate.findings.join("; ");
|
|
93
|
+
} else {
|
|
94
|
+
const prior = previous.get(candidate.location);
|
|
95
|
+
const priorTarget = prior && inventory.find((i) => i.directory === prior);
|
|
96
|
+
const files = this.readSnapshot(scan.id, candidate.id);
|
|
97
|
+
if (priorTarget) {
|
|
98
|
+
targetName = priorTarget.directory;
|
|
99
|
+
status = fileHash(this.renameSkill(files, targetName)) === priorTarget.hash ? "existing" : "blocked";
|
|
100
|
+
if (status === "blocked") reason = "Previously imported content has changed; existing edits will be kept.";
|
|
101
|
+
} else if (inventory.some((i) => i.name.toLowerCase() === candidate.name.toLowerCase() && i.hash === candidate.hash)) status = "existing";
|
|
102
|
+
else if (names.has(targetName.toLowerCase())) {
|
|
103
|
+
targetName = `${candidate.source}-${candidate.name}`.slice(0, 63);
|
|
104
|
+
if (inventory.some((i) => i.name === targetName && i.hash === fileHash(this.renameSkill(files, targetName)))) status = "existing";
|
|
105
|
+
else if (names.has(targetName.toLowerCase())) {
|
|
106
|
+
status = "blocked";
|
|
107
|
+
reason = "Both the original and product-prefixed names are in use.";
|
|
108
|
+
} else status = "conflict";
|
|
109
|
+
}
|
|
122
110
|
}
|
|
123
|
-
names.add(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (!choices.length) return result;
|
|
131
|
-
const plan = this.plan(scan.id, target, choices);
|
|
132
|
-
const staged = this.apply(plan.id, plan.id);
|
|
133
|
-
const job = await this.activate(staged.id, staged.items.filter((i) => i.status === "staged").map((i) => i.action.candidateId));
|
|
134
|
-
for (const item of job.items) if (item.status === "active") result.imported++;
|
|
135
|
-
else if (item.status === "skipped") result.skipped++;
|
|
136
|
-
else result.issues.push({
|
|
137
|
-
name: item.action.name,
|
|
138
|
-
reason: item.error ?? "Could not import this skill. Try again."
|
|
111
|
+
if (status === "ready" || status === "conflict") names.add(targetName.toLowerCase());
|
|
112
|
+
return {
|
|
113
|
+
candidate,
|
|
114
|
+
status,
|
|
115
|
+
reason,
|
|
116
|
+
targetName
|
|
117
|
+
};
|
|
139
118
|
});
|
|
140
|
-
|
|
119
|
+
}
|
|
120
|
+
readSnapshot(scanId, candidateId) {
|
|
121
|
+
const candidate = this.getScan(scanId).candidates.find((c) => c.id === candidateId);
|
|
122
|
+
if (!candidate || candidate.kind !== "skill" || candidate.compatibility !== "compatible") throw new ImportError("not_found", "Snapshot not found", 404);
|
|
123
|
+
const files = JSON.parse(readFileSync(join(this.root, scanId, `${candidateId}.json`), "utf8"));
|
|
124
|
+
if (fileHash(files) !== candidate.hash) throw new ImportError("source_changed", "Snapshot changed", 409);
|
|
125
|
+
return files;
|
|
126
|
+
}
|
|
127
|
+
jobByKey(key) {
|
|
128
|
+
return this.repo.byKey(key);
|
|
129
|
+
}
|
|
130
|
+
validateTarget(target) {
|
|
131
|
+
this.options.validateTarget?.(target);
|
|
132
|
+
}
|
|
133
|
+
reservedNames(target) {
|
|
134
|
+
return this.options.reservedNames?.(target) ?? [];
|
|
135
|
+
}
|
|
136
|
+
isWorkspaceTrusted(root) {
|
|
137
|
+
return this.options.isWorkspaceTrusted?.(root) ?? false;
|
|
141
138
|
}
|
|
142
139
|
getPlan(id) {
|
|
143
140
|
return this.repo.get("plan", id);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ImportService } from './importService.js';
|
|
2
|
+
import { type ImportSource, type ImportInventory, type StoredInventory, type ImportCandidate } from './types.js';
|
|
3
|
+
export declare function contextKey(candidate: ImportCandidate): string;
|
|
4
|
+
export declare function existingContext(candidate: ImportCandidate, projectId?: string): import("../knowledge-memory/domain.js").KnowledgeItem;
|
|
5
|
+
export declare function publicInventory(inventory: StoredInventory): ImportInventory;
|
|
6
|
+
export declare function getLiveInventory(owner: string, id: string): StoredInventory;
|
|
7
|
+
export declare function scanProduct(service: ImportService, source: ImportSource, owner?: string): Promise<ImportInventory>;
|
|
8
|
+
export declare function previewInventoryItem(service: ImportService, owner: string, inventoryId: string, itemId: string): {
|
|
9
|
+
text: string;
|
|
10
|
+
truncated: boolean;
|
|
11
|
+
};
|