@vellumai/assistant 0.7.3 → 0.8.1
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/AGENTS.md +11 -0
- package/ARCHITECTURE.md +29 -28
- package/Dockerfile +6 -4
- package/README.md +2 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/docker-entrypoint.sh +16 -0
- package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
- package/eslint-rules/cli-no-daemon-internals.js +283 -0
- package/eslint.config.mjs +12 -0
- package/knip.json +3 -1
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
- package/openapi.yaml +4126 -959
- package/package.json +5 -1
- package/scripts/generate-openapi.ts +52 -4
- package/scripts/sync-llm-catalog.ts +165 -0
- package/scripts/sync-web-search-catalog.ts +107 -0
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/anthropic-provider.test.ts +92 -2
- package/src/__tests__/app-control-flow.test.ts +7 -0
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
- package/src/__tests__/avatar-identity-sync.test.ts +87 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/call-site-routing-provider.test.ts +172 -45
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
- package/src/__tests__/channel-policy.test.ts +12 -0
- package/src/__tests__/checker.test.ts +89 -0
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
- package/src/__tests__/config-loader-backfill.test.ts +526 -102
- package/src/__tests__/config-loader-corrupt.test.ts +68 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
- package/src/__tests__/config-schema-cmd.test.ts +63 -29
- package/src/__tests__/config-schema.test.ts +14 -3
- package/src/__tests__/config-set-platform-guard.test.ts +75 -152
- package/src/__tests__/config-set-route.test.ts +198 -0
- package/src/__tests__/config-watcher.test.ts +6 -0
- package/src/__tests__/contacts-tools.test.ts +51 -199
- package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
- package/src/__tests__/context-search-agent-runner.test.ts +22 -138
- package/src/__tests__/context-search-conversations-source.test.ts +42 -16
- package/src/__tests__/context-search-fanout.test.ts +20 -157
- package/src/__tests__/context-search-memory-source.test.ts +3 -26
- package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
- package/src/__tests__/context-search-types.test.ts +7 -2
- package/src/__tests__/context-window-manager.test.ts +389 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop.test.ts +3 -3
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
- package/src/__tests__/conversation-error.test.ts +38 -0
- package/src/__tests__/conversation-fork-crud.test.ts +241 -1
- package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
- package/src/__tests__/conversation-lifecycle.test.ts +124 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
- package/src/__tests__/conversation-process-callsite.test.ts +22 -7
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
- package/src/__tests__/conversation-slash-commands.test.ts +194 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
- package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
- package/src/__tests__/credential-security-invariants.test.ts +5 -6
- package/src/__tests__/daemon-credential-client.test.ts +56 -1
- package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
- package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
- package/src/__tests__/db-proxy-transaction.test.ts +206 -0
- package/src/__tests__/external-plugin-loader.test.ts +458 -0
- package/src/__tests__/filing-service.test.ts +25 -22
- package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
- package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
- package/src/__tests__/heartbeat-service.test.ts +50 -233
- package/src/__tests__/history-repair.test.ts +89 -0
- package/src/__tests__/host-app-control-proxy.test.ts +109 -1
- package/src/__tests__/host-app-control-routes.test.ts +247 -1
- package/src/__tests__/host-browser-proxy.test.ts +416 -20
- package/src/__tests__/host-browser-routes.test.ts +325 -33
- package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
- package/src/__tests__/inference-profile-reaper.test.ts +154 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
- package/src/__tests__/injector-chain.test.ts +24 -16
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
- package/src/__tests__/install-skill-routing.test.ts +2 -2
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
- package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
- package/src/__tests__/llm-catalog-parity.test.ts +146 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
- package/src/__tests__/llm-resolver.test.ts +46 -0
- package/src/__tests__/managed-profile-guard.test.ts +131 -2
- package/src/__tests__/mcp-auth-routes.test.ts +1 -0
- package/src/__tests__/mcp-cli.test.ts +182 -220
- package/src/__tests__/mcp-health-check.test.ts +56 -27
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
- package/src/__tests__/message-complete-display-id.test.ts +175 -0
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/notification-platform-adapter.test.ts +229 -0
- package/src/__tests__/oauth-cli.test.ts +38 -1888
- package/src/__tests__/oauth-commands-routes.test.ts +711 -0
- package/src/__tests__/oauth-connect-routes.test.ts +174 -11
- package/src/__tests__/oauth-providers-routes.test.ts +14 -10
- package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
- package/src/__tests__/openai-responses-provider.test.ts +17 -0
- package/src/__tests__/plugin-bootstrap.test.ts +31 -2
- package/src/__tests__/plugin-route-contribution.test.ts +31 -3
- package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
- package/src/__tests__/plugin-types.test.ts +13 -11
- package/src/__tests__/process-message-background-slack.test.ts +46 -0
- package/src/__tests__/profile-entry-status.test.ts +43 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
- package/src/__tests__/provider-registry-ollama.test.ts +12 -4
- package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
- package/src/__tests__/relay-server.test.ts +164 -2
- package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
- package/src/__tests__/schedule-retry.test.ts +56 -4
- package/src/__tests__/schedule-routes.test.ts +104 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
- package/src/__tests__/scheduler-recurrence.test.ts +87 -34
- package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
- package/src/__tests__/scheduler-wake.test.ts +0 -63
- package/src/__tests__/secret-allowlist.test.ts +1 -0
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/shell-credential-ref.test.ts +95 -3
- package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skill-load-tool.test.ts +42 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +63 -0
- package/src/__tests__/task-scheduler.test.ts +88 -23
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/update-bulletin-job.test.ts +96 -193
- package/src/__tests__/usage-cli.test.ts +11 -73
- package/src/__tests__/user-plugin-loader.test.ts +145 -0
- package/src/__tests__/vercel-config.test.ts +168 -0
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
- package/src/__tests__/web-search.test.ts +303 -2
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
- package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
- package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
- package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
- package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
- package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
- package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
- package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
- package/src/acp/__tests__/helpers/which-stub.ts +4 -2
- package/src/acp/resolve-agent.test.ts +25 -0
- package/src/acp/resolve-agent.ts +13 -2
- package/src/acp/session-manager.ts +14 -0
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-decision-primitive.ts +0 -13
- package/src/approvals/guardian-request-resolvers.ts +19 -102
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/relay-server.ts +42 -1
- package/src/calls/relay-setup-router.ts +36 -0
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-session-bridge.ts +24 -5
- package/src/channels/config.ts +14 -1
- package/src/channels/types.ts +1 -0
- package/src/cli/AGENTS.md +164 -4
- package/src/cli/__tests__/notifications.test.ts +54 -0
- package/src/cli/commands/__tests__/avatar.test.ts +540 -0
- package/src/cli/commands/__tests__/backup.test.ts +236 -776
- package/src/cli/commands/__tests__/cache.test.ts +1 -1
- package/src/cli/commands/__tests__/changelog.test.ts +593 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
- package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
- package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
- package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
- package/src/cli/commands/__tests__/email-core.test.ts +579 -0
- package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
- package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
- package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
- package/src/cli/commands/__tests__/skills.test.ts +563 -0
- package/src/cli/commands/__tests__/status.test.ts +249 -0
- package/src/cli/commands/__tests__/stt.test.ts +320 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
- package/src/cli/commands/__tests__/tts.test.ts +321 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
- package/src/cli/commands/attachment.ts +8 -3
- package/src/cli/commands/audit.ts +95 -64
- package/src/cli/commands/auth.ts +61 -58
- package/src/cli/commands/avatar.ts +276 -390
- package/src/cli/commands/backup.ts +409 -505
- package/src/cli/commands/bash.ts +9 -5
- package/src/cli/commands/browser.ts +28 -9
- package/src/cli/commands/cache.ts +9 -4
- package/src/cli/commands/changelog.ts +414 -0
- package/src/cli/commands/channel-verification-sessions.ts +238 -317
- package/src/cli/commands/clients.ts +8 -3
- package/src/cli/commands/completions.ts +9 -9
- package/src/cli/commands/config.ts +102 -72
- package/src/cli/commands/contacts.ts +575 -696
- package/src/cli/commands/conversations-defer.ts +17 -69
- package/src/cli/commands/conversations-import.ts +90 -253
- package/src/cli/commands/conversations.ts +346 -436
- package/src/cli/commands/credential-execution.ts +9 -6
- package/src/cli/commands/credentials.ts +456 -736
- package/src/cli/commands/domain.ts +128 -206
- package/src/cli/commands/email.ts +606 -794
- package/src/cli/commands/gateway.ts +8 -1
- package/src/cli/commands/image-generation.ts +157 -205
- package/src/cli/commands/inference-providers.ts +352 -0
- package/src/cli/commands/inference-session.ts +415 -0
- package/src/cli/commands/inference.ts +87 -65
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.ts +103 -287
- package/src/cli/commands/memory-v2.ts +163 -517
- package/src/cli/commands/notifications.ts +33 -7
- package/src/cli/commands/oauth/apps.ts +292 -261
- package/src/cli/commands/oauth/connect.ts +182 -345
- package/src/cli/commands/oauth/disconnect.ts +16 -215
- package/src/cli/commands/oauth/index.ts +49 -45
- package/src/cli/commands/oauth/mode.ts +43 -199
- package/src/cli/commands/oauth/ping.ts +17 -125
- package/src/cli/commands/oauth/providers.ts +732 -921
- package/src/cli/commands/oauth/request.ts +60 -350
- package/src/cli/commands/oauth/shared.ts +11 -121
- package/src/cli/commands/oauth/status.ts +31 -121
- package/src/cli/commands/oauth/token.ts +13 -55
- package/src/cli/commands/pending.ts +19 -10
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
- package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
- package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
- package/src/cli/commands/platform/connect.ts +16 -80
- package/src/cli/commands/platform/disconnect.ts +14 -112
- package/src/cli/commands/platform/index.ts +177 -246
- package/src/cli/commands/routes.ts +153 -336
- package/src/cli/commands/sequence.ts +316 -360
- package/src/cli/commands/skills.ts +449 -671
- package/src/cli/commands/status.ts +58 -37
- package/src/cli/commands/stt.ts +94 -262
- package/src/cli/commands/task.ts +14 -40
- package/src/cli/commands/trust.ts +8 -3
- package/src/cli/commands/tts.ts +162 -167
- package/src/cli/commands/ui.ts +35 -42
- package/src/cli/commands/usage.ts +188 -126
- package/src/cli/commands/watchers.ts +8 -3
- package/src/cli/commands/webhooks.ts +99 -193
- package/src/cli/lib/__tests__/register-command.test.ts +85 -0
- package/src/cli/lib/daemon-credential-client.ts +4 -5
- package/src/cli/lib/nested-value.ts +44 -0
- package/src/cli/lib/open-browser.ts +36 -0
- package/src/cli/lib/register-command.ts +19 -0
- package/src/cli/lib/time-ago.ts +34 -0
- package/src/cli/program.ts +2 -4
- package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
- package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
- package/src/cli/utils/conversation-id.ts +30 -0
- package/src/cli/utils/parse-duration.ts +41 -0
- package/src/config/acp-defaults.test.ts +5 -1
- package/src/config/acp-defaults.ts +11 -4
- package/src/config/bundled-skills/acp/TOOLS.json +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
- package/src/config/bundled-skills/contacts/SKILL.md +12 -45
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
- package/src/config/bundled-tool-registry.ts +0 -2
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-resolver.ts +16 -1
- package/src/config/loader.ts +148 -33
- package/src/config/raw-config-utils.ts +2 -30
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
- package/src/config/schemas/call-site-catalog.ts +29 -7
- package/src/config/schemas/llm-request-logs.ts +57 -0
- package/src/config/schemas/llm.ts +52 -2
- package/src/config/schemas/memory-retrospective.ts +48 -0
- package/src/config/schemas/memory-v2.ts +33 -2
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +15 -12
- package/src/config/seed-inference-profiles.ts +195 -134
- package/src/contacts/contact-store.ts +0 -61
- package/src/context/window-manager.ts +191 -5
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
- package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
- package/src/daemon/approval-generators.ts +23 -29
- package/src/daemon/config-watcher.ts +2 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
- package/src/daemon/conversation-agent-loop.ts +140 -107
- package/src/daemon/conversation-error.ts +21 -0
- package/src/daemon/conversation-lifecycle.ts +68 -13
- package/src/daemon/conversation-process.ts +36 -19
- package/src/daemon/conversation-runtime-assembly.ts +14 -5
- package/src/daemon/conversation-slash.ts +175 -23
- package/src/daemon/conversation-store.ts +17 -10
- package/src/daemon/conversation-surfaces.ts +92 -26
- package/src/daemon/conversation-tool-setup.ts +33 -19
- package/src/daemon/conversation.ts +49 -10
- package/src/daemon/external-plugins-bootstrap.ts +18 -8
- package/src/daemon/guardian-action-generators.ts +7 -22
- package/src/daemon/handlers/config-model.ts +8 -126
- package/src/daemon/handlers/config-slack-channel.ts +10 -7
- package/src/daemon/handlers/config-vercel.ts +3 -1
- package/src/daemon/handlers/shared.ts +26 -0
- package/src/daemon/handlers/skills.ts +84 -5
- package/src/daemon/history-repair.ts +33 -6
- package/src/daemon/host-app-control-proxy.ts +44 -19
- package/src/daemon/host-bash-proxy.ts +85 -158
- package/src/daemon/host-browser-proxy.ts +97 -36
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-base.ts +13 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -1
- package/src/daemon/host-transfer-proxy.ts +2 -2
- package/src/daemon/identity-helpers.ts +19 -0
- package/src/daemon/lifecycle.ts +128 -114
- package/src/daemon/meet-host-supervisor.ts +15 -15
- package/src/daemon/memory-v2-startup.ts +62 -14
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/bookmarks.ts +18 -0
- package/src/daemon/message-types/conversations.ts +12 -9
- package/src/daemon/message-types/messages.ts +28 -2
- package/src/daemon/message-types/sync.ts +60 -0
- package/src/daemon/pkb-reminder-builder.test.ts +54 -13
- package/src/daemon/pkb-reminder-builder.ts +21 -7
- package/src/daemon/process-message.ts +56 -23
- package/src/daemon/server.ts +23 -18
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/daemon/tool-side-effects.ts +6 -4
- package/src/daemon/wake-target-adapter.ts +11 -0
- package/src/documents/document-store.ts +35 -1
- package/src/export/transcript-formatter.ts +61 -2
- package/src/filing/filing-service.ts +42 -56
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +149 -128
- package/src/home/__tests__/feed-types.test.ts +63 -131
- package/src/home/__tests__/feed-writer.test.ts +77 -278
- package/src/home/__tests__/post-connect-feed.test.ts +9 -12
- package/src/home/feed-types.ts +19 -73
- package/src/home/feed-writer.ts +25 -156
- package/src/home/post-connect-feed.ts +1 -3
- package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
- package/src/ipc/__tests__/email-ipc.test.ts +506 -0
- package/src/ipc/__tests__/exit-helper.test.ts +104 -0
- package/src/ipc/__tests__/streaming-client.test.ts +237 -0
- package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
- package/src/ipc/assistant-server.ts +148 -42
- package/src/ipc/cli-client.ts +370 -50
- package/src/ipc/routes/db-proxy-transaction.ts +151 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
- package/src/ipc/skill-routes/events.ts +30 -3
- package/src/ipc/skill-server.ts +99 -42
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
- package/src/live-voice/live-voice-session-manager.ts +11 -4
- package/src/live-voice/live-voice-session.ts +14 -6
- package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
- package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
- package/src/memory/__tests__/conversation-types.test.ts +36 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
- package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
- package/src/memory/bookmark-crud.ts +179 -0
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
- package/src/memory/context-search/agent-protocol.ts +5 -1
- package/src/memory/context-search/agent-runner.ts +60 -85
- package/src/memory/context-search/limits.ts +1 -4
- package/src/memory/context-search/search.ts +23 -113
- package/src/memory/context-search/sources/conversations.ts +18 -6
- package/src/memory/context-search/sources/memory-v2.ts +40 -31
- package/src/memory/context-search/sources/memory.ts +9 -2
- package/src/memory/context-search/sources/workspace.ts +13 -10
- package/src/memory/context-search/types.ts +1 -1
- package/src/memory/conversation-bootstrap.ts +11 -0
- package/src/memory/conversation-crud.ts +312 -10
- package/src/memory/conversation-queries.ts +9 -5
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/conversation-types.ts +16 -0
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.ts +2 -1
- package/src/memory/embedding-runtime-manager.ts +1 -2
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
- package/src/memory/graph/conversation-graph-memory.ts +108 -14
- package/src/memory/graph/extraction.ts +4 -0
- package/src/memory/graph/graph-memory-state-store.ts +16 -3
- package/src/memory/graph/graph-search.test.ts +6 -5
- package/src/memory/graph/graph-search.ts +3 -4
- package/src/memory/graph/retriever.test.ts +12 -7
- package/src/memory/graph/retriever.ts +4 -5
- package/src/memory/graph/tool-handlers.ts +20 -11
- package/src/memory/graph/tools.ts +48 -9
- package/src/memory/indexer.ts +18 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
- package/src/memory/jobs/embed-concept-page.ts +261 -89
- package/src/memory/jobs-store.ts +51 -1
- package/src/memory/jobs-worker.ts +60 -7
- package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
- package/src/memory/llm-request-log-source-local.ts +26 -0
- package/src/memory/llm-request-log-source.ts +97 -0
- package/src/memory/llm-request-log-store.ts +1 -1
- package/src/memory/memory-retrospective-constants.ts +13 -0
- package/src/memory/memory-retrospective-enqueue.ts +114 -0
- package/src/memory/memory-retrospective-job.ts +351 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
- package/src/memory/memory-retrospective-state.ts +162 -0
- package/src/memory/memory-retrospective-trigger-check.ts +91 -0
- package/src/memory/memory-v2-activation-log-store.ts +49 -5
- package/src/memory/memory-v2-concept-frequency.ts +4 -0
- package/src/memory/message-content.ts +38 -1
- package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
- package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
- package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
- package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
- package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
- package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
- package/src/memory/migrations/242-message-bookmarks.ts +38 -0
- package/src/memory/migrations/243-provider-connections.ts +68 -0
- package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
- package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
- package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
- package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
- package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
- package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/pkb/pkb-search.test.ts +6 -5
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/published-pages-store.ts +16 -0
- package/src/memory/qdrant-client.ts +3 -0
- package/src/memory/schema/bookmarks.ts +38 -0
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/inference.ts +29 -0
- package/src/memory/schema/memory-core.ts +9 -0
- package/src/memory/search/semantic.ts +5 -9
- package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
- package/src/memory/v2/__tests__/activation.test.ts +46 -9
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
- package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
- package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
- package/src/memory/v2/__tests__/injection.test.ts +768 -33
- package/src/memory/v2/__tests__/migration.test.ts +7 -3
- package/src/memory/v2/__tests__/page-index.test.ts +277 -0
- package/src/memory/v2/__tests__/page-store.test.ts +14 -1
- package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
- package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
- package/src/memory/v2/__tests__/reranker.test.ts +4 -4
- package/src/memory/v2/__tests__/router.test.ts +516 -0
- package/src/memory/v2/__tests__/sim.test.ts +163 -8
- package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
- package/src/memory/v2/__tests__/static-context.test.ts +8 -35
- package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
- package/src/memory/v2/activation-store.ts +34 -5
- package/src/memory/v2/activation.ts +40 -27
- package/src/memory/v2/backfill-jobs.ts +17 -84
- package/src/memory/v2/consolidation-job.ts +92 -86
- package/src/memory/v2/frontmatter-sweep.ts +91 -0
- package/src/memory/v2/injection.ts +466 -115
- package/src/memory/v2/migration.ts +117 -20
- package/src/memory/v2/page-index.ts +191 -0
- package/src/memory/v2/page-store.ts +42 -0
- package/src/memory/v2/prompts/consolidation.ts +14 -7
- package/src/memory/v2/prompts/router.ts +192 -0
- package/src/memory/v2/qdrant.ts +307 -133
- package/src/memory/v2/reranker.ts +14 -7
- package/src/memory/v2/router.ts +322 -0
- package/src/memory/v2/sim.ts +88 -34
- package/src/memory/v2/skill-store.ts +118 -29
- package/src/memory/v2/static-context.ts +20 -17
- package/src/memory/v2/sweep-job.ts +127 -102
- package/src/memory/v2/types.ts +16 -5
- package/src/memory/validation.ts +13 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
- package/src/notifications/__tests__/signal-registry.test.ts +17 -0
- package/src/notifications/adapters/platform.ts +171 -0
- package/src/notifications/conversation-pairing.ts +2 -2
- package/src/notifications/copy-composer.ts +61 -12
- package/src/notifications/decision-engine.ts +46 -0
- package/src/notifications/destination-resolver.ts +21 -0
- package/src/notifications/emit-signal.ts +28 -1
- package/src/notifications/home-feed-side-effect.ts +111 -0
- package/src/notifications/signal.ts +5 -0
- package/src/permissions/checker.ts +12 -0
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/ipc-risk-types.ts +2 -0
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugin-api/index.ts +13 -0
- package/src/plugin-api/package.json +12 -0
- package/src/plugin-api/types.ts +62 -0
- package/src/plugins/defaults/injectors.ts +20 -5
- package/src/plugins/external-plugin-loader.ts +294 -0
- package/src/plugins/types.ts +46 -30
- package/src/plugins/user-loader.ts +64 -41
- package/src/proactive-artifact/job.test.ts +63 -8
- package/src/proactive-artifact/job.ts +20 -2
- package/src/proactive-artifact/message-copy.ts +18 -1
- package/src/proactive-artifact/trigger-state.test.ts +9 -0
- package/src/proactive-artifact/trigger-state.ts +4 -0
- package/src/prompts/__tests__/system-prompt.test.ts +105 -0
- package/src/prompts/system-prompt.ts +22 -1
- package/src/prompts/templates/SOUL.md +13 -28
- package/src/prompts/update-bulletin-job.ts +61 -73
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
- package/src/providers/__tests__/inference.test.ts +288 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +14 -32
- package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
- package/src/providers/anthropic/client.ts +95 -26
- package/src/providers/call-site-routing.ts +94 -16
- package/src/providers/connection-resolution.ts +163 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
- package/src/providers/inference/adapter-factory.ts +173 -0
- package/src/providers/inference/auth.ts +112 -0
- package/src/providers/inference/backfill.ts +196 -0
- package/src/providers/inference/connections.ts +356 -0
- package/src/providers/inference/resolve-auth.ts +65 -0
- package/src/providers/model-catalog.ts +104 -6
- package/src/providers/openai/responses-provider.ts +4 -2
- package/src/providers/provider-env-vars.ts +17 -7
- package/src/providers/provider-secret-catalog.ts +49 -30
- package/src/providers/provider-send-message.ts +41 -20
- package/src/providers/registry.ts +143 -159
- package/src/providers/retry.ts +18 -10
- package/src/providers/search-provider-catalog.ts +121 -0
- package/src/runtime/AGENTS.md +18 -5
- package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
- package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
- package/src/runtime/actor-trust-resolver.ts +32 -10
- package/src/runtime/agent-wake.ts +35 -6
- package/src/runtime/assistant-event-hub.ts +3 -85
- package/src/runtime/auth/route-policy.ts +304 -8
- package/src/runtime/auth/same-actor.ts +2 -0
- package/src/runtime/background-job-runner.ts +339 -0
- package/src/runtime/btw-sidechain.ts +1 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/guardian-reply-router.ts +0 -10
- package/src/runtime/http-router.ts +36 -1
- package/src/runtime/http-server.ts +31 -5
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
- package/src/runtime/middleware/request-logger.ts +62 -1
- package/src/runtime/pending-interactions.ts +19 -15
- package/src/runtime/pre-first-message-gate.ts +83 -0
- package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.ts +10 -8
- package/src/runtime/routes/app-management-routes.ts +228 -3
- package/src/runtime/routes/approval-routes.ts +7 -21
- package/src/runtime/routes/audit-routes.ts +43 -0
- package/src/runtime/routes/auth-routes.ts +72 -0
- package/src/runtime/routes/avatar-routes.ts +273 -20
- package/src/runtime/routes/backup-routes.ts +406 -2
- package/src/runtime/routes/bookmark-routes.ts +154 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -1
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/contact-routes.ts +0 -160
- package/src/runtime/routes/conversation-cli-routes.ts +192 -0
- package/src/runtime/routes/conversation-management-routes.ts +30 -43
- package/src/runtime/routes/conversation-query-routes.ts +373 -82
- package/src/runtime/routes/conversation-routes.ts +31 -10
- package/src/runtime/routes/conversations-import-routes.ts +229 -0
- package/src/runtime/routes/credential-routes.ts +540 -0
- package/src/runtime/routes/debug-bash-routes.ts +2 -0
- package/src/runtime/routes/debug-routes.ts +2 -2
- package/src/runtime/routes/document-pdf-renderer.ts +5 -1
- package/src/runtime/routes/domain-routes.ts +167 -0
- package/src/runtime/routes/email-routes.ts +603 -0
- package/src/runtime/routes/errors.ts +2 -2
- package/src/runtime/routes/events-routes.ts +192 -0
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/home-feed-routes.ts +6 -78
- package/src/runtime/routes/host-app-control-routes.ts +44 -2
- package/src/runtime/routes/host-browser-routes.ts +103 -22
- package/src/runtime/routes/http-adapter.ts +2 -0
- package/src/runtime/routes/identity-routes.ts +5 -0
- package/src/runtime/routes/image-generation-routes.ts +99 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
- package/src/runtime/routes/index.ts +36 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
- package/src/runtime/routes/inference-send-routes.ts +115 -0
- package/src/runtime/routes/integrations/twilio.ts +1 -0
- package/src/runtime/routes/mcp-auth-routes.ts +283 -9
- package/src/runtime/routes/memory-item-routes.test.ts +3 -9
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +105 -404
- package/src/runtime/routes/notification-routes.ts +2 -0
- package/src/runtime/routes/oauth-apps.ts +112 -7
- package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
- package/src/runtime/routes/oauth-connect-routes.ts +67 -5
- package/src/runtime/routes/oauth-providers.ts +298 -8
- package/src/runtime/routes/platform-routes.ts +336 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -1
- package/src/runtime/routes/playground/reset-circuit.ts +2 -1
- package/src/runtime/routes/playground/state.ts +2 -1
- package/src/runtime/routes/publish-routes.ts +221 -0
- package/src/runtime/routes/schedule-routes.ts +82 -0
- package/src/runtime/routes/sequence-routes.ts +291 -0
- package/src/runtime/routes/settings-routes.ts +2 -10
- package/src/runtime/routes/skills-routes.ts +31 -1
- package/src/runtime/routes/stt-routes.ts +240 -3
- package/src/runtime/routes/surface-action-routes.ts +43 -7
- package/src/runtime/routes/tts-routes.ts +67 -0
- package/src/runtime/routes/types.ts +32 -0
- package/src/runtime/routes/user-routes-cli.ts +243 -0
- package/src/runtime/routes/webhook-routes.ts +165 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/sync/sync-publisher.test.ts +105 -0
- package/src/runtime/sync/sync-publisher.ts +21 -0
- package/src/schedule/scheduler.ts +200 -123
- package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
- package/src/security/secret-patterns.ts +3 -0
- package/src/sequence/engine.ts +38 -40
- package/src/skills/include-graph.ts +35 -13
- package/src/subagent/manager.ts +20 -15
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
- package/src/tools/browser/browser-execution.ts +15 -4
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
- package/src/tools/browser/cdp-client/factory.ts +66 -5
- package/src/tools/browser/runtime-check.ts +77 -0
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/memory/register.test.ts +10 -8
- package/src/tools/memory/register.ts +9 -1
- package/src/tools/network/__tests__/web-search.test.ts +156 -0
- package/src/tools/network/web-search.ts +280 -37
- package/src/tools/permission-checker.ts +28 -5
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/subagent/spawn.ts +3 -3
- package/src/tools/terminal/shell.ts +44 -0
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +19 -1
- package/src/usage/attribution.ts +3 -2
- package/src/util/pricing.ts +86 -160
- package/src/watcher/__tests__/engine.test.ts +301 -0
- package/src/watcher/constants.ts +7 -0
- package/src/watcher/engine.ts +90 -90
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
- package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
- package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
- package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
- package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
- package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
- package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
- package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
- package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
- package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
- package/src/workspace/migrations/registry.ts +28 -0
- package/src/workspace/migrations/runner.ts +13 -2
- package/src/workspace/migrations/types.ts +13 -3
- package/src/workspace/provider-commit-message-generator.ts +3 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -492
- package/src/__tests__/credentials-cli.test.ts +0 -1225
- package/src/__tests__/memory-admin-recall.test.ts +0 -213
- package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
- package/src/cli/commands/__tests__/email-download.test.ts +0 -260
- package/src/cli/commands/__tests__/email-list.test.ts +0 -216
- package/src/cli/commands/__tests__/email-register.test.ts +0 -186
- package/src/cli/commands/__tests__/email-send.test.ts +0 -416
- package/src/cli/commands/__tests__/email-status.test.ts +0 -185
- package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
- package/src/cli/commands/__tests__/routes.test.ts +0 -562
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
- package/src/cli/commands/autonomy.ts +0 -365
- package/src/cli/commands/memory.ts +0 -424
- package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
- package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
- package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
- package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
- package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
- package/src/cli/lib/daemon-avatar-client.ts +0 -37
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
- package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
- package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
- package/src/home/__tests__/emit-feed-event.test.ts +0 -169
- package/src/home/__tests__/feed-population-integration.test.ts +0 -312
- package/src/home/__tests__/feed-scheduler.test.ts +0 -222
- package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
- package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
- package/src/home/__tests__/rollup-producer.test.ts +0 -507
- package/src/home/assistant-feed-authoring.ts +0 -135
- package/src/home/emit-feed-event.ts +0 -169
- package/src/home/feed-scheduler.ts +0 -281
- package/src/home/platform-gmail-digest.ts +0 -163
- package/src/home/rewrite-command-preview.ts +0 -66
- package/src/home/rewrite-feed-title.ts +0 -58
- package/src/home/rollup-producer.ts +0 -426
- package/src/memory/admin.ts +0 -326
- package/src/memory/context-search/sources/pkb.ts +0 -477
- package/src/memory/graph/compaction.ts +0 -299
- /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
|
@@ -1,33 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `
|
|
2
|
+
* `assistant backup` — manage automated backup configuration and list snapshots.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* so the on-disk `config.json` is the single source of truth and the daemon's
|
|
7
|
-
* config cache is invalidated via `saveRawConfig`.
|
|
4
|
+
* Thin IPC wrapper: each subcommand forwards its request to the daemon via
|
|
5
|
+
* cliIpcCall and never imports daemon-internal modules.
|
|
8
6
|
*/
|
|
9
7
|
|
|
10
|
-
import { stat } from "node:fs/promises";
|
|
11
|
-
import { dirname } from "node:path";
|
|
12
|
-
|
|
13
8
|
import type { Command } from "commander";
|
|
14
9
|
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
type SnapshotEntry,
|
|
18
|
-
} from "../../backup/list-snapshots.js";
|
|
19
|
-
import {
|
|
20
|
-
getLocalBackupsDir,
|
|
21
|
-
resolveOffsiteDestinations,
|
|
22
|
-
} from "../../backup/paths.js";
|
|
23
|
-
import {
|
|
24
|
-
getConfig,
|
|
25
|
-
loadRawConfig,
|
|
26
|
-
saveRawConfig,
|
|
27
|
-
setNestedValue,
|
|
28
|
-
} from "../../config/loader.js";
|
|
29
|
-
import type { BackupDestination } from "../../config/schema.js";
|
|
30
|
-
import { getMemoryCheckpoint } from "../../memory/checkpoints.js";
|
|
10
|
+
import { cliIpcCall, exitFromIpcResult } from "../../ipc/cli-client.js";
|
|
11
|
+
import { registerCommand } from "../lib/register-command.js";
|
|
31
12
|
import { log } from "../logger.js";
|
|
32
13
|
|
|
33
14
|
// ---------------------------------------------------------------------------
|
|
@@ -43,7 +24,7 @@ function formatBytes(bytes: number): string {
|
|
|
43
24
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
44
25
|
}
|
|
45
26
|
|
|
46
|
-
/** Format
|
|
27
|
+
/** Format an ISO date string as `YYYY-MM-DD HH:MM UTC`. */
|
|
47
28
|
function formatDate(date: Date): string {
|
|
48
29
|
const y = date.getUTCFullYear().toString().padStart(4, "0");
|
|
49
30
|
const mo = (date.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
@@ -72,348 +53,19 @@ function formatDurationShort(ms: number): string {
|
|
|
72
53
|
return `${days}d ${remHours}h`;
|
|
73
54
|
}
|
|
74
55
|
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// Reachability probe
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Check whether an offsite destination's parent directory exists. Mirrors the
|
|
81
|
-
* reachability check in the backup worker — if the parent is missing (e.g.
|
|
82
|
-
* iCloud Drive not enabled, external SSD unplugged) the destination is
|
|
83
|
-
* considered unreachable and we skip it at runtime.
|
|
84
|
-
*/
|
|
85
|
-
async function isDestinationReachable(destPath: string): Promise<boolean> {
|
|
86
|
-
try {
|
|
87
|
-
await stat(dirname(destPath));
|
|
88
|
-
return true;
|
|
89
|
-
} catch {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
|
-
// Exported handlers — exported so tests can drive them directly.
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
|
|
98
|
-
export interface EnableOptions {
|
|
99
|
-
interval?: string;
|
|
100
|
-
retention?: string;
|
|
101
|
-
offsite?: boolean;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function handleEnable(opts: EnableOptions): void {
|
|
105
|
-
const raw = loadRawConfig();
|
|
106
|
-
setNestedValue(raw, "backup.enabled", true);
|
|
107
|
-
|
|
108
|
-
if (opts.interval !== undefined) {
|
|
109
|
-
const hours = Number.parseInt(opts.interval, 10);
|
|
110
|
-
if (!Number.isFinite(hours) || hours < 1) {
|
|
111
|
-
log.error(
|
|
112
|
-
`Invalid --interval "${opts.interval}". Must be a positive integer (hours). ` +
|
|
113
|
-
`Run 'vellum backup enable --help' for usage.`,
|
|
114
|
-
);
|
|
115
|
-
process.exitCode = 1;
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
setNestedValue(raw, "backup.intervalHours", hours);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (opts.retention !== undefined) {
|
|
122
|
-
const count = Number.parseInt(opts.retention, 10);
|
|
123
|
-
if (!Number.isFinite(count) || count < 1) {
|
|
124
|
-
log.error(
|
|
125
|
-
`Invalid --retention "${opts.retention}". Must be a positive integer. ` +
|
|
126
|
-
`Run 'vellum backup enable --help' for usage.`,
|
|
127
|
-
);
|
|
128
|
-
process.exitCode = 1;
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
setNestedValue(raw, "backup.retention", count);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// commander's `.option("--no-offsite", ...)` sets `opts.offsite = false`
|
|
135
|
-
// when the flag is present and leaves it `undefined` otherwise. Only a
|
|
136
|
-
// literal `false` flips the offsite switch — we never touch `destinations`.
|
|
137
|
-
if (opts.offsite === false) {
|
|
138
|
-
setNestedValue(raw, "backup.offsite.enabled", false);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
saveRawConfig(raw);
|
|
142
|
-
|
|
143
|
-
const cfg = getConfig().backup;
|
|
144
|
-
log.info(
|
|
145
|
-
`Automatic backups enabled (interval=${cfg.intervalHours}h, retention=${cfg.retention}, offsite=${cfg.offsite.enabled ? "on" : "off"})`,
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function handleDisable(): void {
|
|
150
|
-
const raw = loadRawConfig();
|
|
151
|
-
setNestedValue(raw, "backup.enabled", false);
|
|
152
|
-
saveRawConfig(raw);
|
|
153
|
-
log.info("Automatic backups disabled");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
// destinations subgroup handlers
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Load the raw destinations array, materializing the iCloud default on first
|
|
162
|
-
* touch. Returns the array plus the raw config so callers can mutate and
|
|
163
|
-
* re-persist.
|
|
164
|
-
*
|
|
165
|
-
* When `backup.offsite.destinations` is `null` in config, the runtime uses the
|
|
166
|
-
* iCloud default — but that default is implicit. On first `add`/`remove`/
|
|
167
|
-
* `set-encrypt`, we need to make it explicit so subsequent mutations have
|
|
168
|
-
* something to mutate.
|
|
169
|
-
*/
|
|
170
|
-
function loadDestinationsForMutation(): {
|
|
171
|
-
raw: Record<string, unknown>;
|
|
172
|
-
destinations: BackupDestination[];
|
|
173
|
-
} {
|
|
174
|
-
const raw = loadRawConfig();
|
|
175
|
-
const current = getConfig().backup.offsite.destinations;
|
|
176
|
-
const destinations = resolveOffsiteDestinations(current);
|
|
177
|
-
return { raw, destinations };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export async function handleDestinationsList(): Promise<void> {
|
|
181
|
-
const cfg = getConfig().backup;
|
|
182
|
-
const destinations = resolveOffsiteDestinations(cfg.offsite.destinations);
|
|
183
|
-
|
|
184
|
-
if (destinations.length === 0) {
|
|
185
|
-
log.info("No offsite destinations configured");
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const pathW = Math.max(4, ...destinations.map((d) => d.path.length));
|
|
190
|
-
log.info("Path".padEnd(pathW) + " " + "Encrypted");
|
|
191
|
-
log.info("-".repeat(pathW + 2 + 9));
|
|
192
|
-
for (const d of destinations) {
|
|
193
|
-
log.info(d.path.padEnd(pathW) + " " + (d.encrypt ? "yes" : "no"));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export interface DestinationAddOptions {
|
|
198
|
-
plaintext?: boolean;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function handleDestinationsAdd(
|
|
202
|
-
path: string,
|
|
203
|
-
opts: DestinationAddOptions,
|
|
204
|
-
): void {
|
|
205
|
-
const { raw, destinations } = loadDestinationsForMutation();
|
|
206
|
-
|
|
207
|
-
if (destinations.some((d) => d.path === path)) {
|
|
208
|
-
log.error(
|
|
209
|
-
`Destination "${path}" already exists. Run 'vellum backup destinations list' to see configured destinations.`,
|
|
210
|
-
);
|
|
211
|
-
process.exitCode = 1;
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const next: BackupDestination[] = [
|
|
216
|
-
...destinations,
|
|
217
|
-
{ path, encrypt: !opts.plaintext },
|
|
218
|
-
];
|
|
219
|
-
setNestedValue(raw, "backup.offsite.destinations", next);
|
|
220
|
-
saveRawConfig(raw);
|
|
221
|
-
log.info(
|
|
222
|
-
`Added destination ${path} (${opts.plaintext ? "plaintext" : "encrypted"})`,
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export function handleDestinationsRemove(path: string): void {
|
|
227
|
-
const { raw, destinations } = loadDestinationsForMutation();
|
|
228
|
-
|
|
229
|
-
const filtered = destinations.filter((d) => d.path !== path);
|
|
230
|
-
if (filtered.length === destinations.length) {
|
|
231
|
-
log.error(
|
|
232
|
-
`Destination "${path}" not found. Run 'vellum backup destinations list' to see configured destinations.`,
|
|
233
|
-
);
|
|
234
|
-
process.exitCode = 1;
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
setNestedValue(raw, "backup.offsite.destinations", filtered);
|
|
239
|
-
saveRawConfig(raw);
|
|
240
|
-
log.info(`Removed destination ${path}`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function handleDestinationsSetEncrypt(
|
|
244
|
-
path: string,
|
|
245
|
-
value: string,
|
|
246
|
-
): void {
|
|
247
|
-
const normalized = value.toLowerCase();
|
|
248
|
-
if (normalized !== "true" && normalized !== "false") {
|
|
249
|
-
log.error(
|
|
250
|
-
`Invalid encrypt value "${value}". Must be "true" or "false". ` +
|
|
251
|
-
`Run 'vellum backup destinations set-encrypt --help' for usage.`,
|
|
252
|
-
);
|
|
253
|
-
process.exitCode = 1;
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
const encrypt = normalized === "true";
|
|
257
|
-
|
|
258
|
-
const { raw, destinations } = loadDestinationsForMutation();
|
|
259
|
-
const idx = destinations.findIndex((d) => d.path === path);
|
|
260
|
-
if (idx === -1) {
|
|
261
|
-
log.error(
|
|
262
|
-
`Destination "${path}" not found. Run 'vellum backup destinations list' to see configured destinations.`,
|
|
263
|
-
);
|
|
264
|
-
process.exitCode = 1;
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const next = destinations.map((d, i) => (i === idx ? { ...d, encrypt } : d));
|
|
269
|
-
setNestedValue(raw, "backup.offsite.destinations", next);
|
|
270
|
-
saveRawConfig(raw);
|
|
271
|
-
log.info(`Set ${path} encrypt=${encrypt ? "true" : "false"}`);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// status
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
|
|
278
|
-
export async function handleStatus(): Promise<void> {
|
|
279
|
-
const cfg = getConfig().backup;
|
|
280
|
-
|
|
281
|
-
log.info(`Automatic backups: ${cfg.enabled ? "enabled" : "disabled"}`);
|
|
282
|
-
log.info(`Interval: every ${cfg.intervalHours}h`);
|
|
283
|
-
log.info(`Retention: ${cfg.retention} snapshots per destination`);
|
|
284
|
-
|
|
285
|
-
// Last / next run — both gated on a valid checkpoint. The daemon records
|
|
286
|
-
// `backup:last_run_at` as a unix-millis string.
|
|
287
|
-
const lastRunRaw = getMemoryCheckpoint("backup:last_run_at");
|
|
288
|
-
const lastRunMs = lastRunRaw ? Number.parseInt(lastRunRaw, 10) : NaN;
|
|
289
|
-
const now = Date.now();
|
|
290
|
-
if (!Number.isNaN(lastRunMs)) {
|
|
291
|
-
const lastRunDate = new Date(lastRunMs);
|
|
292
|
-
log.info(
|
|
293
|
-
`Last run: ${formatDate(lastRunDate)} (${formatDurationShort(now - lastRunMs)} ago)`,
|
|
294
|
-
);
|
|
295
|
-
if (cfg.enabled) {
|
|
296
|
-
const intervalMs = cfg.intervalHours * 3600 * 1000;
|
|
297
|
-
const nextMs = lastRunMs + intervalMs;
|
|
298
|
-
const delta = nextMs - now;
|
|
299
|
-
if (delta <= 0) {
|
|
300
|
-
log.info(`Next run: due now`);
|
|
301
|
-
} else {
|
|
302
|
-
log.info(`Next run: in ${formatDurationShort(delta)}`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
} else {
|
|
306
|
-
log.info(`Last run: never`);
|
|
307
|
-
if (cfg.enabled) {
|
|
308
|
-
log.info(`Next run: on next tick`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Local directory line — include snapshot count so users can confirm the
|
|
313
|
-
// pool size matches retention.
|
|
314
|
-
const localDir = getLocalBackupsDir(cfg.localDirectory);
|
|
315
|
-
const localSnapshots = await listSnapshotsInDir(localDir);
|
|
316
|
-
log.info(
|
|
317
|
-
`Local directory: ${localDir} (${localSnapshots.length} snapshots)`,
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
// Offsite destinations — resolve the iCloud default, probe reachability
|
|
321
|
-
// for each, and report snapshot counts.
|
|
322
|
-
log.info(`Offsite:`);
|
|
323
|
-
if (!cfg.offsite.enabled) {
|
|
324
|
-
log.info(` (disabled)`);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
const destinations = resolveOffsiteDestinations(cfg.offsite.destinations);
|
|
328
|
-
if (destinations.length === 0) {
|
|
329
|
-
log.info(` (no destinations configured)`);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
for (const dest of destinations) {
|
|
333
|
-
const reachable = await isDestinationReachable(dest.path);
|
|
334
|
-
const tag = reachable ? "[OK]" : "[unreachable]";
|
|
335
|
-
const enc = dest.encrypt ? "encrypted" : "plaintext";
|
|
336
|
-
const snapshots = reachable ? await listSnapshotsInDir(dest.path) : [];
|
|
337
|
-
const suffix = reachable ? "" : " -- parent directory not reachable";
|
|
338
|
-
log.info(
|
|
339
|
-
` ${tag} ${dest.path} (${enc}, ${snapshots.length} snapshots)${suffix}`,
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// ---------------------------------------------------------------------------
|
|
345
|
-
// list
|
|
346
|
-
// ---------------------------------------------------------------------------
|
|
347
|
-
|
|
348
|
-
/** Print a snapshot table for a group of entries. */
|
|
349
|
-
function printSnapshotGroup(heading: string, entries: SnapshotEntry[]): void {
|
|
350
|
-
log.info(heading);
|
|
351
|
-
if (entries.length === 0) {
|
|
352
|
-
log.info(" (none)");
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
const tsW = 19;
|
|
356
|
-
const sizeW = 10;
|
|
357
|
-
const encW = 9;
|
|
358
|
-
log.info(
|
|
359
|
-
" " +
|
|
360
|
-
"Timestamp".padEnd(tsW) +
|
|
361
|
-
" " +
|
|
362
|
-
"Size".padEnd(sizeW) +
|
|
363
|
-
" " +
|
|
364
|
-
"Encrypted".padEnd(encW) +
|
|
365
|
-
" " +
|
|
366
|
-
"Filename",
|
|
367
|
-
);
|
|
368
|
-
for (const e of entries) {
|
|
369
|
-
log.info(
|
|
370
|
-
" " +
|
|
371
|
-
formatDate(e.createdAt).padEnd(tsW) +
|
|
372
|
-
" " +
|
|
373
|
-
formatBytes(e.sizeBytes).padEnd(sizeW) +
|
|
374
|
-
" " +
|
|
375
|
-
(e.encrypted ? "yes" : "no").padEnd(encW) +
|
|
376
|
-
" " +
|
|
377
|
-
e.filename,
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export async function handleList(): Promise<void> {
|
|
383
|
-
const cfg = getConfig().backup;
|
|
384
|
-
const localDir = getLocalBackupsDir(cfg.localDirectory);
|
|
385
|
-
const localSnapshots = await listSnapshotsInDir(localDir);
|
|
386
|
-
printSnapshotGroup(`Local: ${localDir}`, localSnapshots);
|
|
387
|
-
|
|
388
|
-
if (!cfg.offsite.enabled) return;
|
|
389
|
-
const destinations = resolveOffsiteDestinations(cfg.offsite.destinations);
|
|
390
|
-
for (const dest of destinations) {
|
|
391
|
-
const entries = await listSnapshotsInDir(dest.path);
|
|
392
|
-
const tag = dest.encrypt ? "encrypted" : "plaintext";
|
|
393
|
-
log.info("");
|
|
394
|
-
printSnapshotGroup(`Offsite: ${dest.path} (${tag})`, entries);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// ---------------------------------------------------------------------------
|
|
399
|
-
// ---------------------------------------------------------------------------
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
56
|
// ---------------------------------------------------------------------------
|
|
404
57
|
// Command wiring
|
|
405
58
|
// ---------------------------------------------------------------------------
|
|
406
59
|
|
|
407
60
|
export function registerBackupCommand(program: Command): void {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
`
|
|
61
|
+
registerCommand(program, {
|
|
62
|
+
name: "backup",
|
|
63
|
+
transport: "ipc",
|
|
64
|
+
description: "Manage automated backup configuration and list snapshots",
|
|
65
|
+
build: (backup) => {
|
|
66
|
+
backup.addHelpText(
|
|
67
|
+
"after",
|
|
68
|
+
`
|
|
417
69
|
Backups capture a snapshot of the assistant workspace (config, conversations,
|
|
418
70
|
trust rules, hooks, the SQLite database) as a .vbundle file. Credentials are
|
|
419
71
|
NOT included — they live in the OS keychain / CES and users re-authenticate
|
|
@@ -427,71 +79,105 @@ plaintext — plaintext only makes sense when the user owns physical access to
|
|
|
427
79
|
the medium (e.g. an external SSD).
|
|
428
80
|
|
|
429
81
|
Examples:
|
|
430
|
-
$
|
|
431
|
-
$
|
|
432
|
-
$
|
|
433
|
-
$
|
|
434
|
-
|
|
82
|
+
$ assistant backup enable --interval 6 --retention 3
|
|
83
|
+
$ assistant backup destinations add /Volumes/BackupSSD/vellum --plaintext
|
|
84
|
+
$ assistant backup status
|
|
85
|
+
$ assistant backup list`,
|
|
86
|
+
);
|
|
435
87
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
88
|
+
backup
|
|
89
|
+
.command("enable")
|
|
90
|
+
.description("Enable automated backups")
|
|
91
|
+
.option(
|
|
92
|
+
"--interval <hours>",
|
|
93
|
+
"Hours between automated backups (1-168). Defaults to 6.",
|
|
94
|
+
)
|
|
95
|
+
.option(
|
|
96
|
+
"--retention <n>",
|
|
97
|
+
"Snapshots to retain per destination (1-100). Defaults to 3.",
|
|
98
|
+
)
|
|
99
|
+
.option(
|
|
100
|
+
"--no-offsite",
|
|
101
|
+
"Disable offsite backup (local only). Does not touch the destinations list.",
|
|
102
|
+
)
|
|
103
|
+
.addHelpText(
|
|
104
|
+
"after",
|
|
105
|
+
`
|
|
454
106
|
Sets backup.enabled = true in config.json. Optionally overrides intervalHours,
|
|
455
107
|
retention, and the offsite.enabled flag. Does NOT modify
|
|
456
|
-
backup.offsite.destinations — use '
|
|
108
|
+
backup.offsite.destinations — use 'assistant backup destinations add/remove' to
|
|
457
109
|
manage those.
|
|
458
110
|
|
|
459
111
|
Examples:
|
|
460
|
-
$
|
|
461
|
-
$
|
|
462
|
-
$
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
112
|
+
$ assistant backup enable
|
|
113
|
+
$ assistant backup enable --interval 12 --retention 14
|
|
114
|
+
$ assistant backup enable --no-offsite`,
|
|
115
|
+
)
|
|
116
|
+
.action(
|
|
117
|
+
async (
|
|
118
|
+
opts: { interval?: string; retention?: string; offsite?: boolean },
|
|
119
|
+
cmd: Command,
|
|
120
|
+
) => {
|
|
121
|
+
const r = await cliIpcCall("backup_enable", {
|
|
122
|
+
body: {
|
|
123
|
+
...(opts.interval !== undefined && {
|
|
124
|
+
intervalHours: Number.parseInt(opts.interval, 10),
|
|
125
|
+
}),
|
|
126
|
+
...(opts.retention !== undefined && {
|
|
127
|
+
retention: Number.parseInt(opts.retention, 10),
|
|
128
|
+
}),
|
|
129
|
+
...(opts.offsite === false && { offsiteEnabled: false }),
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
if (!r.ok)
|
|
133
|
+
return exitFromIpcResult(
|
|
134
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
135
|
+
cmd,
|
|
136
|
+
);
|
|
137
|
+
const cfg = r.result as {
|
|
138
|
+
intervalHours: number;
|
|
139
|
+
retention: number;
|
|
140
|
+
offsite: { enabled: boolean };
|
|
141
|
+
};
|
|
142
|
+
log.info(
|
|
143
|
+
`Automatic backups enabled (interval=${cfg.intervalHours}h, retention=${cfg.retention}, offsite=${cfg.offsite.enabled ? "on" : "off"})`,
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
backup
|
|
149
|
+
.command("disable")
|
|
150
|
+
.description("Disable automated backups")
|
|
151
|
+
.addHelpText(
|
|
152
|
+
"after",
|
|
153
|
+
`
|
|
474
154
|
Sets backup.enabled = false in config.json. Existing snapshots are untouched;
|
|
475
155
|
only the automated worker stops creating new ones.
|
|
476
156
|
|
|
477
157
|
Examples:
|
|
478
|
-
$
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
158
|
+
$ assistant backup disable`,
|
|
159
|
+
)
|
|
160
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
161
|
+
const r = await cliIpcCall("backup_disable");
|
|
162
|
+
if (!r.ok)
|
|
163
|
+
return exitFromIpcResult(
|
|
164
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
165
|
+
cmd,
|
|
166
|
+
);
|
|
167
|
+
log.info("Automatic backups disabled");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// -----------------------------------------------------------------------
|
|
171
|
+
// destinations — subgroup
|
|
172
|
+
// -----------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
const destinations = backup
|
|
175
|
+
.command("destinations")
|
|
176
|
+
.description("Manage offsite backup destinations");
|
|
177
|
+
|
|
178
|
+
destinations.addHelpText(
|
|
179
|
+
"after",
|
|
180
|
+
`
|
|
495
181
|
Offsite destinations are absolute paths the backup worker writes a copy of
|
|
496
182
|
each snapshot to after the local write succeeds. The default destination is
|
|
497
183
|
the iCloud Drive VellumAssistant folder, and it is used implicitly until an
|
|
@@ -504,39 +190,57 @@ are written as .vbundle.enc (AES-256-GCM). When false, snapshots are copied
|
|
|
504
190
|
as plaintext .vbundle — only use this for media you control physically.
|
|
505
191
|
|
|
506
192
|
Examples:
|
|
507
|
-
$
|
|
508
|
-
$
|
|
509
|
-
$
|
|
510
|
-
$
|
|
511
|
-
|
|
193
|
+
$ assistant backup destinations list
|
|
194
|
+
$ assistant backup destinations add /Volumes/BackupSSD/vellum --plaintext
|
|
195
|
+
$ assistant backup destinations remove /Volumes/BackupSSD/vellum
|
|
196
|
+
$ assistant backup destinations set-encrypt /Volumes/BackupSSD/vellum false`,
|
|
197
|
+
);
|
|
512
198
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
199
|
+
destinations
|
|
200
|
+
.command("list")
|
|
201
|
+
.description("List configured offsite destinations")
|
|
202
|
+
.addHelpText(
|
|
203
|
+
"after",
|
|
204
|
+
`
|
|
519
205
|
Resolves the current destinations array (materializing the iCloud default if
|
|
520
206
|
no explicit array is configured) and prints a table with the path and
|
|
521
207
|
encryption flag per row.
|
|
522
208
|
|
|
523
209
|
Examples:
|
|
524
|
-
$
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
210
|
+
$ assistant backup destinations list`,
|
|
211
|
+
)
|
|
212
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
213
|
+
const r = await cliIpcCall<{
|
|
214
|
+
destinations: Array<{ path: string; encrypt: boolean }>;
|
|
215
|
+
}>("backup_destinations_list");
|
|
216
|
+
if (!r.ok)
|
|
217
|
+
return exitFromIpcResult(
|
|
218
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
219
|
+
cmd,
|
|
220
|
+
);
|
|
221
|
+
const { destinations: dests } = r.result!;
|
|
222
|
+
if (dests.length === 0) {
|
|
223
|
+
log.info("No offsite destinations configured");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const pathW = Math.max(4, ...dests.map((d) => d.path.length));
|
|
227
|
+
log.info("Path".padEnd(pathW) + " " + "Encrypted");
|
|
228
|
+
log.info("-".repeat(pathW + 2 + 9));
|
|
229
|
+
for (const d of dests) {
|
|
230
|
+
log.info(d.path.padEnd(pathW) + " " + (d.encrypt ? "yes" : "no"));
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
destinations
|
|
235
|
+
.command("add <path>")
|
|
236
|
+
.description("Add an offsite backup destination")
|
|
237
|
+
.option(
|
|
238
|
+
"--plaintext",
|
|
239
|
+
"Write snapshots as plaintext .vbundle (default is AES-256-GCM encrypted .vbundle.enc)",
|
|
240
|
+
)
|
|
241
|
+
.addHelpText(
|
|
242
|
+
"after",
|
|
243
|
+
`
|
|
540
244
|
Arguments:
|
|
541
245
|
path Absolute path to the destination directory. Must be on a mount the
|
|
542
246
|
caller controls; the backup worker writes files inside this
|
|
@@ -547,64 +251,107 @@ the iCloud default is materialized first so the new entry appends to a
|
|
|
547
251
|
2-element array rather than replacing the default.
|
|
548
252
|
|
|
549
253
|
Examples:
|
|
550
|
-
$
|
|
551
|
-
$
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
254
|
+
$ assistant backup destinations add /Volumes/BackupSSD/vellum --plaintext
|
|
255
|
+
$ assistant backup destinations add ~/Dropbox/VellumAssistant/backups`,
|
|
256
|
+
)
|
|
257
|
+
.action(
|
|
258
|
+
async (path: string, opts: { plaintext?: boolean }, cmd: Command) => {
|
|
259
|
+
const r = await cliIpcCall("backup_destinations_add", {
|
|
260
|
+
body: {
|
|
261
|
+
path,
|
|
262
|
+
encrypt: !opts.plaintext,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
if (!r.ok)
|
|
266
|
+
return exitFromIpcResult(
|
|
267
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
268
|
+
cmd,
|
|
269
|
+
);
|
|
270
|
+
log.info(
|
|
271
|
+
`Added destination ${path} (${opts.plaintext ? "plaintext" : "encrypted"})`,
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
destinations
|
|
277
|
+
.command("remove <path>")
|
|
278
|
+
.description("Remove an offsite backup destination by path")
|
|
279
|
+
.addHelpText(
|
|
280
|
+
"after",
|
|
281
|
+
`
|
|
563
282
|
Arguments:
|
|
564
283
|
path Exact path match of the destination to remove. Run
|
|
565
|
-
'
|
|
284
|
+
'assistant backup destinations list' to see configured paths.
|
|
566
285
|
|
|
567
286
|
Errors if no destination with the given path exists.
|
|
568
287
|
|
|
569
288
|
Examples:
|
|
570
|
-
$
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
289
|
+
$ assistant backup destinations remove /Volumes/BackupSSD/vellum`,
|
|
290
|
+
)
|
|
291
|
+
.action(async (path: string, _opts: unknown, cmd: Command) => {
|
|
292
|
+
const r = await cliIpcCall("backup_destinations_remove", { body: { path } });
|
|
293
|
+
if (!r.ok)
|
|
294
|
+
return exitFromIpcResult(
|
|
295
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
296
|
+
cmd,
|
|
297
|
+
);
|
|
298
|
+
log.info(`Removed destination ${path}`);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
destinations
|
|
302
|
+
.command("set-encrypt <path> <value>")
|
|
303
|
+
.description("Toggle encryption for an existing destination")
|
|
304
|
+
.addHelpText(
|
|
305
|
+
"after",
|
|
306
|
+
`
|
|
582
307
|
Arguments:
|
|
583
308
|
path Exact path match of an existing destination. Run
|
|
584
|
-
'
|
|
309
|
+
'assistant backup destinations list' to see configured paths.
|
|
585
310
|
value "true" to encrypt, "false" for plaintext writes.
|
|
586
311
|
|
|
587
312
|
Errors if no destination with the given path exists. Existing snapshot files
|
|
588
313
|
are not modified; only future writes honour the new setting.
|
|
589
314
|
|
|
590
315
|
Examples:
|
|
591
|
-
$
|
|
592
|
-
$
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
316
|
+
$ assistant backup destinations set-encrypt /Volumes/BackupSSD/vellum false
|
|
317
|
+
$ assistant backup destinations set-encrypt /Volumes/BackupSSD/vellum true`,
|
|
318
|
+
)
|
|
319
|
+
.action(
|
|
320
|
+
async (path: string, value: string, _opts: unknown, cmd: Command) => {
|
|
321
|
+
const normalized = value.toLowerCase();
|
|
322
|
+
if (normalized !== "true" && normalized !== "false") {
|
|
323
|
+
log.error(
|
|
324
|
+
`Invalid encrypt value "${value}". Must be "true" or "false". ` +
|
|
325
|
+
`Run 'assistant backup destinations set-encrypt --help' for usage.`,
|
|
326
|
+
);
|
|
327
|
+
process.exitCode = 1;
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const r = await cliIpcCall("backup_destinations_set_encrypt", {
|
|
331
|
+
body: {
|
|
332
|
+
path,
|
|
333
|
+
encrypt: normalized === "true",
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
if (!r.ok)
|
|
337
|
+
return exitFromIpcResult(
|
|
338
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
339
|
+
cmd,
|
|
340
|
+
);
|
|
341
|
+
log.info(`Set ${path} encrypt=${normalized}`);
|
|
342
|
+
},
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// -----------------------------------------------------------------------
|
|
346
|
+
// status / list
|
|
347
|
+
// -----------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
backup
|
|
350
|
+
.command("status")
|
|
351
|
+
.description("Show backup status and next-run timing")
|
|
352
|
+
.addHelpText(
|
|
353
|
+
"after",
|
|
354
|
+
`
|
|
608
355
|
Reports enabled/disabled state, interval and retention, last-run and next-run
|
|
609
356
|
timing (from the backup:last_run_at memory checkpoint), and a per-destination
|
|
610
357
|
reachability probe. Unreachable destinations (parent directory missing, e.g.
|
|
@@ -612,27 +359,184 @@ iCloud Drive not enabled or external volume unplugged) are flagged
|
|
|
612
359
|
[unreachable] and skipped by the worker.
|
|
613
360
|
|
|
614
361
|
Examples:
|
|
615
|
-
$
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
362
|
+
$ assistant backup status`,
|
|
363
|
+
)
|
|
364
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
365
|
+
const r = await cliIpcCall<{
|
|
366
|
+
enabled: boolean;
|
|
367
|
+
intervalHours: number;
|
|
368
|
+
retention: number;
|
|
369
|
+
lastRunAt: string | null;
|
|
370
|
+
nextRunAt: string | null;
|
|
371
|
+
localDir: string;
|
|
372
|
+
localSnapshotCount: number;
|
|
373
|
+
offsiteEnabled: boolean;
|
|
374
|
+
offsite: Array<{
|
|
375
|
+
path: string;
|
|
376
|
+
encrypt: boolean;
|
|
377
|
+
reachable: boolean;
|
|
378
|
+
snapshotCount: number;
|
|
379
|
+
}>;
|
|
380
|
+
}>("backup_status");
|
|
381
|
+
if (!r.ok)
|
|
382
|
+
return exitFromIpcResult(
|
|
383
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
384
|
+
cmd,
|
|
385
|
+
);
|
|
386
|
+
const s = r.result!;
|
|
387
|
+
const now = Date.now();
|
|
388
|
+
|
|
389
|
+
log.info(`Automatic backups: ${s.enabled ? "enabled" : "disabled"}`);
|
|
390
|
+
log.info(`Interval: every ${s.intervalHours}h`);
|
|
391
|
+
log.info(
|
|
392
|
+
`Retention: ${s.retention} snapshots per destination`,
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
if (s.lastRunAt) {
|
|
396
|
+
const lastRunMs = new Date(s.lastRunAt).getTime();
|
|
397
|
+
log.info(
|
|
398
|
+
`Last run: ${formatDate(new Date(s.lastRunAt))} (${formatDurationShort(now - lastRunMs)} ago)`,
|
|
399
|
+
);
|
|
400
|
+
if (s.enabled && s.nextRunAt) {
|
|
401
|
+
const nextMs = new Date(s.nextRunAt).getTime();
|
|
402
|
+
const delta = nextMs - now;
|
|
403
|
+
if (delta <= 0) {
|
|
404
|
+
log.info(`Next run: due now`);
|
|
405
|
+
} else {
|
|
406
|
+
log.info(`Next run: in ${formatDurationShort(delta)}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
log.info(`Last run: never`);
|
|
411
|
+
if (s.enabled) {
|
|
412
|
+
log.info(`Next run: on next tick`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
log.info(
|
|
417
|
+
`Local directory: ${s.localDir} (${s.localSnapshotCount} snapshots)`,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
log.info(
|
|
421
|
+
`Offsite: ${s.offsiteEnabled ? "enabled" : "disabled"}`,
|
|
422
|
+
);
|
|
423
|
+
if (!s.offsiteEnabled) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (s.offsite.length === 0) {
|
|
427
|
+
log.info(` (no destinations configured)`);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
for (const dest of s.offsite) {
|
|
431
|
+
const tag = dest.reachable ? "[OK]" : "[unreachable]";
|
|
432
|
+
const enc = dest.encrypt ? "encrypted" : "plaintext";
|
|
433
|
+
const suffix = dest.reachable
|
|
434
|
+
? ""
|
|
435
|
+
: " -- parent directory not reachable";
|
|
436
|
+
log.info(
|
|
437
|
+
` ${tag} ${dest.path} (${enc}, ${dest.snapshotCount} snapshots)${suffix}`,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
backup
|
|
443
|
+
.command("list")
|
|
444
|
+
.description("List all backup snapshots, grouped by destination")
|
|
445
|
+
.addHelpText(
|
|
446
|
+
"after",
|
|
447
|
+
`
|
|
627
448
|
Prints a per-destination table of snapshots with timestamp, size, and
|
|
628
449
|
encryption flag. Local destination is listed first, followed by each offsite
|
|
629
450
|
destination. Unreachable destinations are listed with an empty snapshot set.
|
|
630
451
|
|
|
631
452
|
Examples:
|
|
632
|
-
$
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
453
|
+
$ assistant backup list`,
|
|
454
|
+
)
|
|
455
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
456
|
+
const r = await cliIpcCall<{
|
|
457
|
+
local: Array<{
|
|
458
|
+
filename: string;
|
|
459
|
+
createdAt: string;
|
|
460
|
+
sizeBytes: number;
|
|
461
|
+
encrypted: boolean;
|
|
462
|
+
}>;
|
|
463
|
+
offsite: Array<{
|
|
464
|
+
destination: { path: string; encrypt: boolean };
|
|
465
|
+
snapshots: Array<{
|
|
466
|
+
filename: string;
|
|
467
|
+
createdAt: string;
|
|
468
|
+
sizeBytes: number;
|
|
469
|
+
encrypted: boolean;
|
|
470
|
+
}>;
|
|
471
|
+
reachable: boolean;
|
|
472
|
+
}>;
|
|
473
|
+
offsiteEnabled: boolean;
|
|
474
|
+
nextRunAt: string | null;
|
|
475
|
+
}>("backups_list");
|
|
476
|
+
if (!r.ok)
|
|
477
|
+
return exitFromIpcResult(
|
|
478
|
+
{ ok: false, error: r.error, statusCode: r.statusCode },
|
|
479
|
+
cmd,
|
|
480
|
+
);
|
|
481
|
+
const data = r.result!;
|
|
482
|
+
|
|
483
|
+
printSnapshotGroup(`Local:`, data.local);
|
|
484
|
+
|
|
485
|
+
if (!data.offsiteEnabled) return;
|
|
486
|
+
for (const dest of data.offsite) {
|
|
487
|
+
const tag = dest.destination.encrypt ? "encrypted" : "plaintext";
|
|
488
|
+
log.info("");
|
|
489
|
+
printSnapshotGroup(
|
|
490
|
+
`Offsite: ${dest.destination.path} (${tag})`,
|
|
491
|
+
dest.snapshots,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
}
|
|
637
498
|
|
|
499
|
+
// ---------------------------------------------------------------------------
|
|
500
|
+
// Snapshot table printer
|
|
501
|
+
// ---------------------------------------------------------------------------
|
|
502
|
+
|
|
503
|
+
function printSnapshotGroup(
|
|
504
|
+
heading: string,
|
|
505
|
+
entries: Array<{
|
|
506
|
+
filename: string;
|
|
507
|
+
createdAt: string;
|
|
508
|
+
sizeBytes: number;
|
|
509
|
+
encrypted: boolean;
|
|
510
|
+
}>,
|
|
511
|
+
): void {
|
|
512
|
+
log.info(heading);
|
|
513
|
+
if (entries.length === 0) {
|
|
514
|
+
log.info(" (none)");
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const tsW = 19;
|
|
518
|
+
const sizeW = 10;
|
|
519
|
+
const encW = 9;
|
|
520
|
+
log.info(
|
|
521
|
+
" " +
|
|
522
|
+
"Timestamp".padEnd(tsW) +
|
|
523
|
+
" " +
|
|
524
|
+
"Size".padEnd(sizeW) +
|
|
525
|
+
" " +
|
|
526
|
+
"Encrypted".padEnd(encW) +
|
|
527
|
+
" " +
|
|
528
|
+
"Filename",
|
|
529
|
+
);
|
|
530
|
+
for (const e of entries) {
|
|
531
|
+
log.info(
|
|
532
|
+
" " +
|
|
533
|
+
formatDate(new Date(e.createdAt)).padEnd(tsW) +
|
|
534
|
+
" " +
|
|
535
|
+
formatBytes(e.sizeBytes).padEnd(sizeW) +
|
|
536
|
+
" " +
|
|
537
|
+
(e.encrypted ? "yes" : "no").padEnd(encW) +
|
|
538
|
+
" " +
|
|
539
|
+
e.filename,
|
|
540
|
+
);
|
|
541
|
+
}
|
|
638
542
|
}
|