@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,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for credential-aware rebind-secrets screen behavior.
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - All credentials imported successfully -> re-enter-secrets auto-completed
|
|
6
|
-
* - Partial import failure -> targeted list of failed credentials
|
|
7
|
-
* - Legacy bundles without credentials -> original rebind-secrets behavior
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { describe, expect, test } from "bun:test";
|
|
11
|
-
|
|
12
|
-
import type { MigrationWizardState } from "../migration-wizard.js";
|
|
13
|
-
import { createWizardState } from "../migration-wizard.js";
|
|
14
|
-
import {
|
|
15
|
-
createTaskCompletionState,
|
|
16
|
-
deriveRebindSecretsScreenState,
|
|
17
|
-
} from "../rebind-secrets-screen.js";
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Helpers
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Create a wizard state that has progressed to the rebind-secrets step.
|
|
25
|
-
*/
|
|
26
|
-
function wizardAtRebindSecrets(
|
|
27
|
-
credentialsImported?: MigrationWizardState["credentialsImported"],
|
|
28
|
-
): MigrationWizardState {
|
|
29
|
-
const base = createWizardState();
|
|
30
|
-
return {
|
|
31
|
-
...base,
|
|
32
|
-
currentStep: "rebind-secrets",
|
|
33
|
-
direction: "managed-to-self-hosted",
|
|
34
|
-
steps: {
|
|
35
|
-
...base.steps,
|
|
36
|
-
"select-direction": { status: "success" },
|
|
37
|
-
"upload-bundle": { status: "success" },
|
|
38
|
-
validate: { status: "success" },
|
|
39
|
-
"preflight-review": { status: "success" },
|
|
40
|
-
transfer: { status: "success" },
|
|
41
|
-
"rebind-secrets": { status: "idle" },
|
|
42
|
-
complete: { status: "idle" },
|
|
43
|
-
},
|
|
44
|
-
hasBundleData: true,
|
|
45
|
-
credentialsImported,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Tests
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
|
|
53
|
-
describe("rebind-secrets-screen credential awareness", () => {
|
|
54
|
-
test("all credentials imported successfully -> re-enter-secrets is auto-completed", () => {
|
|
55
|
-
const wizard = wizardAtRebindSecrets({
|
|
56
|
-
total: 3,
|
|
57
|
-
succeeded: 3,
|
|
58
|
-
failed: 0,
|
|
59
|
-
failedAccounts: [],
|
|
60
|
-
});
|
|
61
|
-
const completion = createTaskCompletionState();
|
|
62
|
-
const screen = deriveRebindSecretsScreenState(wizard, completion);
|
|
63
|
-
|
|
64
|
-
expect(screen.phase).toBe("active");
|
|
65
|
-
if (screen.phase !== "active") {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const secretsTask = screen.tasks.find((t) => t.id === "re-enter-secrets");
|
|
70
|
-
expect(secretsTask).toBeDefined();
|
|
71
|
-
expect(secretsTask!.status).toBe("complete");
|
|
72
|
-
expect(secretsTask!.title).toBe("API keys and secrets transferred");
|
|
73
|
-
expect(secretsTask!.description).toContain("3 credential(s)");
|
|
74
|
-
expect(secretsTask!.description).toContain("automatically imported");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("partial import failure -> shows only failed credentials", () => {
|
|
78
|
-
const wizard = wizardAtRebindSecrets({
|
|
79
|
-
total: 3,
|
|
80
|
-
succeeded: 2,
|
|
81
|
-
failed: 1,
|
|
82
|
-
failedAccounts: ["openai-key"],
|
|
83
|
-
});
|
|
84
|
-
const completion = createTaskCompletionState();
|
|
85
|
-
const screen = deriveRebindSecretsScreenState(wizard, completion);
|
|
86
|
-
|
|
87
|
-
expect(screen.phase).toBe("active");
|
|
88
|
-
if (screen.phase !== "active") {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const secretsTask = screen.tasks.find((t) => t.id === "re-enter-secrets");
|
|
93
|
-
expect(secretsTask).toBeDefined();
|
|
94
|
-
expect(secretsTask!.status).toBe("pending");
|
|
95
|
-
expect(secretsTask!.title).toBe("Re-enter failed credentials");
|
|
96
|
-
expect(secretsTask!.description).toContain("2 of 3");
|
|
97
|
-
expect(secretsTask!.description).toContain('"openai-key"');
|
|
98
|
-
expect(secretsTask!.description).toContain("manual re-entry");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test("legacy bundle without credentials -> original rebind-secrets behavior", () => {
|
|
102
|
-
const wizard = wizardAtRebindSecrets(undefined);
|
|
103
|
-
const completion = createTaskCompletionState();
|
|
104
|
-
const screen = deriveRebindSecretsScreenState(wizard, completion);
|
|
105
|
-
|
|
106
|
-
expect(screen.phase).toBe("active");
|
|
107
|
-
if (screen.phase !== "active") {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const secretsTask = screen.tasks.find((t) => t.id === "re-enter-secrets");
|
|
112
|
-
expect(secretsTask).toBeDefined();
|
|
113
|
-
expect(secretsTask!.status).toBe("pending");
|
|
114
|
-
expect(secretsTask!.title).toBe("Re-enter API keys and secrets");
|
|
115
|
-
expect(secretsTask!.description).toContain("redacted in export bundles");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("all credentials imported -> allRequiredComplete reflects auto-completion", () => {
|
|
119
|
-
const wizard = wizardAtRebindSecrets({
|
|
120
|
-
total: 2,
|
|
121
|
-
succeeded: 2,
|
|
122
|
-
failed: 0,
|
|
123
|
-
failedAccounts: [],
|
|
124
|
-
});
|
|
125
|
-
// Mark all other required tasks as complete
|
|
126
|
-
let completion = createTaskCompletionState();
|
|
127
|
-
completion = {
|
|
128
|
-
...completion,
|
|
129
|
-
"rebind-channels": true,
|
|
130
|
-
"reconfigure-auth": true,
|
|
131
|
-
};
|
|
132
|
-
const screen = deriveRebindSecretsScreenState(wizard, completion);
|
|
133
|
-
|
|
134
|
-
expect(screen.phase).toBe("active");
|
|
135
|
-
if (screen.phase !== "active") {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// re-enter-secrets is auto-completed, rebind-channels and reconfigure-auth are manually done
|
|
140
|
-
expect(screen.allRequiredComplete).toBe(true);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("partial failure -> allRequiredComplete is false when failed creds not manually acknowledged", () => {
|
|
144
|
-
const wizard = wizardAtRebindSecrets({
|
|
145
|
-
total: 3,
|
|
146
|
-
succeeded: 2,
|
|
147
|
-
failed: 1,
|
|
148
|
-
failedAccounts: ["anthropic-key"],
|
|
149
|
-
});
|
|
150
|
-
const completion = createTaskCompletionState();
|
|
151
|
-
const screen = deriveRebindSecretsScreenState(wizard, completion);
|
|
152
|
-
|
|
153
|
-
expect(screen.phase).toBe("active");
|
|
154
|
-
if (screen.phase !== "active") {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// re-enter-secrets is still pending (partial failure), so not all required complete
|
|
159
|
-
expect(screen.allRequiredComplete).toBe(false);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test("multiple failed credentials are listed in description", () => {
|
|
163
|
-
const wizard = wizardAtRebindSecrets({
|
|
164
|
-
total: 4,
|
|
165
|
-
succeeded: 1,
|
|
166
|
-
failed: 3,
|
|
167
|
-
failedAccounts: ["openai-key", "anthropic-key", "github-token"],
|
|
168
|
-
});
|
|
169
|
-
const completion = createTaskCompletionState();
|
|
170
|
-
const screen = deriveRebindSecretsScreenState(wizard, completion);
|
|
171
|
-
|
|
172
|
-
expect(screen.phase).toBe("active");
|
|
173
|
-
if (screen.phase !== "active") {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const secretsTask = screen.tasks.find((t) => t.id === "re-enter-secrets");
|
|
178
|
-
expect(secretsTask).toBeDefined();
|
|
179
|
-
expect(secretsTask!.description).toContain('"openai-key"');
|
|
180
|
-
expect(secretsTask!.description).toContain('"anthropic-key"');
|
|
181
|
-
expect(secretsTask!.description).toContain('"github-token"');
|
|
182
|
-
expect(secretsTask!.description).toContain("1 of 4");
|
|
183
|
-
});
|
|
184
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
mkdirSync,
|
|
3
|
-
mkdtempSync,
|
|
4
|
-
realpathSync,
|
|
5
|
-
symlinkSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
} from "node:fs";
|
|
8
|
-
import { tmpdir } from "node:os";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { describe, expect, test } from "bun:test";
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
resolveStagedImportPath,
|
|
14
|
-
RESTORE_STAGING_DIRNAME,
|
|
15
|
-
StagedImportPathError,
|
|
16
|
-
} from "../staged-import-path.js";
|
|
17
|
-
|
|
18
|
-
function stagingWorkspace(): { workspace: string; staging: string } {
|
|
19
|
-
const workspace = realpathSync(mkdtempSync(join(tmpdir(), "staged-import-")));
|
|
20
|
-
const staging = join(workspace, RESTORE_STAGING_DIRNAME);
|
|
21
|
-
mkdirSync(staging, { recursive: true });
|
|
22
|
-
return { workspace, staging };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("resolveStagedImportPath", () => {
|
|
26
|
-
test("resolves a relative path inside the staging directory", () => {
|
|
27
|
-
const { workspace, staging } = stagingWorkspace();
|
|
28
|
-
const file = join(staging, "backup.vbundle");
|
|
29
|
-
writeFileSync(file, "bundle");
|
|
30
|
-
|
|
31
|
-
expect(
|
|
32
|
-
resolveStagedImportPath(
|
|
33
|
-
`${RESTORE_STAGING_DIRNAME}/backup.vbundle`,
|
|
34
|
-
workspace,
|
|
35
|
-
),
|
|
36
|
-
).toBe(realpathSync(file));
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("resolves an absolute path inside the staging directory", () => {
|
|
40
|
-
const { workspace, staging } = stagingWorkspace();
|
|
41
|
-
const file = join(staging, "backup.vbundle");
|
|
42
|
-
writeFileSync(file, "bundle");
|
|
43
|
-
|
|
44
|
-
expect(resolveStagedImportPath(file, workspace)).toBe(realpathSync(file));
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("rejects a missing file", () => {
|
|
48
|
-
const { workspace } = stagingWorkspace();
|
|
49
|
-
expect(() =>
|
|
50
|
-
resolveStagedImportPath(
|
|
51
|
-
`${RESTORE_STAGING_DIRNAME}/missing.vbundle`,
|
|
52
|
-
workspace,
|
|
53
|
-
),
|
|
54
|
-
).toThrow(StagedImportPathError);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("rejects traversal out of the staging directory", () => {
|
|
58
|
-
const { workspace } = stagingWorkspace();
|
|
59
|
-
writeFileSync(join(workspace, "secret.vbundle"), "nope");
|
|
60
|
-
|
|
61
|
-
expect(() =>
|
|
62
|
-
resolveStagedImportPath(
|
|
63
|
-
`${RESTORE_STAGING_DIRNAME}/../secret.vbundle`,
|
|
64
|
-
workspace,
|
|
65
|
-
),
|
|
66
|
-
).toThrow(StagedImportPathError);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("rejects a path outside the workspace", () => {
|
|
70
|
-
const { workspace } = stagingWorkspace();
|
|
71
|
-
const outside = join(tmpdir(), `outside-${Date.now()}.vbundle`);
|
|
72
|
-
writeFileSync(outside, "nope");
|
|
73
|
-
|
|
74
|
-
expect(() => resolveStagedImportPath(outside, workspace)).toThrow(
|
|
75
|
-
StagedImportPathError,
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("rejects a symlink even when it points at a staging file", () => {
|
|
80
|
-
const { workspace, staging } = stagingWorkspace();
|
|
81
|
-
const file = join(staging, "backup.vbundle");
|
|
82
|
-
writeFileSync(file, "bundle");
|
|
83
|
-
const link = join(staging, "alias.vbundle");
|
|
84
|
-
symlinkSync(file, link);
|
|
85
|
-
|
|
86
|
-
expect(() => resolveStagedImportPath(link, workspace)).toThrow(
|
|
87
|
-
StagedImportPathError,
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("rejects a non-.vbundle file in staging", () => {
|
|
92
|
-
const { workspace, staging } = stagingWorkspace();
|
|
93
|
-
const file = join(staging, "notes.txt");
|
|
94
|
-
writeFileSync(file, "nope");
|
|
95
|
-
|
|
96
|
-
expect(() =>
|
|
97
|
-
resolveStagedImportPath(
|
|
98
|
-
`${RESTORE_STAGING_DIRNAME}/notes.txt`,
|
|
99
|
-
workspace,
|
|
100
|
-
),
|
|
101
|
-
).toThrow(StagedImportPathError);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("rejects an empty path", () => {
|
|
105
|
-
const { workspace } = stagingWorkspace();
|
|
106
|
-
expect(() => resolveStagedImportPath(" ", workspace)).toThrow(
|
|
107
|
-
StagedImportPathError,
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared test helpers for vbundle v1 manifest fixture builders.
|
|
3
|
-
*
|
|
4
|
-
* Most tests don't care about the specific values of the assistant identity,
|
|
5
|
-
* origin, compatibility, or export-options blocks — they just need the
|
|
6
|
-
* builder/validator to accept their fixtures. Centralizing the defaults
|
|
7
|
-
* keeps every test from re-spelling the same six required option fields.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { randomUUID } from "node:crypto";
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
BuildVBundleOptions,
|
|
14
|
-
VBundleAssistantInfo,
|
|
15
|
-
VBundleCompatibility,
|
|
16
|
-
VBundleExportOptions,
|
|
17
|
-
VBundleOriginInfo,
|
|
18
|
-
} from "../vbundle-builder.js";
|
|
19
|
-
import {
|
|
20
|
-
computeManifestChecksum,
|
|
21
|
-
type ManifestFileEntryType,
|
|
22
|
-
type ManifestType,
|
|
23
|
-
} from "../vbundle-validator.js";
|
|
24
|
-
|
|
25
|
-
export interface DefaultV1Options {
|
|
26
|
-
assistant: VBundleAssistantInfo;
|
|
27
|
-
origin: VBundleOriginInfo;
|
|
28
|
-
compatibility: VBundleCompatibility;
|
|
29
|
-
exportOptions: VBundleExportOptions;
|
|
30
|
-
secretsRedacted: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Sensible defaults for the six caller-required v1 manifest options.
|
|
35
|
-
*
|
|
36
|
-
* `secretsRedacted` defaults to false to match the runtime's typical
|
|
37
|
-
* "credentials included by design" path; tests that exercise the cloud
|
|
38
|
-
* cross-field refine override `origin.mode` and `secretsRedacted` directly.
|
|
39
|
-
*/
|
|
40
|
-
export function defaultV1Options(): DefaultV1Options {
|
|
41
|
-
return {
|
|
42
|
-
assistant: {
|
|
43
|
-
id: "self",
|
|
44
|
-
name: "Test",
|
|
45
|
-
runtime_version: "0.0.0-test",
|
|
46
|
-
},
|
|
47
|
-
origin: {
|
|
48
|
-
mode: "self-hosted-local",
|
|
49
|
-
},
|
|
50
|
-
compatibility: {
|
|
51
|
-
min_runtime_version: "0.0.0-test",
|
|
52
|
-
max_runtime_version: null,
|
|
53
|
-
},
|
|
54
|
-
exportOptions: {
|
|
55
|
-
include_logs: false,
|
|
56
|
-
include_browser_state: false,
|
|
57
|
-
include_memory_vectors: false,
|
|
58
|
-
},
|
|
59
|
-
secretsRedacted: false,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Convenience: spread `defaultV1Options()` into a `BuildVBundleOptions`
|
|
65
|
-
* with the supplied `files`. Saves repeating the spread at every call site.
|
|
66
|
-
*/
|
|
67
|
-
export function buildVBundleTestOptions(
|
|
68
|
-
files: BuildVBundleOptions["files"],
|
|
69
|
-
overrides: Partial<DefaultV1Options> = {},
|
|
70
|
-
): BuildVBundleOptions {
|
|
71
|
-
return {
|
|
72
|
-
files,
|
|
73
|
-
...defaultV1Options(),
|
|
74
|
-
...overrides,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Build a v1 ManifestType for tests, mirroring buildManifestObject() in
|
|
80
|
-
* vbundle-builder.ts. Use this in test fixtures that need a synthetic
|
|
81
|
-
* manifest rather than calling buildVBundle (e.g. cross-version compat
|
|
82
|
-
* tests that need to mutate fields between emit and validate).
|
|
83
|
-
*
|
|
84
|
-
* Pass `overrides` to override any field after the defaults are applied —
|
|
85
|
-
* useful for negative-path tests that exercise specific schema rejections.
|
|
86
|
-
* `schema_version` is widened to `number` so negative tests can write 0/2/etc.
|
|
87
|
-
* The checksum is computed on the merged shape so overrides take effect.
|
|
88
|
-
*/
|
|
89
|
-
export type BuildTestManifestOverrides = Partial<
|
|
90
|
-
Omit<ManifestType, "schema_version">
|
|
91
|
-
> & { schema_version?: number };
|
|
92
|
-
|
|
93
|
-
export function buildTestManifest(input: {
|
|
94
|
-
contents: ManifestFileEntryType[];
|
|
95
|
-
overrides?: BuildTestManifestOverrides;
|
|
96
|
-
}): ManifestType {
|
|
97
|
-
const base = defaultV1Options();
|
|
98
|
-
const merged = {
|
|
99
|
-
schema_version: 1,
|
|
100
|
-
bundle_id: randomUUID(),
|
|
101
|
-
created_at: new Date().toISOString(),
|
|
102
|
-
assistant: base.assistant,
|
|
103
|
-
origin: base.origin,
|
|
104
|
-
compatibility: base.compatibility,
|
|
105
|
-
contents: input.contents,
|
|
106
|
-
checksum: "",
|
|
107
|
-
secrets_redacted: base.secretsRedacted,
|
|
108
|
-
export_options: base.exportOptions,
|
|
109
|
-
...(input.overrides ?? {}),
|
|
110
|
-
} as ManifestType;
|
|
111
|
-
return { ...merged, checksum: computeManifestChecksum(merged) };
|
|
112
|
-
}
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for credential inclusion in vbundle export.
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - buildExportVBundle() with credentials includes credentials/* entries
|
|
6
|
-
* - Credentials appear in the manifest with correct SHA-256 checksums
|
|
7
|
-
* - Export with no credentials produces the same archive as before (backward compat)
|
|
8
|
-
* - Streaming path (streamExportVBundle) includes credential entries
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { createHash } from "node:crypto";
|
|
12
|
-
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
import { join } from "node:path";
|
|
15
|
-
import { gunzipSync } from "node:zlib";
|
|
16
|
-
import { describe, expect, test } from "bun:test";
|
|
17
|
-
|
|
18
|
-
import { buildExportVBundle, streamExportVBundle } from "../vbundle-builder.js";
|
|
19
|
-
import { defaultV1Options } from "./v1-test-helpers.js";
|
|
20
|
-
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// Helpers
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
const BLOCK_SIZE = 512;
|
|
26
|
-
|
|
27
|
-
function sha256Hex(data: Uint8Array | string): string {
|
|
28
|
-
return createHash("sha256").update(data).digest("hex");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Parse a tar archive into its entries (name -> data). */
|
|
32
|
-
function parseTar(tar: Uint8Array): Map<string, Uint8Array> {
|
|
33
|
-
const entries = new Map<string, Uint8Array>();
|
|
34
|
-
let offset = 0;
|
|
35
|
-
let paxPath: string | undefined;
|
|
36
|
-
|
|
37
|
-
while (offset + BLOCK_SIZE <= tar.length) {
|
|
38
|
-
const header = tar.subarray(offset, offset + BLOCK_SIZE);
|
|
39
|
-
|
|
40
|
-
// Check for end-of-archive (all zeros)
|
|
41
|
-
if (header.every((b) => b === 0)) {
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Read file name
|
|
46
|
-
let name = "";
|
|
47
|
-
for (let i = 0; i < 100 && header[i] !== 0; i++) {
|
|
48
|
-
name += String.fromCharCode(header[i]);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Read size (octal at offset 124, 12 bytes)
|
|
52
|
-
let sizeStr = "";
|
|
53
|
-
for (let i = 124; i < 136 && header[i] !== 0; i++) {
|
|
54
|
-
sizeStr += String.fromCharCode(header[i]);
|
|
55
|
-
}
|
|
56
|
-
const size = parseInt(sizeStr.trim(), 8) || 0;
|
|
57
|
-
|
|
58
|
-
// Type flag
|
|
59
|
-
const typeFlag = String.fromCharCode(header[156]);
|
|
60
|
-
|
|
61
|
-
offset += BLOCK_SIZE;
|
|
62
|
-
|
|
63
|
-
const data = tar.subarray(offset, offset + size);
|
|
64
|
-
const paddedSize = Math.ceil(size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
65
|
-
offset += paddedSize;
|
|
66
|
-
|
|
67
|
-
if (typeFlag === "x") {
|
|
68
|
-
// PAX extended header — extract path
|
|
69
|
-
const paxStr = new TextDecoder().decode(data);
|
|
70
|
-
const match = paxStr.match(/\d+ path=(.+)\n/);
|
|
71
|
-
if (match) {
|
|
72
|
-
paxPath = match[1];
|
|
73
|
-
}
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const entryName = paxPath ?? name;
|
|
78
|
-
paxPath = undefined;
|
|
79
|
-
entries.set(entryName, new Uint8Array(data));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return entries;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Create a minimal workspace directory with a config file for testing. */
|
|
86
|
-
function createTestWorkspace(): { dir: string; cleanup: () => void } {
|
|
87
|
-
const dir = join(
|
|
88
|
-
tmpdir(),
|
|
89
|
-
`vbundle-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
90
|
-
);
|
|
91
|
-
mkdirSync(dir, { recursive: true });
|
|
92
|
-
writeFileSync(join(dir, "config.json"), JSON.stringify({ test: true }));
|
|
93
|
-
// Create a minimal data/db directory with a fake database file
|
|
94
|
-
const dbDir = join(dir, "data", "db");
|
|
95
|
-
mkdirSync(dbDir, { recursive: true });
|
|
96
|
-
writeFileSync(join(dbDir, "assistant.db"), "fake-db-content");
|
|
97
|
-
return {
|
|
98
|
-
dir,
|
|
99
|
-
cleanup: () => rmSync(dir, { recursive: true, force: true }),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
// Tests
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
describe("buildExportVBundle with credentials", () => {
|
|
108
|
-
test("includes credential entries under credentials/ prefix", () => {
|
|
109
|
-
const workspace = createTestWorkspace();
|
|
110
|
-
try {
|
|
111
|
-
const credentials = [
|
|
112
|
-
{ account: "openai-key", value: "sk-test-123" },
|
|
113
|
-
{ account: "anthropic-key", value: "sk-ant-456" },
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
const result = buildExportVBundle({
|
|
117
|
-
...defaultV1Options(),
|
|
118
|
-
workspaceDir: workspace.dir,
|
|
119
|
-
credentials,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const tar = gunzipSync(result.archive);
|
|
123
|
-
const entries = parseTar(tar);
|
|
124
|
-
|
|
125
|
-
expect(entries.has("credentials/openai-key")).toBe(true);
|
|
126
|
-
expect(entries.has("credentials/anthropic-key")).toBe(true);
|
|
127
|
-
|
|
128
|
-
const decoder = new TextDecoder();
|
|
129
|
-
expect(decoder.decode(entries.get("credentials/openai-key")!)).toBe(
|
|
130
|
-
"sk-test-123",
|
|
131
|
-
);
|
|
132
|
-
expect(decoder.decode(entries.get("credentials/anthropic-key")!)).toBe(
|
|
133
|
-
"sk-ant-456",
|
|
134
|
-
);
|
|
135
|
-
} finally {
|
|
136
|
-
workspace.cleanup();
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("credentials appear in manifest with correct SHA-256 checksums", () => {
|
|
141
|
-
const workspace = createTestWorkspace();
|
|
142
|
-
try {
|
|
143
|
-
const credentials = [
|
|
144
|
-
{ account: "my-api-key", value: "secret-value-abc" },
|
|
145
|
-
];
|
|
146
|
-
|
|
147
|
-
const result = buildExportVBundle({
|
|
148
|
-
...defaultV1Options(),
|
|
149
|
-
workspaceDir: workspace.dir,
|
|
150
|
-
credentials,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const expectedHash = sha256Hex(
|
|
154
|
-
new TextEncoder().encode("secret-value-abc"),
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const credEntry = result.manifest.contents.find(
|
|
158
|
-
(f) => f.path === "credentials/my-api-key",
|
|
159
|
-
);
|
|
160
|
-
expect(credEntry).toBeDefined();
|
|
161
|
-
expect(credEntry!.sha256).toBe(expectedHash);
|
|
162
|
-
expect(credEntry!.size_bytes).toBe(
|
|
163
|
-
new TextEncoder().encode("secret-value-abc").length,
|
|
164
|
-
);
|
|
165
|
-
} finally {
|
|
166
|
-
workspace.cleanup();
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test("export with no credentials produces archive without credentials/ entries", () => {
|
|
171
|
-
const workspace = createTestWorkspace();
|
|
172
|
-
try {
|
|
173
|
-
const result = buildExportVBundle({
|
|
174
|
-
...defaultV1Options(),
|
|
175
|
-
workspaceDir: workspace.dir,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const tar = gunzipSync(result.archive);
|
|
179
|
-
const entries = parseTar(tar);
|
|
180
|
-
|
|
181
|
-
const credentialEntries = [...entries.keys()].filter((k) =>
|
|
182
|
-
k.startsWith("credentials/"),
|
|
183
|
-
);
|
|
184
|
-
expect(credentialEntries).toHaveLength(0);
|
|
185
|
-
|
|
186
|
-
// Manifest should have no credential entries
|
|
187
|
-
const credManifestEntries = result.manifest.contents.filter((f) =>
|
|
188
|
-
f.path.startsWith("credentials/"),
|
|
189
|
-
);
|
|
190
|
-
expect(credManifestEntries).toHaveLength(0);
|
|
191
|
-
} finally {
|
|
192
|
-
workspace.cleanup();
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test("export with empty credentials array produces archive without credentials/ entries", () => {
|
|
197
|
-
const workspace = createTestWorkspace();
|
|
198
|
-
try {
|
|
199
|
-
const result = buildExportVBundle({
|
|
200
|
-
...defaultV1Options(),
|
|
201
|
-
workspaceDir: workspace.dir,
|
|
202
|
-
credentials: [],
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const tar = gunzipSync(result.archive);
|
|
206
|
-
const entries = parseTar(tar);
|
|
207
|
-
|
|
208
|
-
const credentialEntries = [...entries.keys()].filter((k) =>
|
|
209
|
-
k.startsWith("credentials/"),
|
|
210
|
-
);
|
|
211
|
-
expect(credentialEntries).toHaveLength(0);
|
|
212
|
-
} finally {
|
|
213
|
-
workspace.cleanup();
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe("streamExportVBundle with credentials", () => {
|
|
219
|
-
test("includes credential entries in the streaming archive", async () => {
|
|
220
|
-
const workspace = createTestWorkspace();
|
|
221
|
-
let result: Awaited<ReturnType<typeof streamExportVBundle>> | undefined;
|
|
222
|
-
try {
|
|
223
|
-
const credentials = [
|
|
224
|
-
{ account: "stream-key", value: "stream-secret-789" },
|
|
225
|
-
];
|
|
226
|
-
|
|
227
|
-
result = await streamExportVBundle({
|
|
228
|
-
...defaultV1Options(),
|
|
229
|
-
workspaceDir: workspace.dir,
|
|
230
|
-
credentials,
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// Read the temp file and decompress
|
|
234
|
-
const archiveData = new Uint8Array(
|
|
235
|
-
await Bun.file(result.tempPath).arrayBuffer(),
|
|
236
|
-
);
|
|
237
|
-
const tar = gunzipSync(archiveData);
|
|
238
|
-
const entries = parseTar(tar);
|
|
239
|
-
|
|
240
|
-
expect(entries.has("credentials/stream-key")).toBe(true);
|
|
241
|
-
|
|
242
|
-
const decoder = new TextDecoder();
|
|
243
|
-
expect(decoder.decode(entries.get("credentials/stream-key")!)).toBe(
|
|
244
|
-
"stream-secret-789",
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
// Verify manifest entry
|
|
248
|
-
const credEntry = result.manifest.contents.find(
|
|
249
|
-
(f) => f.path === "credentials/stream-key",
|
|
250
|
-
);
|
|
251
|
-
expect(credEntry).toBeDefined();
|
|
252
|
-
expect(credEntry!.sha256).toBe(
|
|
253
|
-
sha256Hex(new TextEncoder().encode("stream-secret-789")),
|
|
254
|
-
);
|
|
255
|
-
} finally {
|
|
256
|
-
await result?.cleanup();
|
|
257
|
-
workspace.cleanup();
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test("streaming export without credentials has no credentials/ entries", async () => {
|
|
262
|
-
const workspace = createTestWorkspace();
|
|
263
|
-
let result: Awaited<ReturnType<typeof streamExportVBundle>> | undefined;
|
|
264
|
-
try {
|
|
265
|
-
result = await streamExportVBundle({
|
|
266
|
-
...defaultV1Options(),
|
|
267
|
-
workspaceDir: workspace.dir,
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const archiveData = new Uint8Array(
|
|
271
|
-
await Bun.file(result.tempPath).arrayBuffer(),
|
|
272
|
-
);
|
|
273
|
-
const tar = gunzipSync(archiveData);
|
|
274
|
-
const entries = parseTar(tar);
|
|
275
|
-
|
|
276
|
-
const credentialEntries = [...entries.keys()].filter((k) =>
|
|
277
|
-
k.startsWith("credentials/"),
|
|
278
|
-
);
|
|
279
|
-
expect(credentialEntries).toHaveLength(0);
|
|
280
|
-
} finally {
|
|
281
|
-
await result?.cleanup();
|
|
282
|
-
workspace.cleanup();
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
});
|