@openparachute/vault 0.4.6 → 0.4.7-rc.1

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/src/server.ts CHANGED
@@ -27,6 +27,9 @@ import { startTranscriptionWorker, registerTranscriptionHook, type Transcription
27
27
  import { assetsDir } from "./routes.ts";
28
28
  import { resolveScribeAuthToken } from "./scribe-env.ts";
29
29
  import { resolveBindHostname } from "./bind.ts";
30
+ import { MirrorManager } from "./mirror-manager.ts";
31
+ import { setMirrorManager } from "./mirror-registry.ts";
32
+ import { buildMirrorDeps, resolveMirrorVaultName } from "./mirror-deps.ts";
30
33
 
31
34
  // Register webhook triggers from global config. Replaces the old hardcoded
32
35
  // tts-hook and transcription-hook with config-driven webhooks.
@@ -192,6 +195,51 @@ const globalConfig = readGlobalConfig();
192
195
  const port = parseInt(process.env.PORT ?? "") || globalConfig.port || DEFAULT_PORT;
193
196
  const hostname = resolveBindHostname();
194
197
 
198
+ // ---------------------------------------------------------------------------
199
+ // Mirror lifecycle (vault-sync Phase A1).
200
+ //
201
+ // Boot-time bootstrap of the persistent mirror manager. Only stands up an
202
+ // active mirror when global config carries `mirror.enabled: true`; otherwise
203
+ // the manager construct + status reflects "disabled" but no filesystem work
204
+ // happens. The HTTP `/admin/mirror` routes can still flip it on at runtime
205
+ // without a vault restart — see routing.ts + mirror-routes.ts.
206
+ //
207
+ // Failure here is logged and ignored; vault keeps serving. The operator
208
+ // sees the error via `GET /admin/mirror` + the log line.
209
+ // ---------------------------------------------------------------------------
210
+ let mirrorManager: MirrorManager | null = null;
211
+ {
212
+ // Canonicalized in `mirror-deps.ts:resolveMirrorVaultName` so the
213
+ // binding rule (default_vault → first listed vault → null) lives in
214
+ // exactly one place; multi-vault-mirror work (design-doc open
215
+ // question 2) only has to touch one site.
216
+ const mirrorVaultName = resolveMirrorVaultName(listVaults);
217
+ if (mirrorVaultName) {
218
+ try {
219
+ mirrorManager = new MirrorManager(buildMirrorDeps(mirrorVaultName));
220
+ setMirrorManager(mirrorManager);
221
+ // Don't block server startup on a slow initial export — kick it off
222
+ // in the background, log the outcome. The HTTP server comes up
223
+ // immediately so OAuth + REST aren't blocked behind a multi-second
224
+ // export pass.
225
+ void mirrorManager
226
+ .start()
227
+ .then((status) => {
228
+ if (!status.enabled && status.last_error) {
229
+ console.warn(`[mirror] startup error: ${status.last_error}`);
230
+ }
231
+ })
232
+ .catch((err) => {
233
+ console.warn(`[mirror] startup threw: ${(err as Error).message ?? err}`);
234
+ });
235
+ } catch (err) {
236
+ console.warn(`[mirror] manager construction failed: ${(err as Error).message ?? err}`);
237
+ }
238
+ } else {
239
+ console.log("[mirror] no vaults yet — manager will be initialized on next restart after a vault exists");
240
+ }
241
+ }
242
+
195
243
  const server = Bun.serve({
196
244
  port,
197
245
  hostname,
@@ -250,6 +298,10 @@ async function shutdown(signal: string): Promise<void> {
250
298
  Promise.all([
251
299
  defaultHookRegistry.drain(),
252
300
  transcriptionWorker?.stop() ?? Promise.resolve(),
301
+ // Mirror manager: cancel the watch interval + let any in-flight
302
+ // export cycle settle. `stop` already has its own brief timeout
303
+ // (250ms) so this doesn't block the larger shutdown race.
304
+ mirrorManager?.stop() ?? Promise.resolve(),
253
305
  ]),
254
306
  new Promise<void>((resolve) => setTimeout(resolve, 5000)),
255
307
  ]);