@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
|
@@ -8,9 +8,12 @@
|
|
|
8
8
|
* behavior or existing feature flags.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { describe, expect, test } from "bun:test";
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
_setOverridesForTesting,
|
|
15
|
+
isAssistantFeatureFlagEnabled,
|
|
16
|
+
} from "../config/assistant-feature-flags.js";
|
|
14
17
|
import type { AssistantConfig } from "../config/schema.js";
|
|
15
18
|
import {
|
|
16
19
|
CES_GRANT_AUDIT_FLAG_KEY,
|
|
@@ -25,15 +28,21 @@ import {
|
|
|
25
28
|
isCesToolsEnabled,
|
|
26
29
|
} from "../credential-execution/feature-gates.js";
|
|
27
30
|
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
_setOverridesForTesting({});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
_setOverridesForTesting({});
|
|
37
|
+
});
|
|
38
|
+
|
|
28
39
|
// ---------------------------------------------------------------------------
|
|
29
40
|
// Helpers
|
|
30
41
|
// ---------------------------------------------------------------------------
|
|
31
42
|
|
|
32
|
-
/** Create a minimal AssistantConfig
|
|
33
|
-
function makeConfig(
|
|
34
|
-
return {
|
|
35
|
-
...(flagOverrides ? { assistantFeatureFlagValues: flagOverrides } : {}),
|
|
36
|
-
} as AssistantConfig;
|
|
43
|
+
/** Create a minimal AssistantConfig (flag overrides are now set via _setOverridesForTesting). */
|
|
44
|
+
function makeConfig(): AssistantConfig {
|
|
45
|
+
return {} as AssistantConfig;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
/** All CES flag keys for iteration. */
|
|
@@ -87,16 +96,16 @@ describe("CES flag key format", () => {
|
|
|
87
96
|
// ---------------------------------------------------------------------------
|
|
88
97
|
|
|
89
98
|
describe("CES flags default safely (all disabled)", () => {
|
|
90
|
-
const config = makeConfig();
|
|
91
|
-
|
|
92
99
|
for (const { name, fn } of ALL_CES_PREDICATES) {
|
|
93
100
|
test(`${name} returns false with no config overrides`, () => {
|
|
101
|
+
const config = makeConfig();
|
|
94
102
|
expect(fn(config)).toBe(false);
|
|
95
103
|
});
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
for (const key of ALL_CES_FLAG_KEYS) {
|
|
99
107
|
test(`isAssistantFeatureFlagEnabled('${key}') returns false with no overrides`, () => {
|
|
108
|
+
const config = makeConfig();
|
|
100
109
|
expect(isAssistantFeatureFlagEnabled(key, config)).toBe(false);
|
|
101
110
|
});
|
|
102
111
|
}
|
|
@@ -109,12 +118,14 @@ describe("CES flags default safely (all disabled)", () => {
|
|
|
109
118
|
describe("CES flags can be enabled independently", () => {
|
|
110
119
|
for (const { name, fn, key } of ALL_CES_PREDICATES) {
|
|
111
120
|
test(`enabling ${key} makes ${name} return true`, () => {
|
|
112
|
-
|
|
121
|
+
_setOverridesForTesting({ [key]: true });
|
|
122
|
+
const config = makeConfig();
|
|
113
123
|
expect(fn(config)).toBe(true);
|
|
114
124
|
});
|
|
115
125
|
|
|
116
126
|
test(`enabling ${key} does not enable other CES flags`, () => {
|
|
117
|
-
|
|
127
|
+
_setOverridesForTesting({ [key]: true });
|
|
128
|
+
const config = makeConfig();
|
|
118
129
|
for (const { fn: otherFn, key: otherKey } of ALL_CES_PREDICATES) {
|
|
119
130
|
if (otherKey === key) continue;
|
|
120
131
|
expect(otherFn(config)).toBe(false);
|
|
@@ -130,7 +141,8 @@ describe("CES flags can be enabled independently", () => {
|
|
|
130
141
|
describe("CES flags respect explicit false overrides", () => {
|
|
131
142
|
for (const { name, fn, key } of ALL_CES_PREDICATES) {
|
|
132
143
|
test(`${name} returns false when explicitly set to false`, () => {
|
|
133
|
-
|
|
144
|
+
_setOverridesForTesting({ [key]: false });
|
|
145
|
+
const config = makeConfig();
|
|
134
146
|
expect(fn(config)).toBe(false);
|
|
135
147
|
});
|
|
136
148
|
}
|
|
@@ -146,7 +158,8 @@ describe("CES flags do not affect unrelated flags", () => {
|
|
|
146
158
|
for (const key of ALL_CES_FLAG_KEYS) {
|
|
147
159
|
overrides[key] = true;
|
|
148
160
|
}
|
|
149
|
-
|
|
161
|
+
_setOverridesForTesting(overrides);
|
|
162
|
+
const config = makeConfig();
|
|
150
163
|
|
|
151
164
|
// browser defaults to true in the registry and should stay true
|
|
152
165
|
expect(
|
|
@@ -159,7 +172,8 @@ describe("CES flags do not affect unrelated flags", () => {
|
|
|
159
172
|
for (const key of ALL_CES_FLAG_KEYS) {
|
|
160
173
|
overrides[key] = true;
|
|
161
174
|
}
|
|
162
|
-
|
|
175
|
+
_setOverridesForTesting(overrides);
|
|
176
|
+
const config = makeConfig();
|
|
163
177
|
|
|
164
178
|
// contacts defaults to true in the registry and should stay true
|
|
165
179
|
expect(
|
|
@@ -26,7 +26,17 @@
|
|
|
26
26
|
* contracts — no real CES process or socket dependencies are needed.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import { describe, expect, test } from "bun:test";
|
|
29
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
30
|
+
|
|
31
|
+
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
_setOverridesForTesting({});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
_setOverridesForTesting({});
|
|
39
|
+
});
|
|
30
40
|
|
|
31
41
|
import {
|
|
32
42
|
CES_PROTOCOL_VERSION,
|
|
@@ -58,11 +68,9 @@ import {
|
|
|
58
68
|
// Helpers
|
|
59
69
|
// ---------------------------------------------------------------------------
|
|
60
70
|
|
|
61
|
-
/** Create a minimal AssistantConfig
|
|
62
|
-
function makeConfig(
|
|
63
|
-
return {
|
|
64
|
-
...(flagOverrides ? { assistantFeatureFlagValues: flagOverrides } : {}),
|
|
65
|
-
} as AssistantConfig;
|
|
71
|
+
/** Create a minimal AssistantConfig (flag overrides are now set via _setOverridesForTesting). */
|
|
72
|
+
function makeConfig(): AssistantConfig {
|
|
73
|
+
return {} as AssistantConfig;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
// ---------------------------------------------------------------------------
|
|
@@ -444,32 +452,36 @@ describe("feature-flag rollback safety", () => {
|
|
|
444
452
|
});
|
|
445
453
|
|
|
446
454
|
test("managed sidecar flag can be explicitly enabled", () => {
|
|
447
|
-
|
|
455
|
+
_setOverridesForTesting({
|
|
448
456
|
"feature_flags.ces-managed-sidecar.enabled": true,
|
|
449
457
|
});
|
|
458
|
+
const config = makeConfig();
|
|
450
459
|
expect(isCesManagedSidecarEnabled(config)).toBe(true);
|
|
451
460
|
});
|
|
452
461
|
|
|
453
462
|
test("managed sidecar flag can be explicitly disabled", () => {
|
|
454
|
-
|
|
463
|
+
_setOverridesForTesting({
|
|
455
464
|
"feature_flags.ces-managed-sidecar.enabled": false,
|
|
456
465
|
});
|
|
466
|
+
const config = makeConfig();
|
|
457
467
|
expect(isCesManagedSidecarEnabled(config)).toBe(false);
|
|
458
468
|
});
|
|
459
469
|
|
|
460
470
|
test("enabling managed sidecar does not enable CES tools", () => {
|
|
461
|
-
|
|
471
|
+
_setOverridesForTesting({
|
|
462
472
|
"feature_flags.ces-managed-sidecar.enabled": true,
|
|
463
473
|
});
|
|
474
|
+
const config = makeConfig();
|
|
464
475
|
// CES tools flag should remain independently controlled
|
|
465
476
|
expect(isCesToolsEnabled(config)).toBe(false);
|
|
466
477
|
});
|
|
467
478
|
|
|
468
479
|
test("disabling managed sidecar does not affect other CES flags", () => {
|
|
469
|
-
|
|
480
|
+
_setOverridesForTesting({
|
|
470
481
|
"feature_flags.ces-managed-sidecar.enabled": false,
|
|
471
482
|
"feature_flags.ces-tools.enabled": true,
|
|
472
483
|
});
|
|
484
|
+
const config = makeConfig();
|
|
473
485
|
expect(isCesToolsEnabled(config)).toBe(true);
|
|
474
486
|
});
|
|
475
487
|
});
|
|
@@ -480,10 +492,11 @@ describe("feature-flag rollback safety", () => {
|
|
|
480
492
|
|
|
481
493
|
describe("process manager config wiring", () => {
|
|
482
494
|
test("CesProcessManagerConfig accepts assistantConfig for flag gating", () => {
|
|
495
|
+
_setOverridesForTesting({
|
|
496
|
+
"feature_flags.ces-managed-sidecar.enabled": true,
|
|
497
|
+
});
|
|
483
498
|
const config: CesProcessManagerConfig = {
|
|
484
|
-
assistantConfig: makeConfig(
|
|
485
|
-
"feature_flags.ces-managed-sidecar.enabled": true,
|
|
486
|
-
}),
|
|
499
|
+
assistantConfig: makeConfig(),
|
|
487
500
|
};
|
|
488
501
|
expect(config.assistantConfig).toBeDefined();
|
|
489
502
|
expect(isCesManagedSidecarEnabled(config.assistantConfig!)).toBe(true);
|
|
@@ -491,16 +504,17 @@ describe("process manager config wiring", () => {
|
|
|
491
504
|
|
|
492
505
|
test("CesProcessManagerConfig requires assistantConfig for feature-flag gate", () => {
|
|
493
506
|
const config: CesProcessManagerConfig = {
|
|
494
|
-
assistantConfig: makeConfig(
|
|
507
|
+
assistantConfig: makeConfig(),
|
|
495
508
|
};
|
|
496
509
|
expect(config.assistantConfig).toBeDefined();
|
|
497
510
|
});
|
|
498
511
|
|
|
499
512
|
test("when flag is off, managed discovery is skipped", () => {
|
|
513
|
+
_setOverridesForTesting({
|
|
514
|
+
"feature_flags.ces-managed-sidecar.enabled": false,
|
|
515
|
+
});
|
|
500
516
|
const config: CesProcessManagerConfig = {
|
|
501
|
-
assistantConfig: makeConfig(
|
|
502
|
-
"feature_flags.ces-managed-sidecar.enabled": false,
|
|
503
|
-
}),
|
|
517
|
+
assistantConfig: makeConfig(),
|
|
504
518
|
};
|
|
505
519
|
// The managed path should be gated
|
|
506
520
|
expect(isCesManagedSidecarEnabled(config.assistantConfig!)).toBe(false);
|
|
@@ -513,9 +527,10 @@ describe("process manager config wiring", () => {
|
|
|
513
527
|
|
|
514
528
|
describe("non-CES internal consumers intact when flag is off", () => {
|
|
515
529
|
test("existing non-agent flows are unaffected by managed sidecar flag", () => {
|
|
516
|
-
|
|
530
|
+
_setOverridesForTesting({
|
|
517
531
|
"feature_flags.ces-managed-sidecar.enabled": false,
|
|
518
532
|
});
|
|
533
|
+
const config = makeConfig();
|
|
519
534
|
expect(isCesManagedSidecarEnabled(config)).toBe(false);
|
|
520
535
|
});
|
|
521
536
|
|
|
@@ -10,7 +10,6 @@ const mockConfig = {
|
|
|
10
10
|
action: "block" as "redact" | "warn" | "block",
|
|
11
11
|
entropyThreshold: 4.0,
|
|
12
12
|
allowOneTimeSend: false,
|
|
13
|
-
blockIngress: true,
|
|
14
13
|
},
|
|
15
14
|
timeouts: { permissionTimeoutSec: 300 },
|
|
16
15
|
};
|
|
@@ -118,8 +117,6 @@ mock.module("../tools/credentials/policy-validate.js", () => ({
|
|
|
118
117
|
|
|
119
118
|
// Import modules under test
|
|
120
119
|
const { credentialStoreTool } = await import("../tools/credentials/vault.js");
|
|
121
|
-
const { checkIngressForSecrets } =
|
|
122
|
-
await import("../security/secret-ingress.js");
|
|
123
120
|
const { isToolAllowed } = await import("../tools/credentials/tool-policy.js");
|
|
124
121
|
const { isDomainAllowed } =
|
|
125
122
|
await import("../tools/credentials/domain-policy.js");
|
|
@@ -203,61 +200,6 @@ describe("E2E: secure store and list lifecycle", () => {
|
|
|
203
200
|
});
|
|
204
201
|
});
|
|
205
202
|
|
|
206
|
-
// ---------------------------------------------------------------------------
|
|
207
|
-
// E2E Scenario 2 — Secret-in-chat blocked and redirected
|
|
208
|
-
// ---------------------------------------------------------------------------
|
|
209
|
-
|
|
210
|
-
describe("E2E: secret ingress blocking", () => {
|
|
211
|
-
beforeEach(() => {
|
|
212
|
-
mockConfig.secretDetection.enabled = true;
|
|
213
|
-
mockConfig.secretDetection.action = "block";
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test("blocks message containing AWS access key", () => {
|
|
217
|
-
const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
218
|
-
const check = checkIngressForSecrets(`Here is my key: ${awsKey}`);
|
|
219
|
-
expect(check.blocked).toBe(true);
|
|
220
|
-
expect(check.detectedTypes.length).toBeGreaterThan(0);
|
|
221
|
-
// Notice must never contain the actual secret
|
|
222
|
-
expect(check.userNotice).toBeDefined();
|
|
223
|
-
expect(check.userNotice).not.toContain(awsKey);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
test("blocks message containing GitHub token", () => {
|
|
227
|
-
const ghToken = ["ghp_", "ABCDEFghijklMN01234567", "89abcdef"].join("");
|
|
228
|
-
const check = checkIngressForSecrets(`Use this token: ${ghToken}`);
|
|
229
|
-
expect(check.blocked).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test("allows normal text through", () => {
|
|
233
|
-
const check = checkIngressForSecrets("Please help me configure my project");
|
|
234
|
-
expect(check.blocked).toBe(false);
|
|
235
|
-
expect(check.detectedTypes).toEqual([]);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
test("bypasses when detection is disabled", () => {
|
|
239
|
-
mockConfig.secretDetection.enabled = false;
|
|
240
|
-
const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
241
|
-
const check = checkIngressForSecrets(awsKey);
|
|
242
|
-
expect(check.blocked).toBe(false);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("bypasses when blockIngress is false", () => {
|
|
246
|
-
mockConfig.secretDetection.blockIngress = false;
|
|
247
|
-
const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
248
|
-
const check = checkIngressForSecrets(awsKey);
|
|
249
|
-
expect(check.blocked).toBe(false);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
test("still blocks when action is warn but blockIngress is true", () => {
|
|
253
|
-
mockConfig.secretDetection.action = "warn";
|
|
254
|
-
mockConfig.secretDetection.blockIngress = true;
|
|
255
|
-
const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
256
|
-
const check = checkIngressForSecrets(awsKey);
|
|
257
|
-
expect(check.blocked).toBe(true);
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
203
|
// ---------------------------------------------------------------------------
|
|
262
204
|
// E2E Scenario 3 — One-time send override path
|
|
263
205
|
// ---------------------------------------------------------------------------
|
|
@@ -411,12 +353,4 @@ describe("E2E: cross-cutting secret leak prevention", () => {
|
|
|
411
353
|
);
|
|
412
354
|
expect(result.content).not.toContain(secret);
|
|
413
355
|
});
|
|
414
|
-
|
|
415
|
-
test("ingress notice never contains the detected secret", () => {
|
|
416
|
-
const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
417
|
-
const check = checkIngressForSecrets(awsKey);
|
|
418
|
-
expect(check.blocked).toBe(true);
|
|
419
|
-
expect(check.userNotice).toBeDefined();
|
|
420
|
-
expect(check.userNotice).not.toContain(awsKey);
|
|
421
|
-
});
|
|
422
356
|
});
|
|
@@ -138,45 +138,6 @@ describe("Invariant 1: secrets never enter LLM context", () => {
|
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
|
|
142
|
-
// PR 27 — secret ingress block scans inbound messages
|
|
143
|
-
test("user message containing secret is blocked from entering history", () => {
|
|
144
|
-
// Mock config to enable block mode
|
|
145
|
-
mock.module("../config/loader.js", () => ({
|
|
146
|
-
applyNestedDefaults: (config: unknown) => config,
|
|
147
|
-
getConfig: () => ({
|
|
148
|
-
ui: {},
|
|
149
|
-
secretDetection: {
|
|
150
|
-
enabled: true,
|
|
151
|
-
action: "block",
|
|
152
|
-
blockIngress: true,
|
|
153
|
-
},
|
|
154
|
-
}),
|
|
155
|
-
invalidateConfigCache: () => {},
|
|
156
|
-
loadConfig: () => ({
|
|
157
|
-
ui: {},
|
|
158
|
-
secretDetection: {
|
|
159
|
-
enabled: true,
|
|
160
|
-
action: "block",
|
|
161
|
-
blockIngress: true,
|
|
162
|
-
},
|
|
163
|
-
}),
|
|
164
|
-
}));
|
|
165
|
-
|
|
166
|
-
// Re-import to pick up the mock
|
|
167
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
168
|
-
const { checkIngressForSecrets } = require("../security/secret-ingress.js");
|
|
169
|
-
|
|
170
|
-
// Build a fake AWS key at runtime to avoid pre-commit hook
|
|
171
|
-
const fakeKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
|
|
172
|
-
const result = checkIngressForSecrets(`My key is ${fakeKey}`);
|
|
173
|
-
|
|
174
|
-
expect(result.blocked).toBe(true);
|
|
175
|
-
expect(result.detectedTypes.length).toBeGreaterThan(0);
|
|
176
|
-
// User notice must not echo the secret
|
|
177
|
-
expect(result.userNotice).toBeDefined();
|
|
178
|
-
expect(result.userNotice).not.toContain(fakeKey);
|
|
179
|
-
});
|
|
180
141
|
});
|
|
181
142
|
|
|
182
143
|
// ---------------------------------------------------------------------------
|
|
@@ -208,6 +169,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
208
169
|
"tools/credentials/broker.ts", // brokered credential access
|
|
209
170
|
"tools/network/web-search.ts", // web search API key lookup
|
|
210
171
|
"daemon/handlers/config-telegram.ts", // Telegram bot token management
|
|
172
|
+
"daemon/handlers/config-vercel.ts", // Vercel API token management
|
|
211
173
|
"runtime/routes/integrations/twilio.ts", // Twilio credential management (HTTP control-plane)
|
|
212
174
|
"security/token-manager.ts", // OAuth token refresh flow
|
|
213
175
|
"email/providers/index.ts", // email provider API key lookup
|
|
@@ -216,6 +178,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
216
178
|
"calls/twilio-config.ts", // call infrastructure credential lookup
|
|
217
179
|
"calls/twilio-provider.ts", // call infrastructure credential lookup
|
|
218
180
|
"calls/twilio-rest.ts", // Twilio REST API credential lookup
|
|
181
|
+
"calls/fish-audio-client.ts", // Fish Audio TTS API key lookup
|
|
219
182
|
"runtime/channel-invite-transports/telegram.ts", // Telegram invite transport bot token lookup
|
|
220
183
|
"cli/commands/keys.ts", // CLI credential management commands
|
|
221
184
|
"cli/commands/credentials.ts", // CLI credential management commands
|
|
@@ -225,6 +188,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
225
188
|
"messaging/providers/slack/adapter.ts", // Slack bot token lookup for Socket Mode connectivity check
|
|
226
189
|
"daemon/handlers/config-slack-channel.ts", // Slack channel config credential management
|
|
227
190
|
"providers/managed-proxy/context.ts", // managed proxy API key lookup for provider initialization
|
|
191
|
+
"platform/client.ts", // platform client keychain fallback for standalone CLI auth
|
|
228
192
|
"mcp/mcp-oauth-provider.ts", // MCP OAuth token/client/discovery persistence
|
|
229
193
|
"runtime/routes/integrations/slack/share.ts", // Slack share routes credential lookup
|
|
230
194
|
"mcp/client.ts", // MCP client cached-token lookup
|
|
@@ -237,7 +201,6 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
237
201
|
"cli/commands/oauth/connections.ts", // CLI OAuth connection delete (legacy credential cleanup)
|
|
238
202
|
"oauth/manual-token-connection.ts", // manual-token provider backfill (keychain credential existence check)
|
|
239
203
|
"cli/commands/doctor.ts", // CLI diagnostic API key verification via secure storage
|
|
240
|
-
"swarm/backend-claude-code.ts", // Claude Code swarm backend API key lookup
|
|
241
204
|
"workspace/provider-commit-message-generator.ts", // commit message generation provider key lookup
|
|
242
205
|
"config/bundled-skills/transcribe/tools/transcribe-media.ts", // transcription tool API key lookup
|
|
243
206
|
"config/bundled-skills/image-studio/tools/media-generate-image.ts", // image generation tool API key lookup
|
|
@@ -249,13 +212,13 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
249
212
|
"media/avatar-router.ts", // avatar generation API key lookup
|
|
250
213
|
"memory/embedding-backend.ts", // embedding backend API key lookup
|
|
251
214
|
"daemon/providers-setup.ts", // provider initialization API key lookup
|
|
252
|
-
"tools/claude-code/claude-code.ts", // Claude Code tool API key lookup
|
|
253
215
|
"workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
|
|
254
216
|
"cli/commands/avatar.ts", // CLI avatar generation API key lookup
|
|
255
217
|
"config/bundled-skills/slack/tools/shared.ts", // Slack skill bot token lookup
|
|
256
218
|
"daemon/conversation-process.ts", // masked provider key display
|
|
257
219
|
"daemon/handlers/config-model.ts", // masked provider key display
|
|
258
220
|
"providers/speech-to-text/resolve.ts", // STT provider API key lookup
|
|
221
|
+
"daemon/lifecycle.ts", // CES client injection into secure-keys at startup
|
|
259
222
|
]);
|
|
260
223
|
|
|
261
224
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -613,10 +576,6 @@ describe("One-time send override", () => {
|
|
|
613
576
|
test("default secretDetection.action is redact", () => {
|
|
614
577
|
expect(DEFAULT_CONFIG.secretDetection.action).toBe("redact");
|
|
615
578
|
});
|
|
616
|
-
|
|
617
|
-
test("default secretDetection.blockIngress is true", () => {
|
|
618
|
-
expect(DEFAULT_CONFIG.secretDetection.blockIngress).toBe(true);
|
|
619
|
-
});
|
|
620
579
|
});
|
|
621
580
|
|
|
622
581
|
// ---------------------------------------------------------------------------
|
|
@@ -12,6 +12,7 @@ import type { CredentialMetadata } from "../tools/credentials/metadata-store.js"
|
|
|
12
12
|
let secureKeyStore = new Map<string, string>();
|
|
13
13
|
let metadataStore: CredentialMetadata[] = [];
|
|
14
14
|
let idCounter = 0;
|
|
15
|
+
let mockBrokerUnreachable = false;
|
|
15
16
|
|
|
16
17
|
function nextUUID(): string {
|
|
17
18
|
idCounter += 1;
|
|
@@ -45,6 +46,12 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
45
46
|
getSecureKeyAsync: async (account: string): Promise<string | undefined> => {
|
|
46
47
|
return secureKeyStore.get(account);
|
|
47
48
|
},
|
|
49
|
+
getSecureKeyResultAsync: async (
|
|
50
|
+
account: string,
|
|
51
|
+
): Promise<{ value: string | undefined; unreachable: boolean }> => ({
|
|
52
|
+
value: secureKeyStore.get(account),
|
|
53
|
+
unreachable: mockBrokerUnreachable,
|
|
54
|
+
}),
|
|
48
55
|
_resetBackend: (): void => {},
|
|
49
56
|
}));
|
|
50
57
|
|
|
@@ -264,6 +271,7 @@ describe("assistant credentials CLI", () => {
|
|
|
264
271
|
secureKeyStore = new Map();
|
|
265
272
|
metadataStore = [];
|
|
266
273
|
idCounter = 0;
|
|
274
|
+
mockBrokerUnreachable = false;
|
|
267
275
|
disconnectOAuthProviderCalls = [];
|
|
268
276
|
disconnectOAuthProviderResult = "not-found";
|
|
269
277
|
process.exitCode = 0;
|
|
@@ -870,6 +878,42 @@ describe("assistant credentials CLI", () => {
|
|
|
870
878
|
expect(parsed.hasSecret).toBe(false);
|
|
871
879
|
expect(parsed.scrubbedValue).toBe("(not set)");
|
|
872
880
|
});
|
|
881
|
+
|
|
882
|
+
test("shows broker unreachable when metadata exists but broker is down", async () => {
|
|
883
|
+
seedMetadataOnly("twilio", "account_sid");
|
|
884
|
+
mockBrokerUnreachable = true;
|
|
885
|
+
|
|
886
|
+
const result = await runCli([
|
|
887
|
+
"inspect",
|
|
888
|
+
"--service",
|
|
889
|
+
"twilio",
|
|
890
|
+
"--field",
|
|
891
|
+
"account_sid",
|
|
892
|
+
"--json",
|
|
893
|
+
]);
|
|
894
|
+
expect(result.exitCode).toBe(0);
|
|
895
|
+
const parsed = JSON.parse(result.stdout);
|
|
896
|
+
expect(parsed.ok).toBe(true);
|
|
897
|
+
expect(parsed.scrubbedValue).toBe("(broker unreachable)");
|
|
898
|
+
expect(parsed.brokerUnreachable).toBe(true);
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
test("shows unreachable error when no metadata and broker is down", async () => {
|
|
902
|
+
mockBrokerUnreachable = true;
|
|
903
|
+
|
|
904
|
+
const result = await runCli([
|
|
905
|
+
"inspect",
|
|
906
|
+
"--service",
|
|
907
|
+
"nonexistent",
|
|
908
|
+
"--field",
|
|
909
|
+
"field",
|
|
910
|
+
"--json",
|
|
911
|
+
]);
|
|
912
|
+
expect(result.exitCode).toBe(1);
|
|
913
|
+
const parsed = JSON.parse(result.stdout);
|
|
914
|
+
expect(parsed.ok).toBe(false);
|
|
915
|
+
expect(parsed.error).toContain("Keychain broker is unreachable");
|
|
916
|
+
});
|
|
873
917
|
});
|
|
874
918
|
|
|
875
919
|
// =========================================================================
|
|
@@ -969,6 +1013,40 @@ describe("assistant credentials CLI", () => {
|
|
|
969
1013
|
expect(parsed.ok).toBe(false);
|
|
970
1014
|
expect(parsed.error).toContain("not found");
|
|
971
1015
|
});
|
|
1016
|
+
|
|
1017
|
+
test("returns unreachable error when broker is down", async () => {
|
|
1018
|
+
mockBrokerUnreachable = true;
|
|
1019
|
+
|
|
1020
|
+
const result = await runCli([
|
|
1021
|
+
"reveal",
|
|
1022
|
+
"--service",
|
|
1023
|
+
"twilio",
|
|
1024
|
+
"--field",
|
|
1025
|
+
"auth_token",
|
|
1026
|
+
"--json",
|
|
1027
|
+
]);
|
|
1028
|
+
expect(result.exitCode).toBe(1);
|
|
1029
|
+
const parsed = JSON.parse(result.stdout);
|
|
1030
|
+
expect(parsed.ok).toBe(false);
|
|
1031
|
+
expect(parsed.error).toContain("Keychain broker is unreachable");
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
test("returns credential-not-found when broker is up", async () => {
|
|
1035
|
+
mockBrokerUnreachable = false;
|
|
1036
|
+
|
|
1037
|
+
const result = await runCli([
|
|
1038
|
+
"reveal",
|
|
1039
|
+
"--service",
|
|
1040
|
+
"twilio",
|
|
1041
|
+
"--field",
|
|
1042
|
+
"nonexistent",
|
|
1043
|
+
"--json",
|
|
1044
|
+
]);
|
|
1045
|
+
expect(result.exitCode).toBe(1);
|
|
1046
|
+
const parsed = JSON.parse(result.stdout);
|
|
1047
|
+
expect(parsed.ok).toBe(false);
|
|
1048
|
+
expect(parsed.error).toBe("Credential not found");
|
|
1049
|
+
});
|
|
972
1050
|
});
|
|
973
1051
|
|
|
974
1052
|
// =========================================================================
|