@serviceme/devtools-core 2.0.0 → 2.0.2

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 (82) hide show
  1. package/dist/auth-Bzss8zOv.mjs +995 -0
  2. package/dist/auth-Bzss8zOv.mjs.map +1 -0
  3. package/dist/auth-DXam3c4S.js +1078 -0
  4. package/dist/auth-DXam3c4S.js.map +1 -0
  5. package/dist/auth.d.mts +2 -2
  6. package/dist/auth.d.ts +2 -2
  7. package/dist/auth.js +14 -1029
  8. package/dist/auth.mjs +2 -991
  9. package/dist/device-CDDcwDY6.mjs +758 -0
  10. package/dist/device-CDDcwDY6.mjs.map +1 -0
  11. package/dist/device-DuAYH8bk.js +839 -0
  12. package/dist/device-DuAYH8bk.js.map +1 -0
  13. package/dist/device.d.mts +2 -455
  14. package/dist/device.d.ts +2 -455
  15. package/dist/device.js +15 -728
  16. package/dist/device.mjs +2 -679
  17. package/dist/index-2rX86yMq.d.mts +331 -0
  18. package/dist/index-BI11AfMH.d.mts +377 -0
  19. package/dist/index-BI11AfMH.d.ts +377 -0
  20. package/dist/index-CoA6y0MP.d.mts +498 -0
  21. package/dist/index-CoA6y0MP.d.ts +498 -0
  22. package/dist/index-DaW7IYA-.d.mts +185 -0
  23. package/dist/index-DaW7IYA-.d.ts +185 -0
  24. package/dist/index-Dp6FwWKh.d.ts +331 -0
  25. package/dist/index.d.mts +2340 -2101
  26. package/dist/index.d.ts +2340 -2101
  27. package/dist/index.js +8117 -10538
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +7895 -10318
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/rolldown-runtime-VH7oDXx4.js +28 -0
  32. package/dist/skill-linker.d.mts +82 -81
  33. package/dist/skill-linker.d.ts +82 -81
  34. package/dist/skill-linker.js +359 -402
  35. package/dist/skill-linker.js.map +1 -1
  36. package/dist/skill-linker.mjs +345 -357
  37. package/dist/skill-linker.mjs.map +1 -1
  38. package/dist/skill-store-Bym2yinU.mjs +388 -0
  39. package/dist/skill-store-Bym2yinU.mjs.map +1 -0
  40. package/dist/skill-store-CJdYyyxu.js +408 -0
  41. package/dist/skill-store-CJdYyyxu.js.map +1 -0
  42. package/dist/skill-store.d.mts +41 -42
  43. package/dist/skill-store.d.ts +41 -42
  44. package/dist/skill-store.js +5 -338
  45. package/dist/skill-store.mjs +2 -301
  46. package/dist/submit-Cg7wSa3x.mjs +84 -0
  47. package/dist/submit-Cg7wSa3x.mjs.map +1 -0
  48. package/dist/submit-CltE6YOZ.js +98 -0
  49. package/dist/submit-CltE6YOZ.js.map +1 -0
  50. package/dist/submit.d.mts +2 -3
  51. package/dist/submit.d.ts +2 -3
  52. package/dist/submit.js +4 -166
  53. package/dist/submit.mjs +2 -130
  54. package/dist/toolbox-CbfxpNg-.mjs +540 -0
  55. package/dist/toolbox-CbfxpNg-.mjs.map +1 -0
  56. package/dist/toolbox-CxnOr-FY.js +614 -0
  57. package/dist/toolbox-CxnOr-FY.js.map +1 -0
  58. package/dist/toolbox.d.mts +2 -244
  59. package/dist/toolbox.d.ts +2 -244
  60. package/dist/toolbox.js +14 -584
  61. package/dist/toolbox.mjs +2 -536
  62. package/dist/{types-B9gk3dXH.d.ts → types-BCV3IedE.d.mts} +25 -23
  63. package/dist/{types-B9gk3dXH.d.mts → types-BCV3IedE.d.ts} +25 -23
  64. package/dist/userHome-BquDeFVE.js +526 -0
  65. package/dist/userHome-BquDeFVE.js.map +1 -0
  66. package/dist/userHome-CcnAQPG1.mjs +212 -0
  67. package/dist/userHome-CcnAQPG1.mjs.map +1 -0
  68. package/package.json +10 -10
  69. package/dist/auth.js.map +0 -1
  70. package/dist/auth.mjs.map +0 -1
  71. package/dist/device.js.map +0 -1
  72. package/dist/device.mjs.map +0 -1
  73. package/dist/index-BMk4tqIT.d.mts +0 -328
  74. package/dist/index-CymN0x9Z.d.ts +0 -328
  75. package/dist/index-PD135hlB.d.mts +0 -626
  76. package/dist/index-PD135hlB.d.ts +0 -626
  77. package/dist/skill-store.js.map +0 -1
  78. package/dist/skill-store.mjs.map +0 -1
  79. package/dist/submit.js.map +0 -1
  80. package/dist/submit.mjs.map +0 -1
  81. package/dist/toolbox.js.map +0 -1
  82. package/dist/toolbox.mjs.map +0 -1
