@vellumai/assistant 0.5.6 → 0.5.7
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/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +1 -1
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +8 -14
- package/src/config/feature-flag-registry.json +48 -8
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +227 -51
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +12 -0
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/jobs-store.ts +30 -5
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +27 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.ts +24 -6
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +3 -50
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handlers for Vercel integration config endpoints.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/integrations/vercel/config — check if a Vercel API token is stored
|
|
5
|
+
* POST /v1/integrations/vercel/config — set or delete token (dispatched via action field)
|
|
6
|
+
* DELETE /v1/integrations/vercel/config — delete the stored Vercel API token
|
|
7
|
+
*
|
|
8
|
+
* The Swift client sends all mutations as POST with an `action` field
|
|
9
|
+
* ("set" or "delete") rather than using HTTP verbs directly.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
deleteVercelConfig,
|
|
14
|
+
getVercelConfig,
|
|
15
|
+
setVercelConfig,
|
|
16
|
+
} from "../../../daemon/handlers/config-vercel.js";
|
|
17
|
+
import type { RouteDefinition } from "../../http-router.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* GET /v1/integrations/vercel/config
|
|
21
|
+
*/
|
|
22
|
+
export async function handleGetVercelConfig(): Promise<Response> {
|
|
23
|
+
const result = await getVercelConfig();
|
|
24
|
+
return Response.json(result);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* POST /v1/integrations/vercel/config
|
|
29
|
+
*
|
|
30
|
+
* Body: { action: "set" | "delete"; apiToken?: string }
|
|
31
|
+
*
|
|
32
|
+
* The Swift client uses POST for both set and delete operations,
|
|
33
|
+
* distinguished by the `action` field.
|
|
34
|
+
*/
|
|
35
|
+
export async function handlePostVercelConfig(req: Request): Promise<Response> {
|
|
36
|
+
const body = (await req.json()) as {
|
|
37
|
+
action?: "get" | "set" | "delete";
|
|
38
|
+
apiToken?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
switch (body.action) {
|
|
42
|
+
case "delete": {
|
|
43
|
+
const result = await deleteVercelConfig();
|
|
44
|
+
return Response.json(result);
|
|
45
|
+
}
|
|
46
|
+
case "get": {
|
|
47
|
+
const result = await getVercelConfig();
|
|
48
|
+
return Response.json(result);
|
|
49
|
+
}
|
|
50
|
+
case "set":
|
|
51
|
+
default: {
|
|
52
|
+
const result = await setVercelConfig(body.apiToken);
|
|
53
|
+
const status = result.success ? 200 : 400;
|
|
54
|
+
return Response.json(result, { status });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* DELETE /v1/integrations/vercel/config
|
|
61
|
+
*/
|
|
62
|
+
export async function handleDeleteVercelConfig(): Promise<Response> {
|
|
63
|
+
const result = await deleteVercelConfig();
|
|
64
|
+
return Response.json(result);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Route definitions
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
export function vercelRouteDefinitions(): RouteDefinition[] {
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
endpoint: "integrations/vercel/config",
|
|
75
|
+
method: "GET",
|
|
76
|
+
handler: () => handleGetVercelConfig(),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
endpoint: "integrations/vercel/config",
|
|
80
|
+
method: "POST",
|
|
81
|
+
handler: async ({ req }) => handlePostVercelConfig(req),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
endpoint: "integrations/vercel/config",
|
|
85
|
+
method: "DELETE",
|
|
86
|
+
handler: async () => handleDeleteVercelConfig(),
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
getWorkspaceConfigPath,
|
|
38
38
|
getWorkspaceDir,
|
|
39
39
|
} from "../../util/platform.js";
|
|
40
|
+
import { APP_VERSION, COMMIT_SHA } from "../../version.js";
|
|
40
41
|
import { httpError } from "../http-errors.js";
|
|
41
42
|
import type { RouteDefinition } from "../http-router.js";
|
|
42
43
|
|
|
@@ -264,12 +265,16 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
|
|
|
264
265
|
? {
|
|
265
266
|
type: "conversation-export" as const,
|
|
266
267
|
conversationId,
|
|
268
|
+
assistantVersion: APP_VERSION,
|
|
269
|
+
commitSha: COMMIT_SHA,
|
|
267
270
|
...(startTime !== undefined ? { startTime } : {}),
|
|
268
271
|
...(endTime !== undefined ? { endTime } : {}),
|
|
269
272
|
exportedAt: new Date().toISOString(),
|
|
270
273
|
}
|
|
271
274
|
: {
|
|
272
275
|
type: "global-export" as const,
|
|
276
|
+
assistantVersion: APP_VERSION,
|
|
277
|
+
commitSha: COMMIT_SHA,
|
|
273
278
|
exportedAt: new Date().toISOString(),
|
|
274
279
|
};
|
|
275
280
|
writeFileSync(
|
|
@@ -45,6 +45,7 @@ const VALID_KINDS = [
|
|
|
45
45
|
"decision",
|
|
46
46
|
"constraint",
|
|
47
47
|
"event",
|
|
48
|
+
"capability",
|
|
48
49
|
] as const;
|
|
49
50
|
|
|
50
51
|
type MemoryItemKind = (typeof VALID_KINDS)[number];
|
|
@@ -154,7 +155,7 @@ async function searchItemsSemantic(
|
|
|
154
155
|
const sparse = generateSparseEmbedding(query);
|
|
155
156
|
const sparseVector = { indices: sparse.indices, values: sparse.values };
|
|
156
157
|
|
|
157
|
-
// Build Qdrant filter — items only, exclude
|
|
158
|
+
// Build Qdrant filter — items only, exclude sentinel
|
|
158
159
|
const mustConditions: Array<Record<string, unknown>> = [
|
|
159
160
|
{ key: "target_type", match: { value: "item" } },
|
|
160
161
|
];
|
|
@@ -168,7 +169,6 @@ async function searchItemsSemantic(
|
|
|
168
169
|
const filter = {
|
|
169
170
|
must: mustConditions,
|
|
170
171
|
must_not: [
|
|
171
|
-
{ key: "kind", match: { value: "capability" } },
|
|
172
172
|
{ key: "_meta", match: { value: true } },
|
|
173
173
|
],
|
|
174
174
|
};
|
|
@@ -185,6 +185,11 @@ async function searchItemsSemantic(
|
|
|
185
185
|
);
|
|
186
186
|
|
|
187
187
|
const ids = results.map((r) => r.payload.target_id);
|
|
188
|
+
|
|
189
|
+
// Use the vector search result count as the pagination total.
|
|
190
|
+
// A DB-wide COUNT would include items with no embedding yet (lagging) and
|
|
191
|
+
// items irrelevant to the search query, inflating the total and causing
|
|
192
|
+
// clients to paginate into empty pages.
|
|
188
193
|
return { ids, total: ids.length };
|
|
189
194
|
} catch (err) {
|
|
190
195
|
log.warn({ err }, "Semantic memory search failed, falling back to SQL");
|
|
@@ -249,11 +254,26 @@ export async function handleListMemoryItems(url: URL): Promise<Response> {
|
|
|
249
254
|
offsetParam + limitParam,
|
|
250
255
|
);
|
|
251
256
|
|
|
252
|
-
|
|
257
|
+
if (pageIds.length === 0) {
|
|
258
|
+
return Response.json({ items: [], total: semanticResult.total });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Re-apply the same DB-side filters used in the SQL path as defense-
|
|
262
|
+
// in-depth against stale Qdrant payloads leaking deleted/mismatched rows.
|
|
263
|
+
const hydrationConditions = [
|
|
264
|
+
inArray(memoryItems.id, pageIds),
|
|
265
|
+
];
|
|
266
|
+
if (statusParam && statusParam !== "all") {
|
|
267
|
+
hydrationConditions.push(eq(memoryItems.status, statusParam));
|
|
268
|
+
}
|
|
269
|
+
if (kindParam) {
|
|
270
|
+
hydrationConditions.push(eq(memoryItems.kind, kindParam));
|
|
271
|
+
}
|
|
272
|
+
|
|
253
273
|
const rows = db
|
|
254
274
|
.select()
|
|
255
275
|
.from(memoryItems)
|
|
256
|
-
.where(
|
|
276
|
+
.where(and(...hydrationConditions))
|
|
257
277
|
.all();
|
|
258
278
|
|
|
259
279
|
// Preserve Qdrant relevance ordering
|
|
@@ -279,8 +299,6 @@ export async function handleListMemoryItems(url: URL): Promise<Response> {
|
|
|
279
299
|
|
|
280
300
|
// ── SQL path (default or fallback) ──────────────────────────────────
|
|
281
301
|
const conditions = [];
|
|
282
|
-
// Hide system-managed capability memories (skill announcements) from the UI
|
|
283
|
-
conditions.push(ne(memoryItems.kind, "capability"));
|
|
284
302
|
if (statusParam && statusParam !== "all") {
|
|
285
303
|
conditions.push(eq(memoryItems.status, statusParam));
|
|
286
304
|
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration rollback endpoint — rolls back DB and/or workspace migrations
|
|
3
|
+
* to a specified target version/migration ID.
|
|
4
|
+
*
|
|
5
|
+
* Protected by a route policy restricting access to gateway service
|
|
6
|
+
* principals only (`svc_gateway` with `internal.write` scope), following
|
|
7
|
+
* the same pattern as other gateway-forwarded control-plane endpoints.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getDb } from "../../memory/db-connection.js";
|
|
11
|
+
import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
|
|
12
|
+
import { rollbackMemoryMigration } from "../../memory/migrations/validate-migration-state.js";
|
|
13
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
14
|
+
import { WORKSPACE_MIGRATIONS } from "../../workspace/migrations/registry.js";
|
|
15
|
+
import {
|
|
16
|
+
getLastWorkspaceMigrationId,
|
|
17
|
+
loadCheckpoints,
|
|
18
|
+
rollbackWorkspaceMigrations,
|
|
19
|
+
} from "../../workspace/migrations/runner.js";
|
|
20
|
+
import { httpError } from "../http-errors.js";
|
|
21
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
22
|
+
|
|
23
|
+
export function migrationRollbackRouteDefinitions(): RouteDefinition[] {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
endpoint: "admin/rollback-migrations",
|
|
27
|
+
method: "POST",
|
|
28
|
+
handler: async ({ req }) => {
|
|
29
|
+
let body: unknown;
|
|
30
|
+
try {
|
|
31
|
+
body = await req.json();
|
|
32
|
+
} catch {
|
|
33
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!body || typeof body !== "object") {
|
|
37
|
+
return httpError(
|
|
38
|
+
"BAD_REQUEST",
|
|
39
|
+
"Request body must be a JSON object",
|
|
40
|
+
400,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
targetDbVersion,
|
|
46
|
+
targetWorkspaceMigrationId,
|
|
47
|
+
rollbackToRegistryCeiling,
|
|
48
|
+
} = body as {
|
|
49
|
+
targetDbVersion?: unknown;
|
|
50
|
+
targetWorkspaceMigrationId?: unknown;
|
|
51
|
+
rollbackToRegistryCeiling?: unknown;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// When rollbackToRegistryCeiling is true, auto-determine targets
|
|
55
|
+
// from this daemon's own migration registry ceilings.
|
|
56
|
+
let effectiveDbVersion = targetDbVersion as number | undefined;
|
|
57
|
+
let effectiveWorkspaceMigrationId = targetWorkspaceMigrationId as
|
|
58
|
+
| string
|
|
59
|
+
| undefined;
|
|
60
|
+
|
|
61
|
+
if (rollbackToRegistryCeiling === true) {
|
|
62
|
+
if (effectiveDbVersion === undefined)
|
|
63
|
+
effectiveDbVersion = getMaxMigrationVersion();
|
|
64
|
+
if (effectiveWorkspaceMigrationId === undefined)
|
|
65
|
+
effectiveWorkspaceMigrationId =
|
|
66
|
+
getLastWorkspaceMigrationId(WORKSPACE_MIGRATIONS) ?? undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// At least one rollback target must be specified.
|
|
70
|
+
if (
|
|
71
|
+
effectiveDbVersion === undefined &&
|
|
72
|
+
effectiveWorkspaceMigrationId === undefined
|
|
73
|
+
) {
|
|
74
|
+
return httpError(
|
|
75
|
+
"BAD_REQUEST",
|
|
76
|
+
"At least one of targetDbVersion or targetWorkspaceMigrationId must be provided",
|
|
77
|
+
400,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Validate effectiveDbVersion when provided.
|
|
82
|
+
if (effectiveDbVersion !== undefined) {
|
|
83
|
+
if (
|
|
84
|
+
typeof effectiveDbVersion !== "number" ||
|
|
85
|
+
!Number.isInteger(effectiveDbVersion) ||
|
|
86
|
+
effectiveDbVersion < 0
|
|
87
|
+
) {
|
|
88
|
+
return httpError(
|
|
89
|
+
"BAD_REQUEST",
|
|
90
|
+
"targetDbVersion must be a non-negative integer",
|
|
91
|
+
400,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Validate effectiveWorkspaceMigrationId when provided.
|
|
97
|
+
if (effectiveWorkspaceMigrationId !== undefined) {
|
|
98
|
+
if (
|
|
99
|
+
typeof effectiveWorkspaceMigrationId !== "string" ||
|
|
100
|
+
effectiveWorkspaceMigrationId.length === 0
|
|
101
|
+
) {
|
|
102
|
+
return httpError(
|
|
103
|
+
"BAD_REQUEST",
|
|
104
|
+
"targetWorkspaceMigrationId must be a non-empty string",
|
|
105
|
+
400,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Preflight: validate that the workspace migration ID exists in the
|
|
111
|
+
// registry BEFORE executing any mutations. This prevents the DB
|
|
112
|
+
// rollback from committing when the workspace target is invalid.
|
|
113
|
+
let resolvedTargetIndex = -1;
|
|
114
|
+
if (effectiveWorkspaceMigrationId !== undefined) {
|
|
115
|
+
const targetId = effectiveWorkspaceMigrationId as string;
|
|
116
|
+
resolvedTargetIndex = WORKSPACE_MIGRATIONS.findIndex(
|
|
117
|
+
(m) => m.id === targetId,
|
|
118
|
+
);
|
|
119
|
+
if (resolvedTargetIndex === -1) {
|
|
120
|
+
return httpError(
|
|
121
|
+
"BAD_REQUEST",
|
|
122
|
+
`Target workspace migration "${targetId}" not found in the registry`,
|
|
123
|
+
400,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const rolledBack: { db: string[]; workspace: string[] } = {
|
|
129
|
+
db: [],
|
|
130
|
+
workspace: [],
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Roll back DB migrations if requested.
|
|
134
|
+
if (effectiveDbVersion !== undefined) {
|
|
135
|
+
try {
|
|
136
|
+
rolledBack.db = rollbackMemoryMigration(
|
|
137
|
+
getDb(),
|
|
138
|
+
effectiveDbVersion,
|
|
139
|
+
);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
const detail = err instanceof Error ? err.message : "Unknown error";
|
|
142
|
+
return httpError(
|
|
143
|
+
"INTERNAL_ERROR",
|
|
144
|
+
`DB migration rollback failed: ${detail}`,
|
|
145
|
+
500,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Roll back workspace migrations if requested.
|
|
151
|
+
if (effectiveWorkspaceMigrationId !== undefined) {
|
|
152
|
+
const workspaceDir = getWorkspaceDir();
|
|
153
|
+
|
|
154
|
+
// Compute which migrations are candidates for rollback before
|
|
155
|
+
// executing, since rollbackWorkspaceMigrations returns void.
|
|
156
|
+
const targetId = effectiveWorkspaceMigrationId;
|
|
157
|
+
|
|
158
|
+
const checkpointsBefore = loadCheckpoints(workspaceDir);
|
|
159
|
+
const candidateIds = WORKSPACE_MIGRATIONS.slice(
|
|
160
|
+
resolvedTargetIndex + 1,
|
|
161
|
+
)
|
|
162
|
+
.filter((m) => {
|
|
163
|
+
const entry = checkpointsBefore.applied[m.id];
|
|
164
|
+
return (
|
|
165
|
+
entry &&
|
|
166
|
+
entry.status !== "started" &&
|
|
167
|
+
entry.status !== "rolling_back"
|
|
168
|
+
);
|
|
169
|
+
})
|
|
170
|
+
.map((m) => m.id);
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
await rollbackWorkspaceMigrations(
|
|
174
|
+
workspaceDir,
|
|
175
|
+
WORKSPACE_MIGRATIONS,
|
|
176
|
+
targetId,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
rolledBack.workspace = candidateIds;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
// Re-read checkpoints to determine which migrations were actually
|
|
182
|
+
// rolled back before the error occurred. A candidate whose entry
|
|
183
|
+
// is no longer present in the checkpoint file was successfully
|
|
184
|
+
// reverted.
|
|
185
|
+
const checkpointsAfter = loadCheckpoints(workspaceDir);
|
|
186
|
+
const actuallyRolledBack = candidateIds.filter(
|
|
187
|
+
(id) => !checkpointsAfter.applied[id],
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const detail = err instanceof Error ? err.message : "Unknown error";
|
|
191
|
+
return httpError(
|
|
192
|
+
"INTERNAL_ERROR",
|
|
193
|
+
`Workspace migration rollback failed: ${detail}`,
|
|
194
|
+
500,
|
|
195
|
+
{
|
|
196
|
+
partialRolledBack: {
|
|
197
|
+
db: rolledBack.db,
|
|
198
|
+
workspace: actuallyRolledBack,
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return Response.json({ ok: true, rolledBack });
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
}
|
|
@@ -15,7 +15,8 @@ import { join } from "node:path";
|
|
|
15
15
|
import { Database } from "bun:sqlite";
|
|
16
16
|
|
|
17
17
|
import { invalidateConfigCache } from "../../config/loader.js";
|
|
18
|
-
import { resetDb } from "../../memory/db-connection.js";
|
|
18
|
+
import { getDb, resetDb } from "../../memory/db-connection.js";
|
|
19
|
+
import { validateMigrationState } from "../../memory/migrations/validate-migration-state.js";
|
|
19
20
|
import { clearCache as clearTrustCache } from "../../permissions/trust-store.js";
|
|
20
21
|
import { getLogger } from "../../util/logger.js";
|
|
21
22
|
import {
|
|
@@ -438,6 +439,21 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
|
|
|
438
439
|
invalidateConfigCache();
|
|
439
440
|
clearTrustCache();
|
|
440
441
|
|
|
442
|
+
// Check whether the imported database contains migration checkpoints from
|
|
443
|
+
// a newer version. This is non-blocking — the import has already
|
|
444
|
+
// succeeded — but we surface a warning so the caller knows some data may
|
|
445
|
+
// not be fully compatible with this daemon's schema.
|
|
446
|
+
try {
|
|
447
|
+
const migrationValidation = validateMigrationState(getDb());
|
|
448
|
+
if (migrationValidation.unknownCheckpoints.length > 0) {
|
|
449
|
+
result.report.warnings.push(
|
|
450
|
+
`Imported data contains ${migrationValidation.unknownCheckpoints.length} migration(s) from a newer version. Some data may not be fully compatible.`,
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
} catch {
|
|
454
|
+
// Don't fail the import if validation itself errors
|
|
455
|
+
}
|
|
456
|
+
|
|
441
457
|
return Response.json(result.report);
|
|
442
458
|
} catch (err) {
|
|
443
459
|
log.error({ err }, "Unexpected error during import commit");
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handlers for notification delivery acknowledgments.
|
|
3
|
+
*
|
|
4
|
+
* Provides a REST endpoint for clients to report the outcome of
|
|
5
|
+
* local notification delivery (UNUserNotificationCenter.add).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { eq } from "drizzle-orm";
|
|
9
|
+
|
|
10
|
+
import { getDb } from "../../memory/db.js";
|
|
11
|
+
import { notificationDeliveries } from "../../memory/schema.js";
|
|
12
|
+
import { httpError } from "../http-errors.js";
|
|
13
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
14
|
+
|
|
15
|
+
export function notificationRouteDefinitions(): RouteDefinition[] {
|
|
16
|
+
return [
|
|
17
|
+
// POST /v1/notification-intent-result — client ack for notification delivery
|
|
18
|
+
{
|
|
19
|
+
endpoint: "notification-intent-result",
|
|
20
|
+
method: "POST",
|
|
21
|
+
policyKey: "notification-intent-result",
|
|
22
|
+
handler: async ({ req }) => {
|
|
23
|
+
const body = (await req.json()) as {
|
|
24
|
+
deliveryId?: string;
|
|
25
|
+
success?: boolean;
|
|
26
|
+
errorMessage?: string;
|
|
27
|
+
errorCode?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (!body.deliveryId || typeof body.deliveryId !== "string") {
|
|
31
|
+
return httpError("BAD_REQUEST", "deliveryId is required", 400);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const db = getDb();
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
|
|
37
|
+
const updates: Record<string, unknown> = {
|
|
38
|
+
clientDeliveryStatus: body.success ? "delivered" : "client_failed",
|
|
39
|
+
clientDeliveryAt: now,
|
|
40
|
+
updatedAt: now,
|
|
41
|
+
};
|
|
42
|
+
if (body.errorMessage) {
|
|
43
|
+
updates.clientDeliveryError = body.errorMessage;
|
|
44
|
+
}
|
|
45
|
+
if (body.errorCode) {
|
|
46
|
+
updates.errorCode = body.errorCode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
db.update(notificationDeliveries)
|
|
50
|
+
.set(updates)
|
|
51
|
+
.where(eq(notificationDeliveries.id, body.deliveryId))
|
|
52
|
+
.run();
|
|
53
|
+
|
|
54
|
+
return Response.json({ ok: true });
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
@@ -99,6 +99,59 @@ function handleCancelSchedule(id: string): Response {
|
|
|
99
99
|
return handleListSchedules();
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
const VALID_MODES = ["notify", "execute"] as const;
|
|
103
|
+
const VALID_ROUTING_INTENTS = [
|
|
104
|
+
"single_channel",
|
|
105
|
+
"multi_channel",
|
|
106
|
+
"all_channels",
|
|
107
|
+
] as const;
|
|
108
|
+
|
|
109
|
+
function handleUpdateSchedule(
|
|
110
|
+
id: string,
|
|
111
|
+
body: Record<string, unknown>,
|
|
112
|
+
): Response {
|
|
113
|
+
const updates: Record<string, unknown> = {};
|
|
114
|
+
|
|
115
|
+
if ("mode" in body && !VALID_MODES.includes(body.mode as (typeof VALID_MODES)[number])) {
|
|
116
|
+
return httpError("BAD_REQUEST", `Invalid mode: must be one of ${VALID_MODES.join(", ")}`, 400);
|
|
117
|
+
}
|
|
118
|
+
if ("routingIntent" in body && !VALID_ROUTING_INTENTS.includes(body.routingIntent as (typeof VALID_ROUTING_INTENTS)[number])) {
|
|
119
|
+
return httpError("BAD_REQUEST", `Invalid routingIntent: must be one of ${VALID_ROUTING_INTENTS.join(", ")}`, 400);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const key of [
|
|
123
|
+
"name",
|
|
124
|
+
"expression",
|
|
125
|
+
"timezone",
|
|
126
|
+
"message",
|
|
127
|
+
"mode",
|
|
128
|
+
"routingIntent",
|
|
129
|
+
"quiet",
|
|
130
|
+
] as const) {
|
|
131
|
+
if (key in body) {
|
|
132
|
+
updates[key] = body[key];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const updated = updateSchedule(id, updates);
|
|
138
|
+
if (!updated) {
|
|
139
|
+
return httpError("NOT_FOUND", "Schedule not found", 404);
|
|
140
|
+
}
|
|
141
|
+
log.info({ id, updates }, "Schedule updated via HTTP");
|
|
142
|
+
} catch (err) {
|
|
143
|
+
if (
|
|
144
|
+
err instanceof Error &&
|
|
145
|
+
(err.message.includes("Invalid") || err.message.includes("invalid"))
|
|
146
|
+
) {
|
|
147
|
+
return httpError("BAD_REQUEST", err.message, 400);
|
|
148
|
+
}
|
|
149
|
+
log.error({ err }, "Failed to update schedule");
|
|
150
|
+
return httpError("INTERNAL_ERROR", "Failed to update schedule", 500);
|
|
151
|
+
}
|
|
152
|
+
return handleListSchedules();
|
|
153
|
+
}
|
|
154
|
+
|
|
102
155
|
async function handleRunScheduleNow(
|
|
103
156
|
id: string,
|
|
104
157
|
sendMessageDeps?: SendMessageDeps,
|
|
@@ -243,6 +296,18 @@ export function scheduleRouteDefinitions(deps: {
|
|
|
243
296
|
policyKey: "schedules",
|
|
244
297
|
handler: ({ params }) => handleDeleteSchedule(params.id),
|
|
245
298
|
},
|
|
299
|
+
{
|
|
300
|
+
endpoint: "schedules/:id",
|
|
301
|
+
method: "PATCH",
|
|
302
|
+
policyKey: "schedules",
|
|
303
|
+
handler: async ({ req, params }) => {
|
|
304
|
+
const body: unknown = await req.json();
|
|
305
|
+
if (typeof body !== "object" || !body || Array.isArray(body)) {
|
|
306
|
+
return httpError("BAD_REQUEST", "Request body must be a JSON object", 400);
|
|
307
|
+
}
|
|
308
|
+
return handleUpdateSchedule(params.id, body as Record<string, unknown>);
|
|
309
|
+
},
|
|
310
|
+
},
|
|
246
311
|
{
|
|
247
312
|
endpoint: "schedules/:id/run",
|
|
248
313
|
method: "POST",
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getPlatformBaseUrl,
|
|
15
|
+
setIngressPublicBaseUrl,
|
|
16
|
+
} from "../../config/env.js";
|
|
14
17
|
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
15
18
|
import { loadSkillCatalog } from "../../config/skills.js";
|
|
16
19
|
import {
|
|
@@ -728,6 +731,43 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
|
|
|
728
731
|
handler: () => handleEnvVars(),
|
|
729
732
|
},
|
|
730
733
|
|
|
734
|
+
// Platform config (GET / PUT)
|
|
735
|
+
{
|
|
736
|
+
endpoint: "config/platform",
|
|
737
|
+
method: "GET",
|
|
738
|
+
policyKey: "config/platform:GET",
|
|
739
|
+
handler: () => {
|
|
740
|
+
const raw = loadRawConfig();
|
|
741
|
+
const platform = (raw?.platform ?? {}) as Record<string, unknown>;
|
|
742
|
+
const baseUrl =
|
|
743
|
+
(platform.baseUrl as string | undefined) || getPlatformBaseUrl();
|
|
744
|
+
return Response.json({ baseUrl, success: true });
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
endpoint: "config/platform",
|
|
749
|
+
method: "PUT",
|
|
750
|
+
policyKey: "config/platform",
|
|
751
|
+
handler: async ({ req }) => {
|
|
752
|
+
try {
|
|
753
|
+
const body = (await req.json()) as { baseUrl?: string };
|
|
754
|
+
const value = (body.baseUrl ?? "").trim().replace(/\/+$/, "");
|
|
755
|
+
const raw = loadRawConfig();
|
|
756
|
+
const platform = (raw?.platform ?? {}) as Record<string, unknown>;
|
|
757
|
+
platform.baseUrl = value || undefined;
|
|
758
|
+
saveRawConfig({ ...raw, platform });
|
|
759
|
+
return Response.json({ baseUrl: value, success: true });
|
|
760
|
+
} catch (err) {
|
|
761
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
762
|
+
log.error({ err }, "Failed to update platform config via HTTP");
|
|
763
|
+
return Response.json(
|
|
764
|
+
{ baseUrl: "", success: false, error: message },
|
|
765
|
+
{ status: 500 },
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
|
|
731
771
|
// Ingress config (GET / PUT)
|
|
732
772
|
{
|
|
733
773
|
endpoint: "integrations/ingress/config",
|