@rynx-ai/daemon 0.1.0 → 0.1.9

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 (89) hide show
  1. package/dist/browser-cli-args.d.ts +20 -0
  2. package/dist/browser-cli-args.js +100 -0
  3. package/dist/browser-runner-session-context.d.ts +24 -0
  4. package/dist/browser-runner-session-context.js +113 -0
  5. package/dist/cdp-keyboard-input.d.ts +6 -0
  6. package/dist/cdp-keyboard-input.js +209 -0
  7. package/dist/channel-store.d.ts +0 -6
  8. package/dist/channel-store.js +2 -18
  9. package/dist/chrome-for-testing-release-resolver.d.ts +34 -0
  10. package/dist/chrome-for-testing-release-resolver.js +214 -0
  11. package/dist/chrome-for-testing-store.d.ts +85 -0
  12. package/dist/chrome-for-testing-store.js +836 -0
  13. package/dist/cli.js +467 -30
  14. package/dist/control-client.d.ts +113 -0
  15. package/dist/control-client.js +792 -0
  16. package/dist/control-deps.js +37 -6
  17. package/dist/control-endpoint.d.ts +14 -0
  18. package/dist/control-endpoint.js +124 -0
  19. package/dist/control-link-store.d.ts +16 -0
  20. package/dist/control-link-store.js +53 -0
  21. package/dist/daemon-owner.d.ts +14 -0
  22. package/dist/daemon-owner.js +104 -0
  23. package/dist/daemon-server.d.ts +12 -2
  24. package/dist/daemon-server.js +559 -28
  25. package/dist/db.js +140 -10
  26. package/dist/desktop-browser-host-client.d.ts +34 -0
  27. package/dist/desktop-browser-host-client.js +413 -0
  28. package/dist/direct-runtime-authenticator.d.ts +13 -0
  29. package/dist/direct-runtime-authenticator.js +127 -0
  30. package/dist/direct-runtime-config.d.ts +20 -0
  31. package/dist/direct-runtime-config.js +101 -0
  32. package/dist/embedded-browser-profile-cleanup.d.ts +58 -0
  33. package/dist/embedded-browser-profile-cleanup.js +338 -0
  34. package/dist/headless-browser-host.d.ts +14 -0
  35. package/dist/headless-browser-host.js +2156 -0
  36. package/dist/index-daemon.js +28 -5
  37. package/dist/installation-id.d.ts +33 -0
  38. package/dist/installation-id.js +692 -0
  39. package/dist/migrations/cleanup-legacy-sessions.d.ts +0 -3
  40. package/dist/migrations/cleanup-legacy-sessions.js +10 -28
  41. package/dist/plugin-cli.d.ts +30 -0
  42. package/dist/plugin-cli.js +270 -0
  43. package/dist/plugin-host-rpc.d.ts +85 -0
  44. package/dist/plugin-host-rpc.js +878 -0
  45. package/dist/plugin-install-restart.d.ts +13 -0
  46. package/dist/plugin-install-restart.js +24 -0
  47. package/dist/plugin-installer.d.ts +36 -15
  48. package/dist/plugin-installer.js +420 -155
  49. package/dist/plugin-package.d.ts +38 -0
  50. package/dist/plugin-package.js +553 -0
  51. package/dist/plugin-runtime-control.d.ts +2 -0
  52. package/dist/plugin-runtime-control.js +37 -0
  53. package/dist/plugin-store.d.ts +37 -8
  54. package/dist/plugin-store.js +80 -15
  55. package/dist/plugin-supervisor.d.ts +160 -0
  56. package/dist/plugin-supervisor.js +1143 -0
  57. package/dist/pm2.d.ts +3 -1
  58. package/dist/pm2.js +55 -13
  59. package/dist/provider-cli-service.d.ts +46 -0
  60. package/dist/provider-cli-service.js +510 -0
  61. package/dist/registry.d.ts +6 -19
  62. package/dist/registry.js +22 -87
  63. package/dist/remote-runtime-access-store.d.ts +61 -0
  64. package/dist/remote-runtime-access-store.js +386 -0
  65. package/dist/remote-runtime-admin.d.ts +32 -0
  66. package/dist/remote-runtime-admin.js +107 -0
  67. package/dist/remote-runtime-connection-manager.d.ts +92 -0
  68. package/dist/remote-runtime-connection-manager.js +1409 -0
  69. package/dist/remote-runtime-credential-store.d.ts +45 -0
  70. package/dist/remote-runtime-credential-store.js +639 -0
  71. package/dist/remote-runtime-target-control.d.ts +47 -0
  72. package/dist/remote-runtime-target-control.js +433 -0
  73. package/dist/remote-runtime-target-store.d.ts +53 -0
  74. package/dist/remote-runtime-target-store.js +307 -0
  75. package/dist/runtime-share-addresses.d.ts +21 -0
  76. package/dist/runtime-share-addresses.js +50 -0
  77. package/dist/session-browser-capability-store.d.ts +13 -0
  78. package/dist/session-browser-capability-store.js +58 -0
  79. package/dist/session-emulator-binding-store.d.ts +10 -0
  80. package/dist/session-emulator-binding-store.js +61 -0
  81. package/dist/session-launch-operations.d.ts +41 -0
  82. package/dist/session-launch-operations.js +124 -0
  83. package/dist/session-meta-store.d.ts +3 -2
  84. package/dist/session-meta-store.js +57 -7
  85. package/dist/setup.js +9 -4
  86. package/dist/skills-catalog.js +8 -4
  87. package/dist/update.d.ts +11 -2
  88. package/dist/update.js +0 -27
  89. package/package.json +18 -10