@@ -0,0 +1,377 @@
1
+ import { DeviceBindingState, DeviceEnrollResult, DeviceRotateSecretResult, DeviceStatus } from "@serviceme/devtools-protocol";
2
+ //#region src/device/deviceAuth.d.ts
3
+ /**
4
+ * deviceAuth — Pure HMAC-SHA-256 signing helpers for device auth headers.
5
+ *
6
+ * **Boundary exception copy.** The single source of truth for the
7
+ * `x-ms-device-*` header names + the signature algorithm now lives in
8
+ * `@serviceme/devtools-shared` (`device-auth.ts`), consumed by the
9
+ * extension signer and the server verifier. ADL-003 forbids
10
+ * core → shared (and shared → core), so this module keeps a
11
+ * byte-for-byte copy as the documented exception. Keep it in lock-step
12
+ * with `packages/serviceme-shared/src/device-auth.ts`.
13
+ *
14
+ * See `docs/architecture/phase-5-device-header-spec.md` §5 for the wire
15
+ * format. The basis is `METHOD\nPATH\nTIMESTAMP\nBODY\nSECRET`
16
+ * (LF-joined, NOT JSON). Output is lowercase hex SHA-256.
17
+ */
18
+ /** Canonical header names — MUST match `@serviceme/devtools-shared`'s `DeviceAuthHeaders`. */
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
+ //#endregion
66
+ //#region src/device/types.d.ts
67
+ /**
68
+ * Schema version of the on-disk `device.json` file. Bumped when the
69
+ * shape changes incompatibly. IdentityStore checks this on read and
70
+ * either migrates (versions ≤ 1) or refuses (versions > supported).
71
+ */
72
+ declare const DEVICE_JSON_SCHEMA_VERSION = 1;
73
+ /** Internal representation of the persisted identity file. */
74
+ interface PersistedDeviceIdentity {
75
+ version: number;
76
+ /** Stable per-machine id (UUID v4 shape) — survives secret rotates. */
77
+ installationId: string;
78
+ /** Raw `os.hostname()` for diagnostics. */
79
+ machineId: string;
80
+ /** Platform string (e.g. "darwin"). */
81
+ platform: string;
82
+ /** Optional hostname override for environments where `os.hostname()` is unstable. */
83
+ hostname?: string;
84
+ /** Public, non-secret id returned by the server. 32-char hex. */
85
+ publicId: string;
86
+ /** Monotonic secret version counter, starts at 1 after first enroll. */
87
+ secretVersion: number;
88
+ /** Current binding state — drives the re-enroll matrix. */
89
+ bindingState: DeviceBindingState;
90
+ /** HMAC secret (32 bytes hex-encoded = 64 chars). Persisted per `device-header-spec.md` §3.1. */
91
+ deviceSecret: string;
92
+ /** Optional: previous secret retained during the grace window (rotation). */
93
+ previousDeviceSecret?: string;
94
+ /** Optional: ISO timestamp at which the previous secret stops being accepted. */
95
+ previousSecretExpiresAt?: string;
96
+ /** ISO timestamp of the most recent successful enroll / rotate. */
97
+ lastEnrollAt: string;
98
+ /** Optional ISO timestamp of the most recent server sync. */
99
+ lastSyncAt?: string;
100
+ /** Optional human-readable message for the last sync error. */
101
+ lastSyncError?: string;
102
+ }
103
+ /** Result of a single atomic write. */
104
+ interface AtomicWriteResult {
105
+ bytesWritten: number;
106
+ /** Path to the temp file (post-rename it no longer exists; useful for diagnostics). */
107
+ tmpPath: string;
108
+ }
109
+ /** Hook called before/after every identity write — used by tests to assert concurrency safety. */
110
+ interface IdentityStoreHooks {
111
+ beforeWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;
112
+ afterWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;
113
+ }
114
+ //#endregion
115
+ //#region src/device/IdentityStore.d.ts
116
+ /**
117
+ * Minimal interface for reading + writing the persisted identity file.
118
+ * Default impl uses `getDeviceJsonPath()` (which honors `SERVICEME_HOME`),
119
+ * but tests can substitute a custom path for isolation.
120
+ */
121
+ interface IdentityFileBackend {
122
+ read(filePath: string): Promise<PersistedDeviceIdentity | null>;
123
+ write(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult>;
124
+ exists(filePath: string): Promise<boolean>;
125
+ delete(filePath: string): Promise<void>;
126
+ listDir?(dir: string): Promise<string[]>;
127
+ }
128
+ interface IdentityStoreOptions {
129
+ filePath?: string;
130
+ hooks?: IdentityStoreHooks;
131
+ lockTimeoutMs?: number;
132
+ lockRetryMs?: number;
133
+ /** Injectable clock for deterministic tests. */
134
+ now?: () => Date;
135
+ backend?: IdentityFileBackend;
136
+ }
137
+ /**
138
+ * Default file backend — uses `node:fs/promises` with the canonical
139
+ * tmp-then-rename atomic-write pattern.
140
+ */
141
+ declare class FsIdentityFileBackend implements IdentityFileBackend {
142
+ exists(filePath: string): Promise<boolean>;
143
+ read(filePath: string): Promise<PersistedDeviceIdentity | null>;
144
+ write(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult>;
145
+ delete(filePath: string): Promise<void>;
146
+ }
147
+ declare class IdentityStore {
148
+ private readonly filePath;
149
+ private readonly backend;
150
+ private readonly hooks;
151
+ private readonly lockTimeoutMs;
152
+ private readonly lockRetryMs;
153
+ constructor(opts?: IdentityStoreOptions);
154
+ /** Absolute path to the underlying JSON file (test seam). */
155
+ getFilePath(): string;
156
+ /** True when the JSON file already exists on disk. */
157
+ exists(): Promise<boolean>;
158
+ /** Read the persisted identity; returns `null` when no identity is stored. */
159
+ read(): Promise<PersistedDeviceIdentity | null>;
160
+ /**
161
+ * Atomically write the given identity. Concurrent writers are
162
+ * serialized via the file lock; the read-modify-write happens
163
+ * inside the lock so callers can't see a partial state.
164
+ */
165
+ write(next: PersistedDeviceIdentity): Promise<AtomicWriteResult>;
166
+ /**
167
+ * Read-modify-write under the same lock. The mutator receives the
168
+ * current identity (or `null` on first call) and returns the
169
+ * replacement. Throwing inside the mutator aborts the write.
170
+ */
171
+ mutate<T>(mutator: (current: PersistedDeviceIdentity | null) => Promise<{
172
+ next: PersistedDeviceIdentity;
173
+ result?: T;
174
+ }>): Promise<{
175
+ result: T | undefined;
176
+ written: PersistedDeviceIdentity;
177
+ }>;
178
+ /** Wipe the persisted identity (used by `device.enroll --force`). */
179
+ clear(): Promise<void>;
180
+ /**
181
+ * Resolve the installation metadata for the current machine.
182
+ * Pure helper — no I/O, just `os.*` calls.
183
+ */
184
+ resolveInstallationMetadata(): Pick<PersistedDeviceIdentity, "installationId" | "machineId" | "platform">;
185
+ /**
186
+ * Ensure the parent directory exists (`~/.serviceme/`). Idempotent.
187
+ * Useful when the bootstrap phase5 placeholder wasn't run yet.
188
+ */
189
+ ensureHome(): Promise<void>;
190
+ }
191
+ //#endregion
192
+ //#region src/device/Enroller.d.ts
193
+ interface EnrollerOptions {
194
+ identityStore: IdentityStore;
195
+ /** Injectable clock for deterministic tests. */
196
+ now?: () => Date;
197
+ /** Override the random source (tests). */
198
+ randomBytes?: (size: number) => Buffer;
199
+ /** Caller-supplied enroll HTTP function. Phase 5.4 wires the real one. */
200
+ enrollRequest?: EnrollRequestFn;
201
+ }
202
+ type EnrollRequestFn = (input: {
203
+ installationId: string;
204
+ machineId: string;
205
+ platform: string;
206
+ existing: PersistedDeviceIdentity | null;
207
+ force: boolean;
208
+ requireAuth: boolean;
209
+ }) => Promise<EnrollResponse>;
210
+ interface EnrollResponse {
211
+ publicId: string;
212
+ deviceSecret: string;
213
+ secretVersion: number;
214
+ bindingState: DeviceBindingState;
215
+ expiresAt?: string;
216
+ }
217
+ /** Sentinel error — re-enroll on a claimed device without auth. */
218
+ declare class DeviceReenrollRequiresAuthError extends Error {
219
+ constructor(message?: string);
220
+ }
221
+ /** Sentinel error — server returned a 410 / version-mismatch after rotation. */
222
+ declare class DeviceSecretVersionMismatchError extends Error {
223
+ constructor(message?: string);
224
+ }
225
+ declare class Enroller {
226
+ private readonly identity;
227
+ private readonly now;
228
+ private readonly random;
229
+ private readonly enrollRequest?;
230
+ private inflight;
231
+ constructor(opts: EnrollerOptions);
232
+ /**
233
+ * Read the current binding state without touching the disk.
234
+ * Returns `anonymous` when no identity is stored.
235
+ */
236
+ currentState(): Promise<DeviceBindingState>;
237
+ /**
238
+ * Drive the enrollment flow.
239
+ *
240
+ * @param force when true, drop the local identity and start fresh
241
+ * (server treats this as a brand-new install).
242
+ * @param requireAuth when true, refuse to silently re-enroll an
243
+ * existing claimed device — throw
244
+ * `DeviceReenrollRequiresAuthError` instead.
245
+ */
246
+ /**
247
+ * Resolve when any in-flight enrollment completes. Returns immediately
248
+ * when no enrollment is in progress. Allows callers (e.g. the extension's
249
+ * `buildDeviceAuthHeaders`) to wait for a concurrent `syncDeviceInfo()`
250
+ * enrollment before attempting to read the identity from the store.
251
+ */
252
+ waitForEnrollment(): Promise<void>;
253
+ enroll(opts?: {
254
+ force?: boolean;
255
+ requireAuth?: boolean;
256
+ }): Promise<DeviceEnrollResult>;
257
+ /** Test seam — surface the underlying identity store. */
258
+ getIdentityStore(): IdentityStore;
259
+ /** True when an enrollment is currently in-flight. Used by callers (e.g. the extension's `buildDeviceAuthHeaders`) to skip triggering a competing enrollment. */
260
+ isEnrolling(): boolean;
261
+ private runEnroll;
262
+ /**
263
+ * Rotate the HMAC secret. Keeps the previous secret for the grace
264
+ * window (default 7 days per `2.需求澄清.md` §1.2) — the
265
+ * `previousSecretExpiresAt` is stamped on the persisted identity.
266
+ */
267
+ rotateSecret(opts?: {
268
+ gracePeriodDays?: number;
269
+ }): Promise<{
270
+ publicId: string;
271
+ secretVersion: number;
272
+ gracePeriodDays: number;
273
+ }>;
274
+ /**
275
+ * Mark the device as `expired`. Used when the server returns a
276
+ * device-expiry response; the next `enroll()` call forces a fresh
277
+ * round-trip.
278
+ */
279
+ markExpired(): Promise<void>;
280
+ /**
281
+ * Mark the device as `claimed`. Called by the bridge after a
282
+ * successful `device.claim` server response.
283
+ */
284
+ markClaimed(): Promise<void>;
285
+ }
286
+ //#endregion
287
+ //#region src/device/DeviceCore.d.ts
288
+ interface DeviceCoreOptions {
289
+ identityStore?: IdentityStore;
290
+ enrollRequest?: EnrollRequestFn;
291
+ now?: () => Date;
292
+ /** Optional override for `deriveInstallationId` (used by tests for determinism). */
293
+ resolveInstallationId?: () => string;
294
+ }
295
+ declare class DeviceCore {
296
+ private readonly identity;
297
+ private readonly enroller;
298
+ private readonly resolveInstallationId;
299
+ constructor(opts?: DeviceCoreOptions);
300
+ /** Read-only snapshot of the device status (matches `device.status` wire shape). */
301
+ status(): Promise<DeviceStatus>;
302
+ /** Enroll (or re-enroll) the device. */
303
+ enroll(opts?: {
304
+ force?: boolean;
305
+ requireAuth?: boolean;
306
+ }): Promise<DeviceEnrollResult>;
307
+ /** 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. */
308
+ waitForEnrollment(): Promise<void>;
309
+ /** True when an enrollment is currently in-flight. Used by callers to skip triggering a competing enrollment. */
310
+ isEnrolling(): boolean;
311
+ /** Rotate the HMAC secret while keeping the previous one for the grace window. */
312
+ rotateSecret(opts?: {
313
+ gracePeriodDays?: number;
314
+ }): Promise<DeviceRotateSecretResult>;
315
+ /** Build the canonical 5-header map for an outbound signed request. */
316
+ buildSignedHeaders(input: {
317
+ method: string;
318
+ path: string;
319
+ body: string;
320
+ }): Promise<DeviceSignedHeaders | null>;
321
+ /** Raw stored identity (CLI/extension internal use). Test seam too. */
322
+ readIdentity(): Promise<PersistedDeviceIdentity | null>;
323
+ /** Wipe the local identity (the `--force` path before re-enroll). */
324
+ clear(): Promise<void>;
325
+ /** Mark the device as claimed (called by the bridge after a successful claim). */
326
+ markClaimed(): Promise<void>;
327
+ /** Mark the device as expired (server returned an expiry response). */
328
+ markExpired(): Promise<void>;
329
+ /** Expose the identity store (CLI uses it for direct file access in tests). */
330
+ getIdentityStore(): IdentityStore;
331
+ /** Expose the enroller (CLI uses it for state inspection). */
332
+ getEnroller(): Enroller;
333
+ /** Header name constants — re-exported from `deviceAuth.ts`. */
334
+ getHeaderNames(): typeof DeviceAuthHeaders;
335
+ /** Compute the installation id for the current machine. */
336
+ getInstallationId(): string;
337
+ }
338
+ //#endregion
339
+ //#region src/device/InstallationId.d.ts
340
+ /**
341
+ * InstallationId — Derive a stable per-machine identifier from
342
+ * `os.hostname()` + `os.userInfo()`.
343
+ *
344
+ * Per `docs/architecture/phase-5-auth-device-toolbox.md § P5-2 (并入本文时对应 § P5-2 拆分)` B2,
345
+ * `installationId` MUST survive Extension re-installs but vary across
346
+ * machines. We compute a UUID v5-style hash over hostname + username +
347
+ * platform so the result is:
348
+ * - deterministic (same machine → same id)
349
+ * - collision-resistant (SHA-256, 128-bit truncated)
350
+ * - browser-safe (no PII survives — username never enters output)
351
+ *
352
+ * Note: this intentionally differs from `vscode.env.machineId`, which
353
+ * is per-Extension-install and uses a different algorithm. The two
354
+ * coexist: `installationId` is what gets sent to the server, while
355
+ * `machineId` (raw `os.hostname()`) is for diagnostics.
356
+ *
357
+ * Refs:
358
+ * - 4.功能规划.md §2.2 — `InstallationId.ts os.hostname() + os.userInfo() 哈希生成`
359
+ * - 3.功能拆分.md B2 — installationId semantics
360
+ */
361
+ /**
362
+ * Returns a deterministic installation id for the current machine.
363
+ * Use this when you need an id that survives Extension reinstalls
364
+ * but stays stable across restarts on the same machine.
365
+ */
366
+ declare function deriveInstallationId(): string;
367
+ /**
368
+ * Returns a random installation id (UUID v4). Use this for fresh
369
+ * installs when no fingerprint input is available (e.g. containerized
370
+ * CI runners where `os.hostname()` is meaningless).
371
+ */
372
+ declare function randomInstallationId(): string;
373
+ /** SHA-256 fingerprint material exposed for tests + diagnostics. */
374
+ declare function fingerprintSource(): string;
375
+ //#endregion
376
+ export { DeviceSignedHeaders as C, DeviceRequestSignatureParams as S, createDeviceRequestSignature as T, DEVICE_JSON_SCHEMA_VERSION as _, DeviceCoreOptions as a, BuildSignedHeadersParams as b, EnrollRequestFn as c, EnrollerOptions as d, FsIdentityFileBackend as f, AtomicWriteResult as g, IdentityStoreOptions as h, DeviceCore as i, EnrollResponse as l, IdentityStore as m, fingerprintSource as n, DeviceReenrollRequiresAuthError as o, IdentityFileBackend as p, randomInstallationId as r, DeviceSecretVersionMismatchError as s, deriveInstallationId as t, Enroller as u, IdentityStoreHooks as v, buildSignedHeaders as w, DeviceAuthHeaders as x, PersistedDeviceIdentity as y };
377
+ //# sourceMappingURL=index-BI11AfMH.d.mts.map