@serviceme/devtools-core 0.1.8 → 0.2.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.
Files changed (47) hide show
  1. package/dist/auth.d.mts +590 -0
  2. package/dist/auth.d.ts +590 -0
  3. package/dist/auth.js +842 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/auth.mjs +804 -0
  6. package/dist/auth.mjs.map +1 -0
  7. package/dist/device.d.mts +456 -0
  8. package/dist/device.d.ts +456 -0
  9. package/dist/device.js +696 -0
  10. package/dist/device.js.map +1 -0
  11. package/dist/device.mjs +647 -0
  12. package/dist/device.mjs.map +1 -0
  13. package/dist/index-CrNC-aao.d.ts +277 -0
  14. package/dist/index-Dmyy4urr.d.mts +277 -0
  15. package/dist/index.d.mts +1372 -27
  16. package/dist/index.d.ts +1372 -27
  17. package/dist/index.js +5125 -888
  18. package/dist/index.js.map +1 -0
  19. package/dist/index.mjs +5022 -906
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/skill-linker.d.mts +188 -0
  22. package/dist/skill-linker.d.ts +188 -0
  23. package/dist/skill-linker.js +374 -0
  24. package/dist/skill-linker.js.map +1 -0
  25. package/dist/skill-linker.mjs +330 -0
  26. package/dist/skill-linker.mjs.map +1 -0
  27. package/dist/skill-store.d.mts +35 -0
  28. package/dist/skill-store.d.ts +35 -0
  29. package/dist/skill-store.js +291 -0
  30. package/dist/skill-store.js.map +1 -0
  31. package/dist/skill-store.mjs +254 -0
  32. package/dist/skill-store.mjs.map +1 -0
  33. package/dist/submit.d.mts +3 -0
  34. package/dist/submit.d.ts +3 -0
  35. package/dist/submit.js +166 -0
  36. package/dist/submit.js.map +1 -0
  37. package/dist/submit.mjs +130 -0
  38. package/dist/submit.mjs.map +1 -0
  39. package/dist/toolbox.d.mts +240 -0
  40. package/dist/toolbox.d.ts +240 -0
  41. package/dist/toolbox.js +530 -0
  42. package/dist/toolbox.js.map +1 -0
  43. package/dist/toolbox.mjs +482 -0
  44. package/dist/toolbox.mjs.map +1 -0
  45. package/dist/types-B9gk3dXH.d.mts +62 -0
  46. package/dist/types-B9gk3dXH.d.ts +62 -0
  47. package/package.json +50 -5
