@mycadet/assistant 0.12.11 → 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 +2 -2
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- 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 +2 -2
- 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/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__/tts-routes.test.ts +2 -6
- 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,485 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Buffer-vs-streaming `.vbundle` import parity suite.
|
|
3
|
-
*
|
|
4
|
-
* Pins the existing disk-outcome equivalence between `commitImport`
|
|
5
|
-
* (buffer-based) and `streamCommitImport` (streaming) as a regression net
|
|
6
|
-
* BEFORE any production code is migrated to a shared policy module.
|
|
7
|
-
*
|
|
8
|
-
* Each test builds one archive, mkdtemps two sibling workspaces, seeds them
|
|
9
|
-
* identically, runs each importer against its own workspace, and asserts the
|
|
10
|
-
* post-import disk trees are byte-for-byte identical:
|
|
11
|
-
*
|
|
12
|
-
* expect(walkDiskTree(streamWs)).toEqual(walkDiskTree(bufferWs))
|
|
13
|
-
*
|
|
14
|
-
* Per-case invariants (carry-forward markers survive, persona lands at the
|
|
15
|
-
* right disk path, traversal entries do not erase the workspace, etc.) are
|
|
16
|
-
* asserted on top of disk-tree equality.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { createHash } from "node:crypto";
|
|
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 { dirname, join, relative } from "node:path";
|
|
32
|
-
import { Readable } from "node:stream";
|
|
33
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
34
|
-
|
|
35
|
-
import { buildVBundle } from "../vbundle-builder.js";
|
|
36
|
-
import {
|
|
37
|
-
analyzeImport,
|
|
38
|
-
DefaultPathResolver,
|
|
39
|
-
} from "../vbundle-import-analyzer.js";
|
|
40
|
-
import { commitImport } from "../vbundle-importer.js";
|
|
41
|
-
import { streamCommitImport } from "../vbundle-streaming-importer.js";
|
|
42
|
-
import { defaultV1Options } from "./v1-test-helpers.js";
|
|
43
|
-
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Shared helpers
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Allocate a fresh workspace path under a temp parent dir we own. The parent
|
|
50
|
-
* is realpath-resolved so macOS `/var` → `/private/var` symlink mismatches
|
|
51
|
-
* don't trip the streaming importer's `rebaseOntoTempWorkspace` containment
|
|
52
|
-
* check (which compares `resolve(diskPath)` against `resolve(workspaceDir)`).
|
|
53
|
-
*
|
|
54
|
-
* Returns `<parent>/workspace`. The directory itself is NOT created — tests
|
|
55
|
-
* that need it pre-existing call `mkdirSync` themselves.
|
|
56
|
-
*/
|
|
57
|
-
function freshWorkspace(): string {
|
|
58
|
-
const parent = realpathSync(
|
|
59
|
-
mkdtempSync(join(tmpdir(), "vbundle-import-parity-")),
|
|
60
|
-
);
|
|
61
|
-
return join(parent, "workspace");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Best-effort cleanup of a workspace's parent dir. */
|
|
65
|
-
function cleanupWorkspaceParent(workspaceDir: string): void {
|
|
66
|
-
try {
|
|
67
|
-
rmSync(join(workspaceDir, ".."), { recursive: true, force: true });
|
|
68
|
-
} catch {
|
|
69
|
-
// best-effort
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function sha256Hex(data: Uint8Array): string {
|
|
74
|
-
return createHash("sha256").update(data).digest("hex");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Recursively walk `root` and return a `Map<relPath, sha256Hex>` for every
|
|
79
|
-
* regular file. Skips dot-prefixed scratch dirs the streaming importer may
|
|
80
|
-
* leave behind on failure paths (`.import-*`, `.pre-import-*`) plus the
|
|
81
|
-
* import marker file — the buffer importer never produces these, so they'd
|
|
82
|
-
* spuriously break parity if included.
|
|
83
|
-
*
|
|
84
|
-
* Two importers are parity-equivalent for a given input iff the maps they
|
|
85
|
-
* produce on identically-seeded sibling workspaces are equal.
|
|
86
|
-
*/
|
|
87
|
-
function walkDiskTree(root: string): Map<string, string> {
|
|
88
|
-
const out = new Map<string, string>();
|
|
89
|
-
if (!existsSync(root)) {
|
|
90
|
-
return out;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const stack: string[] = [root];
|
|
94
|
-
while (stack.length > 0) {
|
|
95
|
-
const dir = stack.pop()!;
|
|
96
|
-
let entries;
|
|
97
|
-
try {
|
|
98
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
99
|
-
} catch {
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
for (const entry of entries) {
|
|
103
|
-
// Skip streaming-importer scratch artifacts so they don't show up as
|
|
104
|
-
// false negatives in the parity comparison.
|
|
105
|
-
if (
|
|
106
|
-
entry.name.startsWith(".import-") ||
|
|
107
|
-
entry.name.startsWith(".pre-import-") ||
|
|
108
|
-
entry.name === ".import-marker.json"
|
|
109
|
-
) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const abs = join(dir, entry.name);
|
|
113
|
-
if (entry.isDirectory()) {
|
|
114
|
-
stack.push(abs);
|
|
115
|
-
} else if (entry.isFile()) {
|
|
116
|
-
const rel = relative(root, abs);
|
|
117
|
-
out.set(rel, sha256Hex(readFileSync(abs)));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return out;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
interface SeedFile {
|
|
125
|
-
relPath: string;
|
|
126
|
-
content: string | Uint8Array;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Mkdir parents and write each file to `workspaceDir`. */
|
|
130
|
-
function seedLiveWorkspace(workspaceDir: string, files: SeedFile[]): void {
|
|
131
|
-
mkdirSync(workspaceDir, { recursive: true });
|
|
132
|
-
for (const { relPath, content } of files) {
|
|
133
|
-
const abs = join(workspaceDir, relPath);
|
|
134
|
-
mkdirSync(dirname(abs), { recursive: true });
|
|
135
|
-
writeFileSync(abs, content);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function runBufferImport(workspaceDir: string, archive: Uint8Array): void {
|
|
140
|
-
const result = commitImport({
|
|
141
|
-
archiveData: archive,
|
|
142
|
-
pathResolver: new DefaultPathResolver(workspaceDir),
|
|
143
|
-
workspaceDir,
|
|
144
|
-
});
|
|
145
|
-
if (!result.ok) {
|
|
146
|
-
throw new Error(
|
|
147
|
-
`buffer commitImport unexpectedly failed: ${JSON.stringify(result)}`,
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async function runStreamImport(
|
|
153
|
-
workspaceDir: string,
|
|
154
|
-
archive: Uint8Array,
|
|
155
|
-
importCredentials?: (
|
|
156
|
-
credentials: Array<{ account: string; value: string }>,
|
|
157
|
-
) => Promise<void>,
|
|
158
|
-
): Promise<void> {
|
|
159
|
-
const result = await streamCommitImport({
|
|
160
|
-
source: Readable.from([Buffer.from(archive)]),
|
|
161
|
-
pathResolver: new DefaultPathResolver(workspaceDir),
|
|
162
|
-
workspaceDir,
|
|
163
|
-
importCredentials,
|
|
164
|
-
});
|
|
165
|
-
if (!result.ok) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
`streamCommitImport unexpectedly failed: ${JSON.stringify(result)}`,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
// Parity tests
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
|
|
176
|
-
describe("vbundle import parity (buffer vs streaming)", () => {
|
|
177
|
-
let bufferWs: string;
|
|
178
|
-
let streamWs: string;
|
|
179
|
-
|
|
180
|
-
beforeEach(() => {
|
|
181
|
-
bufferWs = freshWorkspace();
|
|
182
|
-
streamWs = freshWorkspace();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
afterEach(() => {
|
|
186
|
-
cleanupWorkspaceParent(bufferWs);
|
|
187
|
-
cleanupWorkspaceParent(streamWs);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
test("A — full workspace + credentials: identical disk outcome", async () => {
|
|
191
|
-
const dbBytes = new Uint8Array(16);
|
|
192
|
-
for (let i = 0; i < dbBytes.length; i++) {
|
|
193
|
-
dbBytes[i] = (i * 17) & 0xff;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const configJson = JSON.stringify({ version: 1 });
|
|
197
|
-
const metadataJson = JSON.stringify({
|
|
198
|
-
version: 5,
|
|
199
|
-
credentials: [
|
|
200
|
-
{
|
|
201
|
-
credentialId: "id-openai-api_key",
|
|
202
|
-
service: "openai",
|
|
203
|
-
field: "api_key",
|
|
204
|
-
allowedTools: [],
|
|
205
|
-
allowedDomains: [],
|
|
206
|
-
createdAt: 1700000000000,
|
|
207
|
-
updatedAt: 1700000000000,
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const openaiKey = new Uint8Array(16);
|
|
213
|
-
for (let i = 0; i < openaiKey.length; i++) {
|
|
214
|
-
openaiKey[i] = (i + 5) & 0xff;
|
|
215
|
-
}
|
|
216
|
-
const anthropicKey = new TextEncoder().encode("sk-ant-test");
|
|
217
|
-
|
|
218
|
-
const { archive } = buildVBundle({
|
|
219
|
-
files: [
|
|
220
|
-
{ path: "workspace/data/db/assistant.db", data: dbBytes },
|
|
221
|
-
{
|
|
222
|
-
path: "workspace/config.json",
|
|
223
|
-
data: new TextEncoder().encode(configJson),
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
path: "workspace/data/credentials/metadata.json",
|
|
227
|
-
data: new TextEncoder().encode(metadataJson),
|
|
228
|
-
},
|
|
229
|
-
{ path: "credentials/openai-key", data: openaiKey },
|
|
230
|
-
{ path: "credentials/anthropic-key", data: anthropicKey },
|
|
231
|
-
],
|
|
232
|
-
...defaultV1Options(),
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// Pre-create both workspaces (streaming importer expects to operate
|
|
236
|
-
// against an existing dir and the atomic-swap path requires it).
|
|
237
|
-
mkdirSync(bufferWs, { recursive: true });
|
|
238
|
-
mkdirSync(streamWs, { recursive: true });
|
|
239
|
-
|
|
240
|
-
runBufferImport(bufferWs, archive);
|
|
241
|
-
await runStreamImport(streamWs, archive, async () => {
|
|
242
|
-
// parity test: credentials are intercepted but not persisted
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const bufferMap = walkDiskTree(bufferWs);
|
|
246
|
-
const streamMap = walkDiskTree(streamWs);
|
|
247
|
-
|
|
248
|
-
expect(streamMap).toEqual(bufferMap);
|
|
249
|
-
|
|
250
|
-
// Sanity: the three workspace-bound files we expect both importers to
|
|
251
|
-
// land are present in the parity map.
|
|
252
|
-
expect(bufferMap.has("data/db/assistant.db")).toBe(true);
|
|
253
|
-
expect(bufferMap.has("config.json")).toBe(true);
|
|
254
|
-
expect(bufferMap.has("data/credentials/metadata.json")).toBe(true);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test("B — config-only partial bundle: live preserved paths survive on both sides", async () => {
|
|
258
|
-
const seeds: SeedFile[] = [
|
|
259
|
-
{ relPath: "data/db/marker", content: "db-marker" },
|
|
260
|
-
{ relPath: "data/qdrant/marker", content: "qdrant-marker" },
|
|
261
|
-
{ relPath: "embedding-models/marker", content: "embedding-marker" },
|
|
262
|
-
{ relPath: "deprecated/marker", content: "deprecated-marker" },
|
|
263
|
-
];
|
|
264
|
-
|
|
265
|
-
seedLiveWorkspace(bufferWs, seeds);
|
|
266
|
-
seedLiveWorkspace(streamWs, seeds);
|
|
267
|
-
|
|
268
|
-
const configJson = JSON.stringify({ version: 1 });
|
|
269
|
-
const { archive } = buildVBundle({
|
|
270
|
-
files: [
|
|
271
|
-
// Validator requires the DB entry (legacy or workspace-prefixed).
|
|
272
|
-
// It's not the focus of this case — config.json is — but it must
|
|
273
|
-
// be present for the bundle to validate.
|
|
274
|
-
{ path: "data/db/assistant.db", data: new Uint8Array() },
|
|
275
|
-
{
|
|
276
|
-
path: "workspace/config.json",
|
|
277
|
-
data: new TextEncoder().encode(configJson),
|
|
278
|
-
},
|
|
279
|
-
],
|
|
280
|
-
...defaultV1Options(),
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
runBufferImport(bufferWs, archive);
|
|
284
|
-
await runStreamImport(streamWs, archive);
|
|
285
|
-
|
|
286
|
-
const bufferMap = walkDiskTree(bufferWs);
|
|
287
|
-
const streamMap = walkDiskTree(streamWs);
|
|
288
|
-
|
|
289
|
-
expect(streamMap).toEqual(bufferMap);
|
|
290
|
-
|
|
291
|
-
// Each carry-forward marker must still be on disk.
|
|
292
|
-
for (const seed of seeds) {
|
|
293
|
-
expect(bufferMap.has(seed.relPath)).toBe(true);
|
|
294
|
-
expect(streamMap.has(seed.relPath)).toBe(true);
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test("C — legacy prompts/USER.md: both importers land it at users/<slug>.md", async () => {
|
|
299
|
-
const personaBody = `_ Lines starting with _ are comments - they won't appear in the system prompt
|
|
300
|
-
|
|
301
|
-
# User Profile
|
|
302
|
-
|
|
303
|
-
- Preferred name/reference: Captain Parity
|
|
304
|
-
- Pronouns: they/them
|
|
305
|
-
- Locale: en-US
|
|
306
|
-
- Work role: Quartermaster
|
|
307
|
-
- Goals: Verify importer parity
|
|
308
|
-
- Hobbies/fun: Diff'ing trees
|
|
309
|
-
- Daily tools: Terminal
|
|
310
|
-
`;
|
|
311
|
-
|
|
312
|
-
// Pre-create users/ in both workspaces so the resolver's containment
|
|
313
|
-
// check has a real on-disk parent to anchor against.
|
|
314
|
-
mkdirSync(join(bufferWs, "users"), { recursive: true });
|
|
315
|
-
mkdirSync(join(streamWs, "users"), { recursive: true });
|
|
316
|
-
|
|
317
|
-
const { archive } = buildVBundle({
|
|
318
|
-
files: [
|
|
319
|
-
{ path: "data/db/assistant.db", data: new Uint8Array() },
|
|
320
|
-
{
|
|
321
|
-
path: "prompts/USER.md",
|
|
322
|
-
data: new TextEncoder().encode(personaBody),
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
...defaultV1Options(),
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
const bufferGuardianPath = join(bufferWs, "users", "captain.md");
|
|
329
|
-
const streamGuardianPath = join(streamWs, "users", "captain.md");
|
|
330
|
-
|
|
331
|
-
const bufferResolver = new DefaultPathResolver(
|
|
332
|
-
bufferWs,
|
|
333
|
-
undefined,
|
|
334
|
-
() => bufferGuardianPath,
|
|
335
|
-
);
|
|
336
|
-
const streamResolver = new DefaultPathResolver(
|
|
337
|
-
streamWs,
|
|
338
|
-
undefined,
|
|
339
|
-
() => streamGuardianPath,
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
const bufferResult = commitImport({
|
|
343
|
-
archiveData: archive,
|
|
344
|
-
pathResolver: bufferResolver,
|
|
345
|
-
workspaceDir: bufferWs,
|
|
346
|
-
});
|
|
347
|
-
expect(bufferResult.ok).toBe(true);
|
|
348
|
-
|
|
349
|
-
const streamResult = await streamCommitImport({
|
|
350
|
-
source: Readable.from([Buffer.from(archive)]),
|
|
351
|
-
pathResolver: streamResolver,
|
|
352
|
-
workspaceDir: streamWs,
|
|
353
|
-
});
|
|
354
|
-
expect(streamResult.ok).toBe(true);
|
|
355
|
-
|
|
356
|
-
const bufferMap = walkDiskTree(bufferWs);
|
|
357
|
-
const streamMap = walkDiskTree(streamWs);
|
|
358
|
-
|
|
359
|
-
expect(streamMap).toEqual(bufferMap);
|
|
360
|
-
|
|
361
|
-
expect(existsSync(bufferGuardianPath)).toBe(true);
|
|
362
|
-
expect(existsSync(streamGuardianPath)).toBe(true);
|
|
363
|
-
expect(readFileSync(bufferGuardianPath, "utf-8")).toBe(personaBody);
|
|
364
|
-
expect(readFileSync(streamGuardianPath, "utf-8")).toBe(personaBody);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test("D — no workspace entries at all: legacy bundle leaves seeded files in place", async () => {
|
|
368
|
-
const seeds: SeedFile[] = [
|
|
369
|
-
{ relPath: "unrelated.txt", content: "stay" },
|
|
370
|
-
{ relPath: "custom-dir/note.md", content: "# note" },
|
|
371
|
-
];
|
|
372
|
-
|
|
373
|
-
seedLiveWorkspace(bufferWs, seeds);
|
|
374
|
-
seedLiveWorkspace(streamWs, seeds);
|
|
375
|
-
|
|
376
|
-
const dbBytes = new TextEncoder().encode("legacy-db-payload");
|
|
377
|
-
const { archive } = buildVBundle({
|
|
378
|
-
files: [{ path: "data/db/assistant.db", data: dbBytes }],
|
|
379
|
-
...defaultV1Options(),
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
runBufferImport(bufferWs, archive);
|
|
383
|
-
await runStreamImport(streamWs, archive);
|
|
384
|
-
|
|
385
|
-
const bufferMap = walkDiskTree(bufferWs);
|
|
386
|
-
const streamMap = walkDiskTree(streamWs);
|
|
387
|
-
|
|
388
|
-
expect(streamMap).toEqual(bufferMap);
|
|
389
|
-
|
|
390
|
-
for (const seed of seeds) {
|
|
391
|
-
expect(bufferMap.has(seed.relPath)).toBe(true);
|
|
392
|
-
expect(streamMap.has(seed.relPath)).toBe(true);
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
test("E — path-traversal workspace entry: both importers refuse to clear", async () => {
|
|
397
|
-
seedLiveWorkspace(bufferWs, [{ relPath: "marker.txt", content: "keep" }]);
|
|
398
|
-
seedLiveWorkspace(streamWs, [{ relPath: "marker.txt", content: "keep" }]);
|
|
399
|
-
|
|
400
|
-
const { archive } = buildVBundle({
|
|
401
|
-
files: [
|
|
402
|
-
{ path: "data/db/assistant.db", data: new Uint8Array() },
|
|
403
|
-
{
|
|
404
|
-
path: "workspace/../../etc/passwd",
|
|
405
|
-
data: new TextEncoder().encode("nope"),
|
|
406
|
-
},
|
|
407
|
-
],
|
|
408
|
-
...defaultV1Options(),
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
runBufferImport(bufferWs, archive);
|
|
412
|
-
await runStreamImport(streamWs, archive);
|
|
413
|
-
|
|
414
|
-
const bufferMap = walkDiskTree(bufferWs);
|
|
415
|
-
const streamMap = walkDiskTree(streamWs);
|
|
416
|
-
|
|
417
|
-
expect(streamMap).toEqual(bufferMap);
|
|
418
|
-
|
|
419
|
-
expect(bufferMap.has("marker.txt")).toBe(true);
|
|
420
|
-
expect(streamMap.has("marker.txt")).toBe(true);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
test("F — debug-profile gateway entry: both importers drain it and write nothing", async () => {
|
|
424
|
-
const configJson = JSON.stringify({ version: 1 });
|
|
425
|
-
const { archive, manifest } = buildVBundle({
|
|
426
|
-
files: [
|
|
427
|
-
{ path: "workspace/data/db/assistant.db", data: new Uint8Array(8) },
|
|
428
|
-
{
|
|
429
|
-
path: "workspace/config.json",
|
|
430
|
-
data: new TextEncoder().encode(configJson),
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
path: "gateway/export.tar.gz",
|
|
434
|
-
data: new TextEncoder().encode("not-a-real-tarball"),
|
|
435
|
-
},
|
|
436
|
-
],
|
|
437
|
-
...defaultV1Options(),
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
// Preflight must not block on the gateway entry, or the CLI would
|
|
441
|
-
// refuse the restore before either importer runs.
|
|
442
|
-
const preflight = analyzeImport({
|
|
443
|
-
manifest,
|
|
444
|
-
pathResolver: new DefaultPathResolver(bufferWs),
|
|
445
|
-
});
|
|
446
|
-
expect(preflight.can_import).toBe(true);
|
|
447
|
-
expect(preflight.conflicts).toEqual([]);
|
|
448
|
-
|
|
449
|
-
mkdirSync(bufferWs, { recursive: true });
|
|
450
|
-
mkdirSync(streamWs, { recursive: true });
|
|
451
|
-
|
|
452
|
-
const bufferResult = commitImport({
|
|
453
|
-
archiveData: archive,
|
|
454
|
-
pathResolver: new DefaultPathResolver(bufferWs),
|
|
455
|
-
workspaceDir: bufferWs,
|
|
456
|
-
});
|
|
457
|
-
const streamResult = await streamCommitImport({
|
|
458
|
-
source: Readable.from([Buffer.from(archive)]),
|
|
459
|
-
pathResolver: new DefaultPathResolver(streamWs),
|
|
460
|
-
workspaceDir: streamWs,
|
|
461
|
-
});
|
|
462
|
-
if (!bufferResult.ok || !streamResult.ok) {
|
|
463
|
-
throw new Error("import unexpectedly failed");
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
const bufferMap = walkDiskTree(bufferWs);
|
|
467
|
-
const streamMap = walkDiskTree(streamWs);
|
|
468
|
-
|
|
469
|
-
expect(streamMap).toEqual(bufferMap);
|
|
470
|
-
expect(bufferMap.has("config.json")).toBe(true);
|
|
471
|
-
expect(bufferMap.has("data/db/assistant.db")).toBe(true);
|
|
472
|
-
for (const relPath of bufferMap.keys()) {
|
|
473
|
-
expect(relPath.startsWith("gateway")).toBe(false);
|
|
474
|
-
}
|
|
475
|
-
// Skipped silently, like a retired path: counted, but no
|
|
476
|
-
// "no known disk target" warning.
|
|
477
|
-
for (const { report } of [bufferResult, streamResult]) {
|
|
478
|
-
expect(report.warnings).toEqual([]);
|
|
479
|
-
const gateway = report.files.find(
|
|
480
|
-
(f) => f.path === "gateway/export.tar.gz",
|
|
481
|
-
);
|
|
482
|
-
expect(gateway?.action).toBe("skipped");
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
});
|