@@ -0,0 +1,47 @@
1
+ import { type PairingOffer } from "@rynx-ai/protocol/direct-runtime";
2
+ import type { DaemonStatus } from "@rynx-ai/protocol/remote-runtime";
3
+ import { connectDirectRuntime, pairDirectRuntime, type DirectRuntimeCredential, type PairDirectRuntimeOptions } from "@rynx-ai/remote-runtime-client";
4
+ import { type DaemonRuntimeHost, type RuntimeTargetControlHost, type RuntimeTargetView } from "@rynx-ai/server";
5
+ import { DaemonRuntimeConnectionManager } from "./remote-runtime-connection-manager.js";
6
+ import { FileRemoteRuntimeCredentialStore } from "./remote-runtime-credential-store.js";
7
+ import { RemoteRuntimeTargetStore } from "./remote-runtime-target-store.js";
8
+ export type RuntimeTargetControlErrorCode = "invalid_offer" | "invalid_target" | "not_found" | "conflict" | "tls_error" | "identity_mismatch" | "incompatible" | "authentication_failed" | "protocol_error" | "deadline_exceeded" | "outcome_unknown" | "unreachable";
9
+ export declare class RuntimeTargetControlError extends Error {
10
+ readonly code: RuntimeTargetControlErrorCode;
11
+ constructor(code: RuntimeTargetControlErrorCode, message: string, options?: ErrorOptions);
12
+ }
13
+ interface PairInput {
14
+ offer: unknown;
15
+ displayName?: string;
16
+ }
17
+ interface RuntimeTargetClientAdapter {
18
+ pair(offer: PairingOffer, options: PairDirectRuntimeOptions): ReturnType<typeof pairDirectRuntime>;
19
+ connect(credential: DirectRuntimeCredential): ReturnType<typeof connectDirectRuntime>;
20
+ }
21
+ /** Local resident-daemon application service backing CLI and browser BFF target management. */
22
+ export declare class DaemonRuntimeTargetControl implements RuntimeTargetControlHost {
23
+ private readonly localRuntime;
24
+ private readonly targets;
25
+ private readonly credentials;
26
+ private readonly connections;
27
+ private readonly client;
28
+ private pairing;
29
+ constructor(localRuntime: DaemonRuntimeHost, targets: RemoteRuntimeTargetStore, credentials: FileRemoteRuntimeCredentialStore, connections: DaemonRuntimeConnectionManager, client?: RuntimeTargetClientAdapter);
30
+ list(): RuntimeTargetView[];
31
+ pair(input: PairInput): Promise<{
32
+ target: RuntimeTargetView;
33
+ status: DaemonStatus;
34
+ }>;
35
+ private discardExpiredPending;
36
+ private resumePendingEnrollment;
37
+ private probe;
38
+ test(selector: string): Promise<{
39
+ target: RuntimeTargetView;
40
+ status: DaemonStatus;
41
+ }>;
42
+ forget(daemonId: string): Promise<{
43
+ forgotten: true;
44
+ }>;
45
+ private target;
46
+ }
47
+ export {};
@@ -0,0 +1,433 @@
1
+ import { createHash } from "node:crypto";
2
+ import { decodePairingCode, parsePairingOffer, } from "@rynx-ai/protocol/direct-runtime";
3
+ import { connectDirectRuntime, DirectRuntimeClientError, DirectRuntimeRpcError, generateDirectRuntimeClientIdentity, pairDirectRuntime, } from "@rynx-ai/remote-runtime-client";
4
+ import { RuntimeConnectionManagerError, } from "./remote-runtime-connection-manager.js";
5
+ import { RemoteRuntimeTargetConflictError, } from "./remote-runtime-target-store.js";
6
+ export class RuntimeTargetControlError extends Error {
7
+ code;
8
+ constructor(code, message, options) {
9
+ super(message, options);
10
+ this.code = code;
11
+ this.name = "RuntimeTargetControlError";
12
+ }
13
+ }
14
+ /** Local resident-daemon application service backing CLI and browser BFF target management. */
15
+ export class DaemonRuntimeTargetControl {
16
+ localRuntime;
17
+ targets;
18
+ credentials;
19
+ connections;
20
+ client;
21
+ pairing = false;
22
+ constructor(localRuntime, targets, credentials, connections, client = {
23
+ pair: (offer, options) => pairDirectRuntime(offer, options),
24
+ connect: (credential) => connectDirectRuntime(credential),
25
+ }) {
26
+ this.localRuntime = localRuntime;
27
+ this.targets = targets;
28
+ this.credentials = credentials;
29
+ this.connections = connections;
30
+ this.client = client;
31
+ }
32
+ list() {
33
+ const localStatus = this.localRuntime.status();
34
+ return [
35
+ {
36
+ selector: "local",
37
+ daemonId: localStatus.daemonId,
38
+ displayName: "This device",
39
+ binding: "local",
40
+ },
41
+ ...this.targets.list().map(targetView),
42
+ ];
43
+ }
44
+ async pair(input) {
45
+ if (this.pairing)
46
+ throw controlError("conflict", "another Runtime pairing is in progress");
47
+ this.pairing = true;
48
+ try {
49
+ const parsed = parseOfferInput(input.offer);
50
+ const { offer } = parsed;
51
+ if (offer.daemonId === this.localRuntime.status().daemonId) {
52
+ throw controlError("conflict", "cannot pair the local daemon as a remote target");
53
+ }
54
+ // Validate all client-local metadata before any network enrollment.
55
+ const targetName = displayName(input.displayName, offer.daemonId);
56
+ const existing = this.targets.get(offer.daemonId);
57
+ if (existing) {
58
+ if (existing.identityPublicKey !== offer.identityPublicKey) {
59
+ throw controlError("identity_mismatch", "a target with this daemonId has a different pinned identity key");
60
+ }
61
+ throw controlError("conflict", "this Runtime target is already paired");
62
+ }
63
+ const fingerprint = pairingOfferFingerprint(offer);
64
+ let pending = this.targets.getPendingEnrollment(offer.claimId);
65
+ const otherPending = this.targets.getPendingEnrollmentForDaemon(offer.daemonId);
66
+ if (!pending && otherPending) {
67
+ await this.discardExpiredPending(otherPending);
68
+ pending = this.targets.getPendingEnrollment(offer.claimId);
69
+ }
70
+ if (pending)
71
+ assertPendingOffer(pending, offer, fingerprint);
72
+ if (!pending && parsed.expired) {
73
+ throw controlError("invalid_offer", "pairing offer has expired");
74
+ }
75
+ if (!pending) {
76
+ const identity = generateDirectRuntimeClientIdentity();
77
+ const pendingCredential = pendingCredentialFrom(offer, identity);
78
+ let credentialRef;
79
+ try {
80
+ credentialRef = await this.credentials.savePending(pendingCredential);
81
+ pending = this.targets.addPendingEnrollment({
82
+ claimId: offer.claimId,
83
+ daemonId: offer.daemonId,
84
+ identityPublicKey: offer.identityPublicKey,
85
+ credentialRef,
86
+ offerFingerprint: fingerprint,
87
+ expiresAt: offer.expiresAt,
88
+ });
89
+ }
90
+ catch (error) {
91
+ if (credentialRef)
92
+ await this.credentials.remove(credentialRef).catch(() => undefined);
93
+ throw persistenceError("failed to prepare the Runtime enrollment", error);
94
+ }
95
+ }
96
+ return await this.resumePendingEnrollment(pending, offer, targetName, parsed.expired);
97
+ }
98
+ finally {
99
+ this.pairing = false;
100
+ }
101
+ }
102
+ async discardExpiredPending(pending) {
103
+ if (Date.parse(pending.expiresAt) > Date.now()) {
104
+ throw controlError("conflict", "a different unexpired enrollment is already pending for this Runtime");
105
+ }
106
+ try {
107
+ await this.credentials.remove(pending.credentialRef);
108
+ this.targets.removePendingEnrollment(pending.claimId);
109
+ }
110
+ catch (error) {
111
+ throw persistenceError("failed to discard an expired Runtime enrollment", error);
112
+ }
113
+ }
114
+ async resumePendingEnrollment(pending, offer, targetName, offerExpired) {
115
+ let enrollment;
116
+ try {
117
+ enrollment = await this.credentials.loadEnrollment(pending.credentialRef);
118
+ }
119
+ catch (error) {
120
+ throw controlError("authentication_failed", "pending Runtime enrollment credential is unavailable", error);
121
+ }
122
+ let accepted;
123
+ let status;
124
+ if (enrollment.state === "pending") {
125
+ assertPendingCredential(enrollment.credential, pending, offer);
126
+ let paired;
127
+ try {
128
+ paired = await this.client.pair(offer, {
129
+ identity: clientIdentityFrom(enrollment.credential),
130
+ ...(offerExpired ? { allowExpiredOffer: true } : {}),
131
+ });
132
+ }
133
+ catch (error) {
134
+ throw normalizeClientError(error);
135
+ }
136
+ assertPairResult(paired, offer, enrollment.credential);
137
+ accepted = secretCredentialFrom(paired.credential);
138
+ status = paired.status;
139
+ try {
140
+ await this.credentials.finalizePending(pending.credentialRef, pending.claimId, accepted);
141
+ }
142
+ catch (error) {
143
+ throw persistenceError("failed to finalize the Runtime enrollment credential", error);
144
+ }
145
+ }
146
+ else {
147
+ accepted = enrollment.credential;
148
+ assertCredentialTarget(accepted, pending.daemonId, pending.identityPublicKey);
149
+ status = await this.probe(directCredentialForOffer(accepted, offer));
150
+ }
151
+ let target;
152
+ try {
153
+ target = this.targets.commitPendingEnrollment(pending.claimId, {
154
+ daemonId: offer.daemonId,
155
+ displayName: targetName,
156
+ identityPublicKey: offer.identityPublicKey,
157
+ credentialRef: pending.credentialRef,
158
+ directEndpoint: offer.directEndpoint,
159
+ });
160
+ }
161
+ catch (error) {
162
+ throw persistenceError("failed to persist the paired Runtime target", error);
163
+ }
164
+ return { target: targetView(target), status };
165
+ }
166
+ async probe(credential) {
167
+ let connection;
168
+ try {
169
+ connection = await this.client.connect(credential);
170
+ const status = await connection.status();
171
+ if (status.daemonId !== credential.daemonId) {
172
+ throw controlError("identity_mismatch", "remote status belongs to a different daemon");
173
+ }
174
+ return status;
175
+ }
176
+ catch (error) {
177
+ if (error instanceof RuntimeTargetControlError)
178
+ throw error;
179
+ throw normalizeClientError(error);
180
+ }
181
+ finally {
182
+ await connection?.close().catch(() => undefined);
183
+ }
184
+ }
185
+ async test(selector) {
186
+ const target = selector === "local" ? this.list()[0] : targetView(this.target(selector));
187
+ let failure;
188
+ for (let attempt = 0; attempt < 2; attempt += 1) {
189
+ let lease;
190
+ try {
191
+ lease = await this.connections.acquire(selector);
192
+ const status = await lease.call("status.get", {});
193
+ if (status.daemonId !== lease.target.daemonId) {
194
+ throw controlError("identity_mismatch", "remote status belongs to a different daemon");
195
+ }
196
+ return { target, status };
197
+ }
198
+ catch (error) {
199
+ failure = error;
200
+ // status.get is read-only. Retry once only when a previously cached
201
+ // channel was already closed; mutations never use this path.
202
+ if (selector === "local" || attempt > 0 || !isClosedStatusChannel(error))
203
+ break;
204
+ }
205
+ finally {
206
+ lease?.release();
207
+ }
208
+ }
209
+ if (failure instanceof RuntimeTargetControlError)
210
+ throw failure;
211
+ if (failure instanceof RuntimeConnectionManagerError) {
212
+ throw normalizeConnectionManagerError(failure);
213
+ }
214
+ if (failure instanceof DirectRuntimeRpcError)
215
+ throw normalizeRpcError(failure);
216
+ throw normalizeClientError(failure);
217
+ }
218
+ async forget(daemonId) {
219
+ if (daemonId === "local" || daemonId === this.localRuntime.status().daemonId) {
220
+ throw controlError("invalid_target", "the local Runtime target cannot be forgotten");
221
+ }
222
+ const existing = this.target(daemonId);
223
+ // invalidate() detaches the current generation synchronously. Removing the
224
+ // target immediately afterwards closes the acquire/forget race without
225
+ // exposing the credential or silently falling back to Local.
226
+ const invalidation = this.connections.invalidate(existing.daemonId);
227
+ const target = this.targets.remove(existing.daemonId);
228
+ if (!target)
229
+ throw controlError("conflict", "Runtime target disappeared while forgetting it");
230
+ let invalidationError;
231
+ try {
232
+ await invalidation;
233
+ }
234
+ catch (error) {
235
+ invalidationError = error;
236
+ }
237
+ try {
238
+ await this.credentials.remove(target.credentialRef);
239
+ }
240
+ catch (error) {
241
+ // The target is already unreachable from Rynx. Report cleanup failure so
242
+ // the operator knows an orphaned protected file may require attention.
243
+ throw controlError("conflict", "Runtime target was removed but credential cleanup failed", error);
244
+ }
245
+ if (invalidationError !== undefined) {
246
+ throw controlError("conflict", "Runtime target was removed but its cached connection did not close cleanly", invalidationError);
247
+ }
248
+ return { forgotten: true };
249
+ }
250
+ target(selector) {
251
+ if (!/^[A-Za-z0-9][A-Za-z0-9._~-]{0,255}$/.test(selector)) {
252
+ throw controlError("invalid_target", "Runtime target selector is invalid");
253
+ }
254
+ const target = this.targets.get(selector);
255
+ if (!target)
256
+ throw controlError("not_found", "Runtime target was not found");
257
+ return target;
258
+ }
259
+ }
260
+ function parseOfferInput(input) {
261
+ let value = input;
262
+ if (typeof input === "string") {
263
+ try {
264
+ value = input.trim().startsWith("rynx://")
265
+ ? decodePairingCode(input, { allowExpired: true })
266
+ : JSON.parse(input);
267
+ }
268
+ catch (error) {
269
+ throw controlError("invalid_offer", "pairing offer must be a valid Rynx pairing link or JSON", error);
270
+ }
271
+ }
272
+ try {
273
+ const offer = parsePairingOffer(value, { allowExpired: true });
274
+ return { offer, expired: Date.parse(offer.expiresAt) <= Date.now() };
275
+ }
276
+ catch (error) {
277
+ throw controlError("invalid_offer", "pairing offer is invalid", error);
278
+ }
279
+ }
280
+ function displayName(value, daemonId) {
281
+ if (value === undefined)
282
+ return `Runtime ${daemonId.slice(0, 8)}`;
283
+ if (typeof value !== "string" ||
284
+ value.length === 0 ||
285
+ value.length > 128 ||
286
+ value.trim() !== value ||
287
+ /[\u0000-\u001f\u007f]/.test(value)) {
288
+ throw controlError("invalid_target", "Runtime target displayName is invalid");
289
+ }
290
+ return value;
291
+ }
292
+ function secretCredentialFrom(credential) {
293
+ return {
294
+ version: 2,
295
+ daemonId: credential.daemonId,
296
+ identityPublicKey: credential.identityPublicKey,
297
+ serverEncryptionPublicKey: credential.serverEncryptionPublicKey,
298
+ clientId: credential.clientId,
299
+ grantId: credential.grantId,
300
+ clientPublicKey: credential.clientPublicKey,
301
+ clientPrivateKeyPem: credential.clientPrivateKeyPem,
302
+ };
303
+ }
304
+ function pendingCredentialFrom(offer, identity) {
305
+ return {
306
+ version: 2,
307
+ state: "pending_enrollment",
308
+ daemonId: offer.daemonId,
309
+ identityPublicKey: offer.identityPublicKey,
310
+ serverEncryptionPublicKey: offer.serverEncryptionPublicKey,
311
+ claimId: offer.claimId,
312
+ ...identity,
313
+ };
314
+ }
315
+ function clientIdentityFrom(credential) {
316
+ return {
317
+ clientId: credential.clientId,
318
+ clientPublicKey: credential.clientPublicKey,
319
+ clientPrivateKeyPem: credential.clientPrivateKeyPem,
320
+ };
321
+ }
322
+ function directCredentialForOffer(credential, offer) {
323
+ return {
324
+ ...credential,
325
+ directEndpoint: offer.directEndpoint,
326
+ };
327
+ }
328
+ function assertPendingOffer(pending, offer, offerFingerprint) {
329
+ if (pending.daemonId !== offer.daemonId) {
330
+ throw controlError("conflict", "pairing claim belongs to a different Runtime");
331
+ }
332
+ if (pending.identityPublicKey !== offer.identityPublicKey) {
333
+ throw controlError("identity_mismatch", "pending enrollment has a different pinned Runtime identity");
334
+ }
335
+ if (pending.offerFingerprint !== offerFingerprint) {
336
+ throw controlError("conflict", "pairing offer differs from the pending enrollment");
337
+ }
338
+ }
339
+ function assertPendingCredential(credential, pending, offer) {
340
+ if (credential.claimId !== pending.claimId ||
341
+ credential.daemonId !== pending.daemonId ||
342
+ credential.daemonId !== offer.daemonId ||
343
+ credential.identityPublicKey !== pending.identityPublicKey ||
344
+ credential.identityPublicKey !== offer.identityPublicKey ||
345
+ credential.serverEncryptionPublicKey !== offer.serverEncryptionPublicKey) {
346
+ throw controlError("identity_mismatch", "pending Runtime metadata does not match its protected credential");
347
+ }
348
+ }
349
+ function assertCredentialTarget(credential, daemonId, identityPublicKey) {
350
+ if (credential.daemonId !== daemonId ||
351
+ credential.identityPublicKey !== identityPublicKey) {
352
+ throw controlError("identity_mismatch", "Runtime target metadata does not match its protected credential");
353
+ }
354
+ }
355
+ function assertPairResult(paired, offer, pending) {
356
+ const credential = paired.credential;
357
+ if (credential.daemonId !== offer.daemonId ||
358
+ credential.identityPublicKey !== offer.identityPublicKey ||
359
+ credential.serverEncryptionPublicKey !== offer.serverEncryptionPublicKey ||
360
+ paired.status.daemonId !== offer.daemonId) {
361
+ throw controlError("identity_mismatch", "enrollment result belongs to a different Runtime");
362
+ }
363
+ if (credential.directEndpoint !== offer.directEndpoint ||
364
+ credential.clientId !== pending.clientId ||
365
+ credential.clientPublicKey !== pending.clientPublicKey ||
366
+ credential.clientPrivateKeyPem !== pending.clientPrivateKeyPem) {
367
+ throw controlError("protocol_error", "enrollment result differs from the requested offer");
368
+ }
369
+ }
370
+ function pairingOfferFingerprint(offer) {
371
+ return createHash("sha256")
372
+ .update(JSON.stringify([
373
+ offer.type,
374
+ offer.enrollmentVersion,
375
+ offer.daemonId,
376
+ offer.identityAlgorithm,
377
+ offer.identityPublicKey,
378
+ offer.security,
379
+ offer.encryptionAlgorithm,
380
+ offer.serverEncryptionPublicKey,
381
+ offer.directEndpoint,
382
+ offer.claimId,
383
+ offer.claimSecret,
384
+ offer.expiresAt,
385
+ ]))
386
+ .digest("hex");
387
+ }
388
+ function targetView(target) {
389
+ return {
390
+ selector: target.daemonId,
391
+ daemonId: target.daemonId,
392
+ displayName: target.displayName,
393
+ binding: "direct",
394
+ endpoint: target.directRoute.endpoint,
395
+ identityKeyFingerprint: fingerprint(target.identityPublicKey),
396
+ createdAt: target.createdAt,
397
+ };
398
+ }
399
+ function fingerprint(publicKey) {
400
+ return `SHA256:${createHash("sha256")
401
+ .update(Buffer.from(publicKey, "base64url"))
402
+ .digest("base64url")}`;
403
+ }
404
+ function normalizeClientError(error) {
405
+ if (error instanceof DirectRuntimeClientError) {
406
+ const code = error.code === "closed" ? "unreachable" : error.code;
407
+ return controlError(code, error.message, error);
408
+ }
409
+ return controlError("unreachable", "remote Runtime connection failed", error);
410
+ }
411
+ function normalizeConnectionManagerError(error) {
412
+ const code = error.code === "closed" || error.code === "cancelled" ? "unreachable" : error.code;
413
+ return controlError(code, error.message, error);
414
+ }
415
+ function isClosedStatusChannel(error) {
416
+ return ((error instanceof DirectRuntimeClientError && error.code === "closed") ||
417
+ (error instanceof RuntimeConnectionManagerError && error.code === "closed"));
418
+ }
419
+ function normalizeRpcError(error) {
420
+ const code = error.code === "unauthorized" ? "authentication_failed" : "protocol_error";
421
+ return controlError(code, error.message, error);
422
+ }
423
+ function persistenceError(message, error) {
424
+ if (error instanceof RuntimeTargetControlError)
425
+ return error;
426
+ if (error instanceof RemoteRuntimeTargetConflictError) {
427
+ return controlError("conflict", error.message, error);
428
+ }
429
+ return controlError("conflict", message, error);
430
+ }
431
+ function controlError(code, message, cause) {
432
+ return new RuntimeTargetControlError(code, message, cause === undefined ? undefined : { cause });
433
+ }
@@ -0,0 +1,53 @@
1
+ import Database from "better-sqlite3";
2
+ export interface RemoteRuntimeTarget {
3
+ daemonId: string;
4
+ displayName: string;
5
+ identityPublicKey: string;
6
+ credentialRef: string;
7
+ directRoute: {
8
+ endpoint: string;
9
+ };
10
+ createdAt: string;
11
+ updatedAt: string;
12
+ }
13
+ export interface AddRemoteRuntimeTargetInput {
14
+ daemonId: string;
15
+ displayName: string;
16
+ identityPublicKey: string;
17
+ credentialRef: string;
18
+ directEndpoint: string;
19
+ }
20
+ export interface RemoteRuntimePendingEnrollment {
21
+ claimId: string;
22
+ daemonId: string;
23
+ identityPublicKey: string;
24
+ credentialRef: string;
25
+ offerFingerprint: string;
26
+ expiresAt: string;
27
+ createdAt: string;
28
+ }
29
+ export interface AddRemoteRuntimePendingEnrollmentInput {
30
+ claimId: string;
31
+ daemonId: string;
32
+ identityPublicKey: string;
33
+ credentialRef: string;
34
+ offerFingerprint: string;
35
+ expiresAt: string;
36
+ }
37
+ export declare class RemoteRuntimeTargetStore {
38
+ private readonly connection;
39
+ private readonly clock;
40
+ constructor(connection?: Database.Database, clock?: () => Date);
41
+ add(input: AddRemoteRuntimeTargetInput): RemoteRuntimeTarget;
42
+ addPendingEnrollment(input: AddRemoteRuntimePendingEnrollmentInput): RemoteRuntimePendingEnrollment;
43
+ getPendingEnrollment(claimIdInput: string): RemoteRuntimePendingEnrollment | undefined;
44
+ getPendingEnrollmentForDaemon(daemonIdInput: string): RemoteRuntimePendingEnrollment | undefined;
45
+ removePendingEnrollment(claimIdInput: string): RemoteRuntimePendingEnrollment | undefined;
46
+ commitPendingEnrollment(claimIdInput: string, input: AddRemoteRuntimeTargetInput): RemoteRuntimeTarget;
47
+ get(daemonIdInput: string): RemoteRuntimeTarget | undefined;
48
+ list(): RemoteRuntimeTarget[];
49
+ remove(daemonIdInput: string): RemoteRuntimeTarget | undefined;
50
+ }
51
+ export declare class RemoteRuntimeTargetConflictError extends Error {
52
+ constructor(message: string, options?: ErrorOptions);
53
+ }