@mycadet/assistant 0.12.10 → 0.13.0
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/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/ces-client/src/http-credentials.ts +18 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/openapi.yaml +2 -708
- package/package.json +2 -3
- package/scripts/sync-web-search-catalog.ts +1 -2
- package/src/__tests__/cloud-profile-guard.test.ts +41 -5
- package/src/__tests__/config-schema.test.ts +6 -38
- package/src/__tests__/conversation-queue.test.ts +45 -6
- package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
- package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +0 -51
- package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
- package/src/__tests__/run-due-schedules.test.ts +0 -4
- package/src/__tests__/schedule-store.test.ts +0 -12
- package/src/__tests__/stt-stream-session.test.ts +4 -4
- package/src/__tests__/telephony-tts-guard.test.ts +2 -50
- package/src/__tests__/voice-config-update.test.ts +12 -167
- package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
- package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
- package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
- package/src/calls/resolve-call-tts-provider.ts +6 -25
- package/src/calls/telephony-credential-preflight.ts +0 -10
- package/src/calls/telephony-tts-capability.ts +1 -17
- package/src/cli/commands/tts.ts +1 -2
- package/src/cli/index.help.ts +0 -2
- package/src/cli/program.ts +0 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
- package/src/config/schemas/__tests__/stt.test.ts +10 -14
- package/src/config/schemas/llm.ts +3 -5
- package/src/config/schemas/services.ts +5 -6
- package/src/config/schemas/stt.ts +1 -3
- package/src/config/schemas/tts.ts +1 -30
- package/src/daemon/daemon-readiness.ts +4 -29
- package/src/daemon/lifecycle.ts +0 -31
- package/src/daemon/providers-setup.ts +0 -8
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -66
- package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
- package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
- package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
- package/src/live-voice/live-voice-credential-preflight.ts +9 -33
- package/src/live-voice/live-voice-session.ts +10 -6
- package/src/live-voice/live-voice-tts.ts +1 -8
- package/src/plugins/defaults/memory/worker.ts +3 -3
- package/src/prompts/persona-resolver.ts +0 -35
- package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
- package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
- package/src/providers/search-provider-catalog.ts +7 -39
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
- package/src/providers/speech-to-text/provider-catalog.ts +2 -73
- package/src/providers/speech-to-text/resolve.ts +3 -120
- package/src/runtime/http-router-types.ts +1 -3
- package/src/runtime/http-server.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +13 -11
- package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
- package/src/runtime/routes/conversation-query-routes.ts +0 -96
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/live-voice-routes.test.ts +4 -11
- package/src/runtime/routes/live-voice-routes.ts +8 -11
- package/src/runtime/routes/secret-routes.ts +0 -6
- package/src/runtime/routes/tts-routes.ts +0 -38
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
- package/src/schedule/schedule-store.ts +0 -7
- package/src/schedule/scheduler.ts +0 -5
- package/src/schedule/worker.ts +1 -1
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
- package/src/stt/daemon-batch-transcriber.ts +4 -38
- package/src/stt/types.ts +1 -9
- package/src/tools/network/__tests__/web-search.test.ts +3 -250
- package/src/tools/network/web-search.ts +4 -165
- package/src/tts/__tests__/provider-resolution.test.ts +0 -5
- package/src/tts/provider-catalog.ts +0 -4
- package/src/tts/types.ts +0 -1
- package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
- package/src/__tests__/migration-export-http.test.ts +0 -557
- package/src/__tests__/migration-export-streaming.test.ts +0 -350
- package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
- package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
- package/src/__tests__/migration-import-from-path.test.ts +0 -324
- package/src/__tests__/migration-import-from-url.test.ts +0 -588
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
- package/src/__tests__/migration-jobs-status.test.ts +0 -139
- package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
- package/src/__tests__/migration-transport.test.ts +0 -1089
- package/src/__tests__/migration-validate-http.test.ts +0 -634
- package/src/__tests__/migration-wizard.test.ts +0 -1395
- package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
- package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
- package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
- package/src/__tests__/validation-results-screen.test.ts +0 -1130
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
- package/src/background-wake/background-wake-routes.test.ts +0 -906
- package/src/background-wake/cloud-client.test.ts +0 -328
- package/src/background-wake/cloud-client.ts +0 -196
- package/src/background-wake/next-wake.test.ts +0 -335
- package/src/background-wake/next-wake.ts +0 -188
- package/src/background-wake/publisher.ts +0 -93
- package/src/background-wake/wake-intent-hooks.test.ts +0 -234
- package/src/cli/commands/__tests__/backup.test.ts +0 -363
- package/src/cli/commands/backup.help.ts +0 -180
- package/src/cli/commands/backup.ts +0 -398
- package/src/cloud/cloud-speech.test.ts +0 -335
- package/src/cloud/cloud-speech.ts +0 -300
- package/src/config/cloud-speech-defaults.ts +0 -300
- package/src/config/sanitize-for-transfer.ts +0 -111
- package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
- package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
- package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
- package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
- package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
- package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
- package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
- package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
- package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
- package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
- package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
- package/src/runtime/migrations/gcs-signed-url.ts +0 -190
- package/src/runtime/migrations/job-registry.ts +0 -297
- package/src/runtime/migrations/migration-transport.ts +0 -612
- package/src/runtime/migrations/migration-wizard.ts +0 -747
- package/src/runtime/migrations/origin-mode.ts +0 -42
- package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
- package/src/runtime/migrations/staged-import-path.ts +0 -116
- package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
- package/src/runtime/migrations/validation-results-screen.ts +0 -469
- package/src/runtime/migrations/vbundle-builder.ts +0 -1382
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
- package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
- package/src/runtime/migrations/vbundle-importer.ts +0 -826
- package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
- package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
- package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
- package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
- package/src/runtime/migrations/vbundle-validator.ts +0 -724
- package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
- package/src/runtime/routes/background-wake-routes.ts +0 -387
- package/src/runtime/routes/migration-routes.ts +0 -2584
- package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
- package/src/tools/network/cloud-search-proxy.ts +0 -211
- package/src/tts/__tests__/cadet-provider.test.ts +0 -253
- package/src/tts/providers/cadet-provider.ts +0 -244
- package/src/types/tar-stream.d.ts +0 -66
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the pure policy module shared by both vbundle importers.
|
|
3
|
-
*
|
|
4
|
-
* No `node:fs`, no temp dirs — every test exercises a constant or a
|
|
5
|
-
* predicate over strings.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, expect, test } from "bun:test";
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
compareSemver,
|
|
12
|
-
CONFIG_ARCHIVE_PATHS,
|
|
13
|
-
CREDENTIAL_METADATA_ARCHIVE_PATH,
|
|
14
|
-
evaluateRuntimeCompatibility,
|
|
15
|
-
isConfigArchivePath,
|
|
16
|
-
isCredentialMetadataArchivePath,
|
|
17
|
-
isLegacyPersonaArchivePath,
|
|
18
|
-
isLocalDevRuntimeVersion,
|
|
19
|
-
isWorkspaceNamespacedArchivePath,
|
|
20
|
-
LEGACY_USER_MD_ARCHIVE_PATH,
|
|
21
|
-
partitionWorkspacePreserveSkipDirs,
|
|
22
|
-
WORKSPACE_PRESERVE_PATHS,
|
|
23
|
-
} from "../vbundle-import-policy.js";
|
|
24
|
-
|
|
25
|
-
describe("LEGACY_USER_MD_ARCHIVE_PATH", () => {
|
|
26
|
-
test("equals the legacy guardian persona archive path", () => {
|
|
27
|
-
expect(LEGACY_USER_MD_ARCHIVE_PATH).toBe("prompts/USER.md");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("CONFIG_ARCHIVE_PATHS", () => {
|
|
32
|
-
test("contains exactly the two known config archive paths", () => {
|
|
33
|
-
expect(CONFIG_ARCHIVE_PATHS.size).toBe(2);
|
|
34
|
-
expect(CONFIG_ARCHIVE_PATHS.has("workspace/config.json")).toBe(true);
|
|
35
|
-
expect(CONFIG_ARCHIVE_PATHS.has("config/settings.json")).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe("CREDENTIAL_METADATA_ARCHIVE_PATH", () => {
|
|
40
|
-
test("equals the workspace-namespaced credential metadata path", () => {
|
|
41
|
-
expect(CREDENTIAL_METADATA_ARCHIVE_PATH).toBe(
|
|
42
|
-
"workspace/data/credentials/metadata.json",
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe("WORKSPACE_PRESERVE_PATHS", () => {
|
|
48
|
-
test("matches the literal 4-element ordered list", () => {
|
|
49
|
-
expect(WORKSPACE_PRESERVE_PATHS).toEqual([
|
|
50
|
-
"embedding-models",
|
|
51
|
-
"deprecated",
|
|
52
|
-
"data/db",
|
|
53
|
-
"data/qdrant",
|
|
54
|
-
]);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe("isWorkspaceNamespacedArchivePath", () => {
|
|
59
|
-
test("true for paths under workspace/", () => {
|
|
60
|
-
expect(isWorkspaceNamespacedArchivePath("workspace/foo")).toBe(true);
|
|
61
|
-
expect(isWorkspaceNamespacedArchivePath("workspace/data/db/x")).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("false for non-workspace paths", () => {
|
|
65
|
-
expect(isWorkspaceNamespacedArchivePath("prompts/USER.md")).toBe(false);
|
|
66
|
-
expect(isWorkspaceNamespacedArchivePath("data/db/assistant.db")).toBe(
|
|
67
|
-
false,
|
|
68
|
-
);
|
|
69
|
-
expect(isWorkspaceNamespacedArchivePath("")).toBe(false);
|
|
70
|
-
expect(isWorkspaceNamespacedArchivePath("workspace")).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe("isLegacyPersonaArchivePath", () => {
|
|
75
|
-
test("true only for the exact legacy path", () => {
|
|
76
|
-
expect(isLegacyPersonaArchivePath("prompts/USER.md")).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("false for near-misses and unrelated paths", () => {
|
|
80
|
-
expect(isLegacyPersonaArchivePath("prompts/USER")).toBe(false);
|
|
81
|
-
expect(isLegacyPersonaArchivePath("workspace/prompts/USER.md")).toBe(false);
|
|
82
|
-
expect(isLegacyPersonaArchivePath("")).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe("isConfigArchivePath", () => {
|
|
87
|
-
test("true for both members of CONFIG_ARCHIVE_PATHS", () => {
|
|
88
|
-
expect(isConfigArchivePath("workspace/config.json")).toBe(true);
|
|
89
|
-
expect(isConfigArchivePath("config/settings.json")).toBe(true);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("false for non-members", () => {
|
|
93
|
-
expect(isConfigArchivePath("workspace/foo.json")).toBe(false);
|
|
94
|
-
expect(isConfigArchivePath("config/settings")).toBe(false);
|
|
95
|
-
expect(isConfigArchivePath("")).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe("isCredentialMetadataArchivePath", () => {
|
|
100
|
-
test("true for the exact constant", () => {
|
|
101
|
-
expect(
|
|
102
|
-
isCredentialMetadataArchivePath(
|
|
103
|
-
"workspace/data/credentials/metadata.json",
|
|
104
|
-
),
|
|
105
|
-
).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("false for the legacy non-prefixed form and empty string", () => {
|
|
109
|
-
expect(
|
|
110
|
-
isCredentialMetadataArchivePath("data/credentials/metadata.json"),
|
|
111
|
-
).toBe(false);
|
|
112
|
-
expect(isCredentialMetadataArchivePath("")).toBe(false);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe("partitionWorkspacePreserveSkipDirs", () => {
|
|
117
|
-
test("splits preserve-paths into top-level vs data-subdir skip sets", () => {
|
|
118
|
-
const { topLevelSkipDirs, dataSubdirSkipDirs } =
|
|
119
|
-
partitionWorkspacePreserveSkipDirs();
|
|
120
|
-
|
|
121
|
-
expect(topLevelSkipDirs.size).toBe(2);
|
|
122
|
-
expect(topLevelSkipDirs.has("embedding-models")).toBe(true);
|
|
123
|
-
expect(topLevelSkipDirs.has("deprecated")).toBe(true);
|
|
124
|
-
|
|
125
|
-
expect(dataSubdirSkipDirs.size).toBe(2);
|
|
126
|
-
expect(dataSubdirSkipDirs.has("db")).toBe(true);
|
|
127
|
-
expect(dataSubdirSkipDirs.has("qdrant")).toBe(true);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("compareSemver", () => {
|
|
132
|
-
test("0.10.0 > 0.9.0 (numeric, not lexical)", () => {
|
|
133
|
-
expect(compareSemver("0.10.0", "0.9.0")).toBe(1);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("equal triples return 0", () => {
|
|
137
|
-
expect(compareSemver("0.7.1", "0.7.1")).toBe(0);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("smaller patch returns -1", () => {
|
|
141
|
-
expect(compareSemver("0.7.0", "0.7.1")).toBe(-1);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test("prerelease tag is stripped (treated as base release)", () => {
|
|
145
|
-
expect(compareSemver("0.7.1-staging.1", "0.7.1")).toBe(0);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test("non-version string returns null", () => {
|
|
149
|
-
expect(compareSemver("not-a-version", "0.7.1")).toBe(null);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test("two-part version returns null", () => {
|
|
153
|
-
expect(compareSemver("1.2", "0.7.1")).toBe(null);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Regression: Number.parseInt accepts numeric prefixes and ignores
|
|
157
|
-
// trailing junk ("0foo" → 0), which previously coerced malformed
|
|
158
|
-
// triples through the gate. parseSemverTriple now requires each
|
|
159
|
-
// component to match /^\d+$/ exactly.
|
|
160
|
-
test("trailing junk on a component returns null (left arg)", () => {
|
|
161
|
-
expect(compareSemver("0.8.0foo", "0.8.0")).toBe(null);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("trailing junk on a component returns null (right arg)", () => {
|
|
165
|
-
expect(compareSemver("0.8.0", "0.7.1xyz")).toBe(null);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Leading zeros are accepted: "01.02.03" parses to the same numeric
|
|
169
|
-
// triple as "1.2.3" since Number("01") === 1. We pin this behavior
|
|
170
|
-
// so a future tightening doesn't accidentally regress callers that
|
|
171
|
-
// pass zero-padded versions from upstream tooling.
|
|
172
|
-
test("leading zeros parse equal to un-padded triple", () => {
|
|
173
|
-
expect(compareSemver("01.02.03", "1.2.3")).toBe(0);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("leading whitespace returns null", () => {
|
|
177
|
-
expect(compareSemver(" 0.8.0", "0.8.0")).toBe(null);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test("negative component returns null", () => {
|
|
181
|
-
expect(compareSemver("0.8.-1", "0.8.0")).toBe(null);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe("evaluateRuntimeCompatibility", () => {
|
|
186
|
-
test("legacy sentinel passes regardless of runtime version", () => {
|
|
187
|
-
expect(
|
|
188
|
-
evaluateRuntimeCompatibility(
|
|
189
|
-
{ min_runtime_version: "0.0.0-legacy", max_runtime_version: null },
|
|
190
|
-
"0.7.1",
|
|
191
|
-
),
|
|
192
|
-
).toEqual({ ok: true });
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("runtime above min with no max passes", () => {
|
|
196
|
-
expect(
|
|
197
|
-
evaluateRuntimeCompatibility(
|
|
198
|
-
{ min_runtime_version: "0.7.0", max_runtime_version: null },
|
|
199
|
-
"0.7.1",
|
|
200
|
-
),
|
|
201
|
-
).toEqual({ ok: true });
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test("runtime below min fails with full echo", () => {
|
|
205
|
-
const compat = {
|
|
206
|
-
min_runtime_version: "0.8.0",
|
|
207
|
-
max_runtime_version: null,
|
|
208
|
-
};
|
|
209
|
-
expect(evaluateRuntimeCompatibility(compat, "0.7.1")).toEqual({
|
|
210
|
-
ok: false,
|
|
211
|
-
reason: "version_incompatible",
|
|
212
|
-
bundle_compat: compat,
|
|
213
|
-
runtime_version: "0.7.1",
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test("runtime above max fails", () => {
|
|
218
|
-
const compat = {
|
|
219
|
-
min_runtime_version: "0.7.0",
|
|
220
|
-
max_runtime_version: "0.7.5",
|
|
221
|
-
};
|
|
222
|
-
expect(evaluateRuntimeCompatibility(compat, "0.8.0")).toEqual({
|
|
223
|
-
ok: false,
|
|
224
|
-
reason: "version_incompatible",
|
|
225
|
-
bundle_compat: compat,
|
|
226
|
-
runtime_version: "0.8.0",
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test("max is inclusive", () => {
|
|
231
|
-
expect(
|
|
232
|
-
evaluateRuntimeCompatibility(
|
|
233
|
-
{ min_runtime_version: "0.7.0", max_runtime_version: "0.7.5" },
|
|
234
|
-
"0.7.5",
|
|
235
|
-
),
|
|
236
|
-
).toEqual({ ok: true });
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
test("unparsable min skips the gate (fail-open)", () => {
|
|
240
|
-
expect(
|
|
241
|
-
evaluateRuntimeCompatibility(
|
|
242
|
-
{ min_runtime_version: "garbage", max_runtime_version: null },
|
|
243
|
-
"0.7.1",
|
|
244
|
-
),
|
|
245
|
-
).toEqual({ ok: true });
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// Regression for Codex feedback: a malformed min like "0.8.0foo"
|
|
249
|
-
// previously coerced to [0, 8, 0] via Number.parseInt and incorrectly
|
|
250
|
-
// produced a version_incompatible decision against runtime "0.7.1".
|
|
251
|
-
// With strict per-component digit matching, the parse fails and the
|
|
252
|
-
// gate fails open.
|
|
253
|
-
test("malformed min with trailing junk fails open, does not block", () => {
|
|
254
|
-
expect(
|
|
255
|
-
evaluateRuntimeCompatibility(
|
|
256
|
-
{ min_runtime_version: "0.8.0foo", max_runtime_version: null },
|
|
257
|
-
"0.7.1",
|
|
258
|
-
),
|
|
259
|
-
).toEqual({ ok: true });
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// A `vel up` minikube assistant image is stamped
|
|
263
|
-
// `<pkg.version>-local.<ts>.<sha>` from the checkout's package.json,
|
|
264
|
-
// which only moves on release cuts, so the minimum bound cannot be
|
|
265
|
-
// decided from it; the explicit maximum ceiling still applies.
|
|
266
|
-
test("local dev runtime build skips the minimum bound", () => {
|
|
267
|
-
expect(
|
|
268
|
-
evaluateRuntimeCompatibility(
|
|
269
|
-
{ min_runtime_version: "0.12.2-staging.4", max_runtime_version: null },
|
|
270
|
-
"0.12.1-local.20260916144827.2f409056e6",
|
|
271
|
-
),
|
|
272
|
-
).toEqual({ ok: true });
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
test("local dev runtime build within an explicit range passes", () => {
|
|
276
|
-
expect(
|
|
277
|
-
evaluateRuntimeCompatibility(
|
|
278
|
-
{ min_runtime_version: "0.12.2", max_runtime_version: "0.12.5" },
|
|
279
|
-
"0.12.1-local.20260916144827.2f409056e6",
|
|
280
|
-
),
|
|
281
|
-
).toEqual({ ok: true });
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
test("local dev runtime build above max still fails", () => {
|
|
285
|
-
const compat = {
|
|
286
|
-
min_runtime_version: "0.7.0",
|
|
287
|
-
max_runtime_version: "0.7.5",
|
|
288
|
-
};
|
|
289
|
-
const runtime = "0.12.1-local.20260916144827.2f409056e6";
|
|
290
|
-
expect(evaluateRuntimeCompatibility(compat, runtime)).toEqual({
|
|
291
|
-
ok: false,
|
|
292
|
-
reason: "version_incompatible",
|
|
293
|
-
bundle_compat: compat,
|
|
294
|
-
runtime_version: runtime,
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test("local dev runtime build with unparsable max fails open", () => {
|
|
299
|
-
expect(
|
|
300
|
-
evaluateRuntimeCompatibility(
|
|
301
|
-
{ min_runtime_version: "0.7.0", max_runtime_version: "garbage" },
|
|
302
|
-
"0.12.1-local.20260916144827.2f409056e6",
|
|
303
|
-
),
|
|
304
|
-
).toEqual({ ok: true });
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test("other prerelease builds are still gated", () => {
|
|
308
|
-
const compat = {
|
|
309
|
-
min_runtime_version: "0.12.2-staging.4",
|
|
310
|
-
max_runtime_version: null,
|
|
311
|
-
};
|
|
312
|
-
for (const runtime of [
|
|
313
|
-
"0.12.1-dev.202609151913.a151da3",
|
|
314
|
-
"0.12.1-staging.9",
|
|
315
|
-
"0.12.1",
|
|
316
|
-
]) {
|
|
317
|
-
expect(evaluateRuntimeCompatibility(compat, runtime)).toEqual({
|
|
318
|
-
ok: false,
|
|
319
|
-
reason: "version_incompatible",
|
|
320
|
-
bundle_compat: compat,
|
|
321
|
-
runtime_version: runtime,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
describe("isLocalDevRuntimeVersion", () => {
|
|
328
|
-
test("matches the local hatch / minikube version stamp", () => {
|
|
329
|
-
expect(
|
|
330
|
-
isLocalDevRuntimeVersion("0.12.1-local.20260916144827.2f409056e6"),
|
|
331
|
-
).toBe(true);
|
|
332
|
-
expect(isLocalDevRuntimeVersion("0.0.0-local.1789584535000")).toBe(true);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
test("rejects release, dev and staging builds and lookalikes", () => {
|
|
336
|
-
for (const version of [
|
|
337
|
-
"0.12.1",
|
|
338
|
-
"0.12.1-dev.202609151913.a151da3",
|
|
339
|
-
"0.12.2-staging.4",
|
|
340
|
-
"0.0.0-dev",
|
|
341
|
-
"0.0.0-legacy",
|
|
342
|
-
"0.12.1-localhost.1",
|
|
343
|
-
"local.0.12.1",
|
|
344
|
-
"",
|
|
345
|
-
]) {
|
|
346
|
-
expect(isLocalDevRuntimeVersion(version)).toBe(false);
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
});
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Buffer importer runtime-version compat gate.
|
|
3
|
-
*
|
|
4
|
-
* Wires `commitImport` to `policy.evaluateRuntimeCompatibility` BEFORE any
|
|
5
|
-
* state mutation. The cloud-side gate is the primary check; this catches
|
|
6
|
-
* legacy bundles whose ExportJob row predates PR #5470 (compat columns NULL
|
|
7
|
-
* → platform gate skipped) and any caller that bypasses the cloud-issued
|
|
8
|
-
* signed URL flow.
|
|
9
|
-
*
|
|
10
|
-
* Invariants pinned here:
|
|
11
|
-
* - A compatible bundle imports normally.
|
|
12
|
-
* - An incompatible bundle (min_runtime_version above APP_VERSION) returns
|
|
13
|
-
* `{ ok: false, reason: "version_incompatible", bundle_compat,
|
|
14
|
-
* runtime_version }` with NO disk mutation.
|
|
15
|
-
* - The legacy sentinel "0.0.0-legacy" passes the gate unconditionally.
|
|
16
|
-
* - Pre-existing workspace files are byte-identical and no `*.backup-*`
|
|
17
|
-
* files are created when the gate rejects.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
existsSync,
|
|
22
|
-
mkdirSync,
|
|
23
|
-
mkdtempSync,
|
|
24
|
-
readdirSync,
|
|
25
|
-
readFileSync,
|
|
26
|
-
realpathSync,
|
|
27
|
-
rmSync,
|
|
28
|
-
writeFileSync,
|
|
29
|
-
} from "node:fs";
|
|
30
|
-
import { tmpdir } from "node:os";
|
|
31
|
-
import { join, relative } from "node:path";
|
|
32
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
33
|
-
|
|
34
|
-
import { APP_VERSION } from "../../../version.js";
|
|
35
|
-
import { buildVBundle } from "../vbundle-builder.js";
|
|
36
|
-
import { DefaultPathResolver } from "../vbundle-import-analyzer.js";
|
|
37
|
-
import { commitImport } from "../vbundle-importer.js";
|
|
38
|
-
import { defaultV1Options } from "./v1-test-helpers.js";
|
|
39
|
-
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// Helpers
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
function freshWorkspace(): string {
|
|
45
|
-
const parent = realpathSync(
|
|
46
|
-
mkdtempSync(join(tmpdir(), "vbundle-import-version-compat-")),
|
|
47
|
-
);
|
|
48
|
-
return join(parent, "workspace");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function cleanupWorkspaceParent(workspaceDir: string): void {
|
|
52
|
-
try {
|
|
53
|
-
rmSync(join(workspaceDir, ".."), { recursive: true, force: true });
|
|
54
|
-
} catch {
|
|
55
|
-
// best-effort
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Recursively yields every regular file under `root` as a relative path. */
|
|
60
|
-
function* walkFiles(root: string): Generator<string> {
|
|
61
|
-
if (!existsSync(root)) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const stack: string[] = [root];
|
|
65
|
-
while (stack.length > 0) {
|
|
66
|
-
const dir = stack.pop()!;
|
|
67
|
-
let entries;
|
|
68
|
-
try {
|
|
69
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
70
|
-
} catch {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
for (const entry of entries) {
|
|
74
|
-
const abs = join(dir, entry.name);
|
|
75
|
-
if (entry.isDirectory()) {
|
|
76
|
-
stack.push(abs);
|
|
77
|
-
} else if (entry.isFile()) {
|
|
78
|
-
yield relative(root, abs);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function buildBundleWithMinRuntimeVersion(
|
|
85
|
-
minRuntimeVersion: string,
|
|
86
|
-
): Uint8Array {
|
|
87
|
-
const dbBytes = new TextEncoder().encode("db-payload");
|
|
88
|
-
const configJson = JSON.stringify({ version: 1 });
|
|
89
|
-
const { archive } = buildVBundle({
|
|
90
|
-
files: [
|
|
91
|
-
{ path: "workspace/data/db/assistant.db", data: dbBytes },
|
|
92
|
-
{
|
|
93
|
-
path: "workspace/config.json",
|
|
94
|
-
data: new TextEncoder().encode(configJson),
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
...defaultV1Options(),
|
|
98
|
-
compatibility: {
|
|
99
|
-
min_runtime_version: minRuntimeVersion,
|
|
100
|
-
max_runtime_version: null,
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
return archive;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// ---------------------------------------------------------------------------
|
|
107
|
-
// Tests
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
|
|
110
|
-
describe("commitImport runtime-version compat gate", () => {
|
|
111
|
-
let workspaceDir: string;
|
|
112
|
-
|
|
113
|
-
beforeEach(() => {
|
|
114
|
-
workspaceDir = freshWorkspace();
|
|
115
|
-
mkdirSync(workspaceDir, { recursive: true });
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
afterEach(() => {
|
|
119
|
-
cleanupWorkspaceParent(workspaceDir);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("compatible bundle imports normally", () => {
|
|
123
|
-
const archive = buildBundleWithMinRuntimeVersion("0.0.1");
|
|
124
|
-
|
|
125
|
-
const result = commitImport({
|
|
126
|
-
archiveData: archive,
|
|
127
|
-
pathResolver: new DefaultPathResolver(workspaceDir),
|
|
128
|
-
workspaceDir,
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
expect(result.ok).toBe(true);
|
|
132
|
-
expect(existsSync(join(workspaceDir, "data/db/assistant.db"))).toBe(true);
|
|
133
|
-
expect(existsSync(join(workspaceDir, "config.json"))).toBe(true);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("incompatible bundle (above APP_VERSION) returns version_incompatible without writing", () => {
|
|
137
|
-
const archive = buildBundleWithMinRuntimeVersion("99.0.0");
|
|
138
|
-
|
|
139
|
-
const result = commitImport({
|
|
140
|
-
archiveData: archive,
|
|
141
|
-
pathResolver: new DefaultPathResolver(workspaceDir),
|
|
142
|
-
workspaceDir,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
expect(result.ok).toBe(false);
|
|
146
|
-
if (!result.ok && result.reason === "version_incompatible") {
|
|
147
|
-
expect(result.bundle_compat.min_runtime_version).toBe("99.0.0");
|
|
148
|
-
expect(result.bundle_compat.max_runtime_version).toBeNull();
|
|
149
|
-
expect(result.runtime_version).toBe(APP_VERSION);
|
|
150
|
-
} else {
|
|
151
|
-
throw new Error(
|
|
152
|
-
`expected version_incompatible, got ${JSON.stringify(result)}`,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Fresh workspace stays empty — no bundle files, no backups.
|
|
157
|
-
expect([...walkFiles(workspaceDir)]).toEqual([]);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
test("legacy sentinel passes through", () => {
|
|
161
|
-
const archive = buildBundleWithMinRuntimeVersion("0.0.0-legacy");
|
|
162
|
-
|
|
163
|
-
const result = commitImport({
|
|
164
|
-
archiveData: archive,
|
|
165
|
-
pathResolver: new DefaultPathResolver(workspaceDir),
|
|
166
|
-
workspaceDir,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
expect(result.ok).toBe(true);
|
|
170
|
-
expect(existsSync(join(workspaceDir, "data/db/assistant.db"))).toBe(true);
|
|
171
|
-
expect(existsSync(join(workspaceDir, "config.json"))).toBe(true);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("pre-existing workspace files stay untouched on incompatibility", () => {
|
|
175
|
-
const sentinelPath = join(workspaceDir, "sentinel.txt");
|
|
176
|
-
const sentinelContent = "keep me";
|
|
177
|
-
writeFileSync(sentinelPath, sentinelContent);
|
|
178
|
-
|
|
179
|
-
const archive = buildBundleWithMinRuntimeVersion("99.0.0");
|
|
180
|
-
|
|
181
|
-
const result = commitImport({
|
|
182
|
-
archiveData: archive,
|
|
183
|
-
pathResolver: new DefaultPathResolver(workspaceDir),
|
|
184
|
-
workspaceDir,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
expect(result.ok).toBe(false);
|
|
188
|
-
if (!result.ok) {
|
|
189
|
-
expect(result.reason).toBe("version_incompatible");
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
expect(readFileSync(sentinelPath, "utf-8")).toBe(sentinelContent);
|
|
193
|
-
// Only the seeded sentinel remains — no bundle files, no `*.backup-*`.
|
|
194
|
-
expect([...walkFiles(workspaceDir)]).toEqual(["sentinel.txt"]);
|
|
195
|
-
});
|
|
196
|
-
});
|