@@ -0,0 +1,456 @@
1
+ import { DeviceBindingState, DeviceEnrollResult, DeviceStatus, DeviceRotateSecretResult } from '@serviceme/devtools-protocol';
2
+
3
+ /**
4
+ * deviceAuth — Pure HMAC-SHA-256 signing helpers for device auth headers.
5
+ *
6
+ * Ported byte-for-byte from
7
+ * `apps/extension/src/services/device/deviceAuth.ts:11-26` (the wire
8
+ * algorithm is locked by the server-side `device-signature-guard.ts`
9
+ * verifier, see `docs/architecture/phase-5-device-header-spec.md` §5).
10
+ *
11
+ * The basis is `METHOD\nPATH\nTIMESTAMP\nBODY\nSECRET` (LF-joined,
12
+ * NOT JSON). Output is lowercase hex SHA-256.
13
+ *
14
+ * Refs:
15
+ * - 4.功能规划.md §2.2 — `deviceAuth.ts HMAC 签名(纯 node:crypto,直接搬)`
16
+ * - `docs/architecture/phase-5-device-header-spec.md` §5 (signature format)
17
+ */
18
+ /** Canonical header names — MUST match server's `device-signature-guard.ts:9-15`. */
19
+ declare const DeviceAuthHeaders: {
20
+ readonly deviceId: "x-ms-device-id";
21
+ readonly deviceSecret: "x-ms-device-secret";
22
+ readonly signature: "x-ms-device-signature";
23
+ readonly timestamp: "x-ms-device-timestamp";
24
+ readonly secretVersion: "x-ms-device-secret-version";
25
+ };
26
+ interface DeviceRequestSignatureParams {
27
+ method: string;
28
+ path: string;
29
+ timestamp: number;
30
+ body: string;
31
+ secret: string;
32
+ }
33
+ /**
34
+ * Compute the HMAC-SHA-256 hex digest of the canonical basis.
35
+ *
36
+ * Server contract (`apps/server/src/lib/auth/device-signature-guard.ts:46-62`)
37
+ * is line-for-line identical: same LF-joined basis, same lowercase
38
+ * hex output. Any divergence breaks `device-signature-guard.test.ts`.
39
+ */
40
+ declare function createDeviceRequestSignature(params: DeviceRequestSignatureParams): string;
41
+ /** 5-header map consumed by `fetch()` callers (CLI bridge, Extension). */
42
+ interface DeviceSignedHeaders {
43
+ [DeviceAuthHeaders.deviceId]: string;
44
+ [DeviceAuthHeaders.deviceSecret]: string;
45
+ [DeviceAuthHeaders.signature]: string;
46
+ [DeviceAuthHeaders.timestamp]: string;
47
+ [DeviceAuthHeaders.secretVersion]: string;
48
+ }
49
+ interface BuildSignedHeadersParams {
50
+ method: string;
51
+ path: string;
52
+ body: string;
53
+ publicId: string;
54
+ deviceSecret: string;
55
+ secretVersion: number;
56
+ /** Override for deterministic tests. */
57
+ timestamp?: number;
58
+ }
59
+ /**
60
+ * Build the canonical 5-header map. The `body` parameter MUST be the
61
+ * exact byte sequence sent on the wire (no whitespace re-canonicalization
62
+ * between client serialization and signature basis construction).
63
+ */
64
+ declare function buildSignedHeaders(params: BuildSignedHeadersParams): DeviceSignedHeaders;
65
+
66
+ /**
67
+ * Internal types for the device domain.
68
+ *
69
+ * These types are NOT re-exported from the protocol package — they are
70
+ * implementation details of the IdentityStore + Enroller. Public data
71
+ * models (the bridge wire shape) live in `@serviceme/devtools-protocol`'s
72
+ * `device.ts`.
73
+ *
74
+ * Refs:
75
+ * - 4.功能规划.md §2.2 — `device/types.ts`
76
+ */
77
+
78
+ /**
79
+ * Schema version of the on-disk `device.json` file. Bumped when the
80
+ * shape changes incompatibly. IdentityStore checks this on read and
81
+ * either migrates (versions ≤ 1) or refuses (versions > supported).
82
+ */
83
+ declare const DEVICE_JSON_SCHEMA_VERSION = 1;
84
+ /** Internal representation of the persisted identity file. */
85
+ interface PersistedDeviceIdentity {
86
+ version: number;
87
+ /** Stable per-machine id (UUID v4 shape) — survives secret rotates. */
88
+ installationId: string;
89
+ /** Raw `os.hostname()` for diagnostics. */
90
+ machineId: string;
91
+ /** Platform string (e.g. "darwin"). */
92
+ platform: string;
93
+ /** Optional hostname override for environments where `os.hostname()` is unstable. */
94
+ hostname?: string;
95
+ /** Public, non-secret id returned by the server. 32-char hex. */
96
+ publicId: string;
97
+ /** Monotonic secret version counter, starts at 1 after first enroll. */
98
+ secretVersion: number;
99
+ /** Current binding state — drives the re-enroll matrix. */
100
+ bindingState: DeviceBindingState;
101
+ /** HMAC secret (32 bytes hex-encoded = 64 chars). Persisted per `device-header-spec.md` §3.1. */
102
+ deviceSecret: string;
103
+ /** Optional: previous secret retained during the grace window (rotation). */
104
+ previousDeviceSecret?: string;
105
+ /** Optional: ISO timestamp at which the previous secret stops being accepted. */
106
+ previousSecretExpiresAt?: string;
107
+ /** ISO timestamp of the most recent successful enroll / rotate. */
108
+ lastEnrollAt: string;
109
+ /** Optional ISO timestamp of the most recent server sync. */
110
+ lastSyncAt?: string;
111
+ /** Optional human-readable message for the last sync error. */
112
+ lastSyncError?: string;
113
+ }
114
+ /** Result of a single atomic write. */
115
+ interface AtomicWriteResult {
116
+ bytesWritten: number;
117
+ /** Path to the temp file (post-rename it no longer exists; useful for diagnostics). */
118
+ tmpPath: string;
119
+ }
120
+ /** Hook called before/after every identity write — used by tests to assert concurrency safety. */
121
+ interface IdentityStoreHooks {
122
+ beforeWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;
123
+ afterWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;
124
+ }
125
+
126
+ /**
127
+ * IdentityStore — Atomic JSON persistence for the device identity file.
128
+ *
129
+ * Stores the `PersistedDeviceIdentity` (incl. the HMAC secret cleartext)
130
+ * at `~/.serviceme/device.json` (per `phase-5-device-header-spec.md`
131
+ * §3.1). Writes are atomic via `write-tmp + fsync + rename`, matching
132
+ * the `SkillStore` / `ToolboxStore` precedent. Concurrent writes are
133
+ * serialized with a mkdir-based file lock (POSIX-atomic) — proper
134
+ * cross-process locking is deferred to Phase 6+ per the open spec.
135
+ *
136
+ * The file mode is `0600` (owner read/write only) so the cleartext
137
+ * secret stays safe at rest. On Windows the mode hint is a no-op
138
+ * (Windows uses ACLs) but `writeFile` still succeeds.
139
+ *
140
+ * Migration — IdentityStore auto-detects a v0-shape (pre-Phase-5.2)
141
+ * file written by the Extension's old `globalState` blob:
142
+ * { version: 1, claimed: false, publicKeyFingerprint: null }
143
+ * In that case the file is migrated forward to the v1 schema on the
144
+ * next write (the data fields are empty and a fresh enroll is required).
145
+ * The full Extension `globalState` → JSON migration happens in the
146
+ * Phase 5.5 adapter (`apps/extension/.../DeviceService.ts`) since the
147
+ * adapter holds the live `globalState` access.
148
+ *
149
+ * Refs:
150
+ * - 4.功能规划.md §2.2 — `IdentityStore.ts 持久化到 ~/.config/serviceme/device.json, 原子写`
151
+ * - `docs/architecture/phase-5-device-header-spec.md` §3.1, §2.5
152
+ */
153
+
154
+ /**
155
+ * Minimal interface for reading + writing the persisted identity file.
156
+ * Default impl uses `getDeviceJsonPath()` (which honors `SERVICEME_HOME`),
157
+ * but tests can substitute a custom path for isolation.
158
+ */
159
+ interface IdentityFileBackend {
160
+ read(filePath: string): Promise<PersistedDeviceIdentity | null>;
161
+ write(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult>;
162
+ exists(filePath: string): Promise<boolean>;
163
+ delete(filePath: string): Promise<void>;
164
+ listDir?(dir: string): Promise<string[]>;
165
+ }
166
+ interface IdentityStoreOptions {
167
+ filePath?: string;
168
+ hooks?: IdentityStoreHooks;
169
+ lockTimeoutMs?: number;
170
+ lockRetryMs?: number;
171
+ /** Injectable clock for deterministic tests. */
172
+ now?: () => Date;
173
+ backend?: IdentityFileBackend;
174
+ }
175
+ /**
176
+ * Default file backend — uses `node:fs/promises` with the canonical
177
+ * tmp-then-rename atomic-write pattern.
178
+ */
179
+ declare class FsIdentityFileBackend implements IdentityFileBackend {
180
+ exists(filePath: string): Promise<boolean>;
181
+ read(filePath: string): Promise<PersistedDeviceIdentity | null>;
182
+ write(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult>;
183
+ delete(filePath: string): Promise<void>;
184
+ }
185
+ declare class IdentityStore {
186
+ private readonly filePath;
187
+ private readonly backend;
188
+ private readonly hooks;
189
+ private readonly lockTimeoutMs;
190
+ private readonly lockRetryMs;
191
+ private readonly now;
192
+ constructor(opts?: IdentityStoreOptions);
193
+ /** Absolute path to the underlying JSON file (test seam). */
194
+ getFilePath(): string;
195
+ /** True when the JSON file already exists on disk. */
196
+ exists(): Promise<boolean>;
197
+ /** Read the persisted identity; returns `null` when no identity is stored. */
198
+ read(): Promise<PersistedDeviceIdentity | null>;
199
+ /**
200
+ * Atomically write the given identity. Concurrent writers are
201
+ * serialized via the file lock; the read-modify-write happens
202
+ * inside the lock so callers can't see a partial state.
203
+ */
204
+ write(next: PersistedDeviceIdentity): Promise<AtomicWriteResult>;
205
+ /**
206
+ * Read-modify-write under the same lock. The mutator receives the
207
+ * current identity (or `null` on first call) and returns the
208
+ * replacement. Throwing inside the mutator aborts the write.
209
+ */
210
+ mutate<T>(mutator: (current: PersistedDeviceIdentity | null) => Promise<{
211
+ next: PersistedDeviceIdentity;
212
+ result?: T;
213
+ }>): Promise<{
214
+ result: T | undefined;
215
+ written: PersistedDeviceIdentity;
216
+ }>;
217
+ /** Wipe the persisted identity (used by `device.enroll --force`). */
218
+ clear(): Promise<void>;
219
+ /**
220
+ * Resolve the installation metadata for the current machine.
221
+ * Pure helper — no I/O, just `os.*` calls.
222
+ */
223
+ resolveInstallationMetadata(): Pick<PersistedDeviceIdentity, "installationId" | "machineId" | "platform">;
224
+ /**
225
+ * Ensure the parent directory exists (`~/.serviceme/`). Idempotent.
226
+ * Useful when the bootstrap phase5 placeholder wasn't run yet.
227
+ */
228
+ ensureHome(): Promise<void>;
229
+ }
230
+
231
+ /**
232
+ * Enroller — State machine for `device.enroll` and `device.rotate-secret`.
233
+ *
234
+ * States per `2.需求澄清.md` §1.2:
235
+ * anonymous → pending → claimed → expired
236
+ *
237
+ * - `anonymous` (initial): no device has ever enrolled. Server returns
238
+ * a fresh `publicId` + secret.
239
+ * - `pending`: enrollment HTTP call has been issued but the server
240
+ * hasn't confirmed yet. In-flight state — never persisted.
241
+ * - `claimed`: user has linked this device to their account (via
242
+ * `/api/v1/devices/claim`). Sticky binding locks future re-enrolls
243
+ * to the same `userId` (server-side matrix).
244
+ * - `expired`: server returned a device-expiry error. Forces a fresh
245
+ * enroll on next call.
246
+ *
247
+ * `--force` semantics: any non-anonymous state can be force-reset to
248
+ * `anonymous` by wiping the local identity file. The next enroll will
249
+ * be treated as a brand-new install by the server (no sticky binding).
250
+ *
251
+ * The Enroller is the **state machine**; the actual HTTP I/O is the
252
+ * caller's responsibility (the `DeviceSyncClient` in Phase 5.4 wires
253
+ * the server). This split keeps the Enroller unit-testable without
254
+ * a live server.
255
+ *
256
+ * Refs:
257
+ * - 4.功能规划.md §2.2 — `Enroller.ts anonymous → pending → claimed → expired`
258
+ * - `2.需求澄清.md` §1.2 — binding-state machine
259
+ */
260
+
261
+ interface EnrollerOptions {
262
+ identityStore: IdentityStore;
263
+ /** Injectable clock for deterministic tests. */
264
+ now?: () => Date;
265
+ /** Override the random source (tests). */
266
+ randomBytes?: (size: number) => Buffer;
267
+ /** Caller-supplied enroll HTTP function. Phase 5.4 wires the real one. */
268
+ enrollRequest?: EnrollRequestFn;
269
+ }
270
+ type EnrollRequestFn = (input: {
271
+ installationId: string;
272
+ machineId: string;
273
+ platform: string;
274
+ existing: PersistedDeviceIdentity | null;
275
+ force: boolean;
276
+ requireAuth: boolean;
277
+ }) => Promise<EnrollResponse>;
278
+ interface EnrollResponse {
279
+ publicId: string;
280
+ deviceSecret: string;
281
+ secretVersion: number;
282
+ bindingState: DeviceBindingState;
283
+ expiresAt?: string;
284
+ }
285
+ /** Sentinel error — re-enroll on a claimed device without auth. */
286
+ declare class DeviceReenrollRequiresAuthError extends Error {
287
+ constructor(message?: string);
288
+ }
289
+ /** Sentinel error — server returned a 410 / version-mismatch after rotation. */
290
+ declare class DeviceSecretVersionMismatchError extends Error {
291
+ constructor(message?: string);
292
+ }
293
+ declare class Enroller {
294
+ private readonly identity;
295
+ private readonly now;
296
+ private readonly random;
297
+ private readonly enrollRequest?;
298
+ private inflight;
299
+ constructor(opts: EnrollerOptions);
300
+ /**
301
+ * Read the current binding state without touching the disk.
302
+ * Returns `anonymous` when no identity is stored.
303
+ */
304
+ currentState(): Promise<DeviceBindingState>;
305
+ /**
306
+ * Drive the enrollment flow.
307
+ *
308
+ * @param force when true, drop the local identity and start fresh
309
+ * (server treats this as a brand-new install).
310
+ * @param requireAuth when true, refuse to silently re-enroll an
311
+ * existing claimed device — throw
312
+ * `DeviceReenrollRequiresAuthError` instead.
313
+ */
314
+ /**
315
+ * Resolve when any in-flight enrollment completes. Returns immediately
316
+ * when no enrollment is in progress. Allows callers (e.g. the extension's
317
+ * `buildDeviceAuthHeaders`) to wait for a concurrent `syncDeviceInfo()`
318
+ * enrollment before attempting to read the identity from the store.
319
+ */
320
+ waitForEnrollment(): Promise<void>;
321
+ enroll(opts?: {
322
+ force?: boolean;
323
+ requireAuth?: boolean;
324
+ }): Promise<DeviceEnrollResult>;
325
+ /** Test seam — surface the underlying identity store. */
326
+ getIdentityStore(): IdentityStore;
327
+ /** True when an enrollment is currently in-flight. Used by callers (e.g. the extension's `buildDeviceAuthHeaders`) to skip triggering a competing enrollment. */
328
+ isEnrolling(): boolean;
329
+ private runEnroll;
330
+ /**
331
+ * Rotate the HMAC secret. Keeps the previous secret for the grace
332
+ * window (default 7 days per `2.需求澄清.md` §1.2) — the
333
+ * `previousSecretExpiresAt` is stamped on the persisted identity.
334
+ */
335
+ rotateSecret(opts?: {
336
+ gracePeriodDays?: number;
337
+ }): Promise<{
338
+ publicId: string;
339
+ secretVersion: number;
340
+ gracePeriodDays: number;
341
+ }>;
342
+ /**
343
+ * Mark the device as `expired`. Used when the server returns a
344
+ * device-expiry response; the next `enroll()` call forces a fresh
345
+ * round-trip.
346
+ */
347
+ markExpired(): Promise<void>;
348
+ /**
349
+ * Mark the device as `claimed`. Called by the bridge after a
350
+ * successful `device.claim` server response.
351
+ */
352
+ markClaimed(): Promise<void>;
353
+ }
354
+
355
+ /**
356
+ * DeviceCore — Main entry for the device domain.
357
+ *
358
+ * Aggregates `IdentityStore` + `Enroller` + signer helpers into a single
359
+ * surface that the CLI / Extension / Bridge can call. Pure orchestration
360
+ * — no HTTP of its own (the actual `POST /api/v1/devices/enroll` lives
361
+ * behind `EnrollerOptions.enrollRequest`, wired in Phase 5.4).
362
+ *
363
+ * Refs:
364
+ * - 4.功能规划.md §2.2 — `DeviceCore.ts 主入口`
365
+ * - ADL-003 — data model in `@serviceme/devtools-protocol`
366
+ * - `docs/architecture/phase-5-device-header-spec.md` §3.1
367
+ */
368
+
369
+ interface DeviceCoreOptions {
370
+ identityStore?: IdentityStore;
371
+ enrollRequest?: EnrollRequestFn;
372
+ now?: () => Date;
373
+ /** Optional override for `deriveInstallationId` (used by tests for determinism). */
374
+ resolveInstallationId?: () => string;
375
+ }
376
+ declare class DeviceCore {
377
+ private readonly identity;
378
+ private readonly enroller;
379
+ private readonly resolveInstallationId;
380
+ constructor(opts?: DeviceCoreOptions);
381
+ /** Read-only snapshot of the device status (matches `device.status` wire shape). */
382
+ status(): Promise<DeviceStatus>;
383
+ /** Enroll (or re-enroll) the device. */
384
+ enroll(opts?: {
385
+ force?: boolean;
386
+ requireAuth?: boolean;
387
+ }): Promise<DeviceEnrollResult>;
388
+ /** Wait for any in-flight enrollment to finish. Use before `buildSignedHeaders` so that a concurrent `syncDeviceInfo` enrollment has time to write the identity to the store. */
389
+ waitForEnrollment(): Promise<void>;
390
+ /** True when an enrollment is currently in-flight. Used by callers to skip triggering a competing enrollment. */
391
+ isEnrolling(): boolean;
392
+ /** Rotate the HMAC secret while keeping the previous one for the grace window. */
393
+ rotateSecret(opts?: {
394
+ gracePeriodDays?: number;
395
+ }): Promise<DeviceRotateSecretResult>;
396
+ /** Build the canonical 5-header map for an outbound signed request. */
397
+ buildSignedHeaders(input: {
398
+ method: string;
399
+ path: string;
400
+ body: string;
401
+ }): Promise<DeviceSignedHeaders | null>;
402
+ /** Raw stored identity (CLI/extension internal use). Test seam too. */
403
+ readIdentity(): Promise<PersistedDeviceIdentity | null>;
404
+ /** Wipe the local identity (the `--force` path before re-enroll). */
405
+ clear(): Promise<void>;
406
+ /** Mark the device as claimed (called by the bridge after a successful claim). */
407
+ markClaimed(): Promise<void>;
408
+ /** Mark the device as expired (server returned an expiry response). */
409
+ markExpired(): Promise<void>;
410
+ /** Expose the identity store (CLI uses it for direct file access in tests). */
411
+ getIdentityStore(): IdentityStore;
412
+ /** Expose the enroller (CLI uses it for state inspection). */
413
+ getEnroller(): Enroller;
414
+ /** Header name constants — re-exported from `deviceAuth.ts`. */
415
+ getHeaderNames(): typeof DeviceAuthHeaders;
416
+ /** Compute the installation id for the current machine. */
417
+ getInstallationId(): string;
418
+ }
419
+
420
+ /**
421
+ * InstallationId — Derive a stable per-machine identifier from
422
+ * `os.hostname()` + `os.userInfo()`.
423
+ *
424
+ * Per `docs/requirements/phase-5-auth-device-toolbox/3.功能拆分.md` B2,
425
+ * `installationId` MUST survive Extension re-installs but vary across
426
+ * machines. We compute a UUID v5-style hash over hostname + username +
427
+ * platform so the result is:
428
+ * - deterministic (same machine → same id)
429
+ * - collision-resistant (SHA-256, 128-bit truncated)
430
+ * - browser-safe (no PII survives — username never enters output)
431
+ *
432
+ * Note: this intentionally differs from `vscode.env.machineId`, which
433
+ * is per-Extension-install and uses a different algorithm. The two
434
+ * coexist: `installationId` is what gets sent to the server, while
435
+ * `machineId` (raw `os.hostname()`) is for diagnostics.
436
+ *
437
+ * Refs:
438
+ * - 4.功能规划.md §2.2 — `InstallationId.ts os.hostname() + os.userInfo() 哈希生成`
439
+ * - 3.功能拆分.md B2 — installationId semantics
440
+ */
441
+ /**
442
+ * Returns a deterministic installation id for the current machine.
443
+ * Use this when you need an id that survives Extension reinstalls
444
+ * but stays stable across restarts on the same machine.
445
+ */
446
+ declare function deriveInstallationId(): string;
447
+ /**
448
+ * Returns a random installation id (UUID v4). Use this for fresh
449
+ * installs when no fingerprint input is available (e.g. containerized
450
+ * CI runners where `os.hostname()` is meaningless).
451
+ */
452
+ declare function randomInstallationId(): string;
453
+ /** SHA-256 fingerprint material exposed for tests + diagnostics. */
454
+ declare function fingerprintSource(): string;
455
+
456
+ export { type AtomicWriteResult, type BuildSignedHeadersParams, DEVICE_JSON_SCHEMA_VERSION, DeviceAuthHeaders, DeviceCore, type DeviceCoreOptions, DeviceReenrollRequiresAuthError, type DeviceRequestSignatureParams, DeviceSecretVersionMismatchError, type DeviceSignedHeaders, type EnrollRequestFn, type EnrollResponse, Enroller, type EnrollerOptions, FsIdentityFileBackend, type IdentityFileBackend, IdentityStore, type IdentityStoreHooks, type IdentityStoreOptions, type PersistedDeviceIdentity, buildSignedHeaders, createDeviceRequestSignature, deriveInstallationId, fingerprintSource, randomInstallationId };