@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,297 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory registry for async migration jobs (export/import).
|
|
3
|
-
*
|
|
4
|
-
* Each job has a UUID, a type, a lifecycle (`pending` -> `running` ->
|
|
5
|
-
* `complete`/`failed`), and optional result/error payloads. Runners are
|
|
6
|
-
* scheduled with `queueMicrotask` so `startJob` returns synchronously and
|
|
7
|
-
* callers can immediately poll the job record.
|
|
8
|
-
*
|
|
9
|
-
* At most one job of each `MigrationJobType` may be in-flight at a time;
|
|
10
|
-
* attempting to start a second one while another is pending or running
|
|
11
|
-
* rejects with `JobAlreadyInProgressError`.
|
|
12
|
-
*
|
|
13
|
-
* Completed/failed jobs are kept for `completedJobTtlMs` (default 10
|
|
14
|
-
* minutes) so clients can still fetch the final status after the runner
|
|
15
|
-
* finishes, then evicted by `sweep()` which is wired to a periodic
|
|
16
|
-
* `setInterval` on the singleton. The interval is `unref()`'d so it does
|
|
17
|
-
* not block process shutdown.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { randomUUID } from "node:crypto";
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Types
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
export type MigrationJobType = "export" | "import";
|
|
27
|
-
|
|
28
|
-
export type MigrationJobStatus = "pending" | "running" | "complete" | "failed";
|
|
29
|
-
|
|
30
|
-
export interface MigrationJobError {
|
|
31
|
-
code: string;
|
|
32
|
-
message: string;
|
|
33
|
-
upstreamStatus?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface MigrationJob {
|
|
37
|
-
id: string;
|
|
38
|
-
type: MigrationJobType;
|
|
39
|
-
status: MigrationJobStatus;
|
|
40
|
-
createdAt: number;
|
|
41
|
-
startedAt?: number;
|
|
42
|
-
completedAt?: number;
|
|
43
|
-
error?: MigrationJobError;
|
|
44
|
-
result?: unknown;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Thrown by `MigrationJobRegistry.startJob` when a job of the same type
|
|
49
|
-
* is already pending or running.
|
|
50
|
-
*/
|
|
51
|
-
export class JobAlreadyInProgressError extends Error {
|
|
52
|
-
public readonly existingJobId: string;
|
|
53
|
-
|
|
54
|
-
constructor(type: MigrationJobType, existingJobId: string) {
|
|
55
|
-
super(
|
|
56
|
-
`A ${type} migration job is already in progress (id=${existingJobId}).`,
|
|
57
|
-
);
|
|
58
|
-
this.name = "JobAlreadyInProgressError";
|
|
59
|
-
this.existingJobId = existingJobId;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
// kFetchBodyError — preserve the tag convention used by the URL-body import
|
|
65
|
-
// path in `routes/migration-routes.ts`. An error object carrying this symbol
|
|
66
|
-
// was thrown from a failed upstream fetch body; we translate it into
|
|
67
|
-
// `error.code = "fetch_failed"` for clients.
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
|
|
70
|
-
const kFetchBodyError = Symbol.for("cadet.migrationImport.fetchBodyError");
|
|
71
|
-
|
|
72
|
-
function isFetchBodyError(err: unknown): boolean {
|
|
73
|
-
if (!err || typeof err !== "object") {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
return (err as unknown as Record<symbol, boolean>)[kFetchBodyError] === true;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function extractUpstreamStatus(err: unknown): number | undefined {
|
|
80
|
-
if (!err || typeof err !== "object") {
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
const status = (err as { upstreamStatus?: unknown }).upstreamStatus;
|
|
84
|
-
return typeof status === "number" ? status : undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function mapError(err: unknown): MigrationJobError {
|
|
88
|
-
const message =
|
|
89
|
-
err instanceof Error
|
|
90
|
-
? err.message
|
|
91
|
-
: typeof err === "object" &&
|
|
92
|
-
err !== null &&
|
|
93
|
-
"message" in err &&
|
|
94
|
-
typeof (err as { message?: unknown }).message === "string"
|
|
95
|
-
? (err as { message: string }).message
|
|
96
|
-
: String(err);
|
|
97
|
-
|
|
98
|
-
if (isFetchBodyError(err)) {
|
|
99
|
-
const mapped: MigrationJobError = {
|
|
100
|
-
code: "fetch_failed",
|
|
101
|
-
message,
|
|
102
|
-
};
|
|
103
|
-
const upstreamStatus = extractUpstreamStatus(err);
|
|
104
|
-
if (upstreamStatus !== undefined) {
|
|
105
|
-
mapped.upstreamStatus = upstreamStatus;
|
|
106
|
-
}
|
|
107
|
-
return mapped;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Runner-provided override: `err.code` wins over the `"unknown"` default
|
|
111
|
-
// so callers that throw typed errors get their codes preserved.
|
|
112
|
-
const overrideCode =
|
|
113
|
-
typeof err === "object" &&
|
|
114
|
-
err !== null &&
|
|
115
|
-
typeof (err as { code?: unknown }).code === "string"
|
|
116
|
-
? (err as { code: string }).code
|
|
117
|
-
: undefined;
|
|
118
|
-
|
|
119
|
-
const mapped: MigrationJobError = {
|
|
120
|
-
code: overrideCode ?? "unknown",
|
|
121
|
-
message,
|
|
122
|
-
};
|
|
123
|
-
const upstreamStatus = extractUpstreamStatus(err);
|
|
124
|
-
if (upstreamStatus !== undefined) {
|
|
125
|
-
mapped.upstreamStatus = upstreamStatus;
|
|
126
|
-
}
|
|
127
|
-
return mapped;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
// Registry
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Default TTL for completed/failed jobs before `sweep()` evicts them.
|
|
136
|
-
* Exposed as a class field so tests can override the singleton's value.
|
|
137
|
-
*/
|
|
138
|
-
const DEFAULT_COMPLETED_JOB_TTL_MS = 10 * 60 * 1000;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Return a shallow clone of a `MigrationJob` that is decoupled from the
|
|
142
|
-
* internal registry record. The optional `error` object is spread so
|
|
143
|
-
* callers cannot mutate the stored error either. `result` is `unknown` by
|
|
144
|
-
* design — callers that pass mutable values in and then mutate them
|
|
145
|
-
* externally are already outside the registry's invariants.
|
|
146
|
-
*/
|
|
147
|
-
function cloneJob(job: MigrationJob): MigrationJob {
|
|
148
|
-
const snapshot: MigrationJob = {
|
|
149
|
-
id: job.id,
|
|
150
|
-
type: job.type,
|
|
151
|
-
status: job.status,
|
|
152
|
-
createdAt: job.createdAt,
|
|
153
|
-
};
|
|
154
|
-
if (job.startedAt !== undefined) {
|
|
155
|
-
snapshot.startedAt = job.startedAt;
|
|
156
|
-
}
|
|
157
|
-
if (job.completedAt !== undefined) {
|
|
158
|
-
snapshot.completedAt = job.completedAt;
|
|
159
|
-
}
|
|
160
|
-
if (job.error !== undefined) {
|
|
161
|
-
snapshot.error = { ...job.error };
|
|
162
|
-
}
|
|
163
|
-
if (job.result !== undefined) {
|
|
164
|
-
snapshot.result = job.result;
|
|
165
|
-
}
|
|
166
|
-
return snapshot;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export class MigrationJobRegistry {
|
|
170
|
-
private readonly jobs = new Map<string, MigrationJob>();
|
|
171
|
-
/** Tracks the single in-flight (pending or running) job id per type. */
|
|
172
|
-
private readonly inFlight = new Map<MigrationJobType, string>();
|
|
173
|
-
|
|
174
|
-
/** TTL for completed/failed jobs; mutable so tests can tighten it. */
|
|
175
|
-
public completedJobTtlMs: number = DEFAULT_COMPLETED_JOB_TTL_MS;
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Start a new migration job. Returns a snapshot of the `MigrationJob`
|
|
179
|
-
* record synchronously (status `"pending"`); the runner is scheduled via
|
|
180
|
-
* `queueMicrotask` so the caller can poll via `getJob(id)` immediately.
|
|
181
|
-
*
|
|
182
|
-
* The returned object is a shallow clone decoupled from the internal
|
|
183
|
-
* record so that external mutation cannot violate the single-in-flight
|
|
184
|
-
* invariant (e.g. flipping a running job's status to `"complete"` must
|
|
185
|
-
* not unblock a same-type `startJob` while the runner is still active).
|
|
186
|
-
*
|
|
187
|
-
* Throws `JobAlreadyInProgressError` if another job of the same type is
|
|
188
|
-
* already pending or running.
|
|
189
|
-
*/
|
|
190
|
-
public startJob<T>(
|
|
191
|
-
type: MigrationJobType,
|
|
192
|
-
runner: (job: MigrationJob) => Promise<T>,
|
|
193
|
-
): MigrationJob {
|
|
194
|
-
const existingId = this.inFlight.get(type);
|
|
195
|
-
if (existingId !== undefined) {
|
|
196
|
-
const existing = this.jobs.get(existingId);
|
|
197
|
-
if (
|
|
198
|
-
existing &&
|
|
199
|
-
(existing.status === "pending" || existing.status === "running")
|
|
200
|
-
) {
|
|
201
|
-
throw new JobAlreadyInProgressError(type, existingId);
|
|
202
|
-
}
|
|
203
|
-
// Stale entry (e.g. sweep raced with a new start) — clear it.
|
|
204
|
-
this.inFlight.delete(type);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const job: MigrationJob = {
|
|
208
|
-
id: randomUUID(),
|
|
209
|
-
type,
|
|
210
|
-
status: "pending",
|
|
211
|
-
createdAt: Date.now(),
|
|
212
|
-
};
|
|
213
|
-
this.jobs.set(job.id, job);
|
|
214
|
-
this.inFlight.set(type, job.id);
|
|
215
|
-
|
|
216
|
-
queueMicrotask(() => {
|
|
217
|
-
job.status = "running";
|
|
218
|
-
job.startedAt = Date.now();
|
|
219
|
-
Promise.resolve()
|
|
220
|
-
.then(() => runner(job))
|
|
221
|
-
.then(
|
|
222
|
-
(result) => {
|
|
223
|
-
job.status = "complete";
|
|
224
|
-
job.completedAt = Date.now();
|
|
225
|
-
job.result = result;
|
|
226
|
-
if (this.inFlight.get(type) === job.id) {
|
|
227
|
-
this.inFlight.delete(type);
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
(err: unknown) => {
|
|
231
|
-
job.status = "failed";
|
|
232
|
-
job.completedAt = Date.now();
|
|
233
|
-
job.error = mapError(err);
|
|
234
|
-
if (this.inFlight.get(type) === job.id) {
|
|
235
|
-
this.inFlight.delete(type);
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
return cloneJob(job);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
public getJob(id: string): MigrationJob | null {
|
|
245
|
-
const job = this.jobs.get(id);
|
|
246
|
-
return job ? cloneJob(job) : null;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
public listJobs(): MigrationJob[] {
|
|
250
|
-
return Array.from(this.jobs.values(), cloneJob);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Drop completed/failed jobs whose `completedAt` is older than
|
|
255
|
-
* `completedJobTtlMs`. Pending/running jobs are always retained.
|
|
256
|
-
*/
|
|
257
|
-
public sweep(): void {
|
|
258
|
-
const now = Date.now();
|
|
259
|
-
for (const [id, job] of this.jobs) {
|
|
260
|
-
if (job.status !== "complete" && job.status !== "failed") {
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
const completedAt = job.completedAt;
|
|
264
|
-
if (completedAt === undefined) {
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
if (now - completedAt >= this.completedJobTtlMs) {
|
|
268
|
-
this.jobs.delete(id);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// Singleton + sweep interval
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
|
|
278
|
-
/** Process-wide registry used by migration routes. */
|
|
279
|
-
export const migrationJobs = new MigrationJobRegistry();
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* How often the sweep runs. The singleton wires this via `setInterval`;
|
|
283
|
-
* the interval is `unref()`'d so it does not keep the event loop alive
|
|
284
|
-
* during shutdown. Tests that need to stop the interval can call
|
|
285
|
-
* `clearInterval(sweepIntervalId)`.
|
|
286
|
-
*/
|
|
287
|
-
const SWEEP_INTERVAL_MS = 60 * 1000;
|
|
288
|
-
|
|
289
|
-
const sweepIntervalId: NodeJS.Timeout = setInterval(() => {
|
|
290
|
-
migrationJobs.sweep();
|
|
291
|
-
}, SWEEP_INTERVAL_MS);
|
|
292
|
-
|
|
293
|
-
// `.unref()` exists on Node/Bun Timeout objects. Guard defensively in case
|
|
294
|
-
// an alternative runtime returns a plain number.
|
|
295
|
-
if (typeof (sweepIntervalId as { unref?: () => void }).unref === "function") {
|
|
296
|
-
(sweepIntervalId as { unref: () => void }).unref();
|
|
297
|
-
}
|