@cydm/happy-elves 0.1.0-beta.75 → 0.1.0-beta.76
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/apps/cli/dist/commands/app.js +2 -0
- package/apps/cli/dist/commands/diagnostics.d.ts +2 -0
- package/apps/cli/dist/commands/diagnostics.js +71 -0
- package/apps/cli/dist/commands/lib/args.js +4 -0
- package/apps/cli/dist/commands/lib/usage.js +15 -1
- package/apps/daemon/dist/lifecycle/detached-helper-bundle.mjs +268 -2
- package/apps/daemon/package.json +1 -1
- package/apps/relay/dist/connections.d.ts +1 -0
- package/apps/relay/dist/connections.js +1 -0
- package/apps/relay/dist/relay-context.d.ts +1 -0
- package/apps/relay/dist/relay-context.js +14 -12
- package/apps/relay/dist/types.d.ts +7 -1
- package/apps/relay/dist/ui-probe-router.d.ts +35 -0
- package/apps/relay/dist/ui-probe-router.js +414 -0
- package/apps/relay/dist/websocket.js +78 -8
- package/build-identity.json +2 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/packages/client/dist/index.d.ts +3 -1
- package/packages/client/dist/index.js +1 -0
- package/packages/client/dist/transport.d.ts +4 -1
- package/packages/client/dist/ui-probe.d.ts +116 -0
- package/packages/client/dist/ui-probe.js +825 -0
- package/packages/shared/dist/index.d.ts +1 -0
- package/packages/shared/dist/index.js +1 -0
- package/packages/shared/dist/protocol.d.ts +54 -0
- package/packages/shared/dist/protocol.js +64 -0
- package/packages/shared/dist/ui-probe.d.ts +459 -0
- package/packages/shared/dist/ui-probe.js +257 -0
|
@@ -2,6 +2,7 @@ import { handleAccount } from "./account.js";
|
|
|
2
2
|
import { handleCollect } from "./collect.js";
|
|
3
3
|
import { handleConfig } from "./config.js";
|
|
4
4
|
import { handleDaemon } from "./daemon.js";
|
|
5
|
+
import { handleDiagnostics } from "./diagnostics.js";
|
|
5
6
|
import { handleGateway } from "./gateway.js";
|
|
6
7
|
import { handleLoop } from "./loop.js";
|
|
7
8
|
import { handleMachine } from "./machine.js";
|
|
@@ -22,6 +23,7 @@ const domainHandlers = [
|
|
|
22
23
|
handleToken,
|
|
23
24
|
handleLoop,
|
|
24
25
|
handleDaemon,
|
|
26
|
+
handleDiagnostics,
|
|
25
27
|
handleRelay,
|
|
26
28
|
handleMachine,
|
|
27
29
|
handleMemory,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { UiProbeClient } from "../../../../packages/client/dist/index.js";
|
|
2
|
+
import { CliError, ok, parseDurationMs, readConfig, requireString, wantsJson } from "./lib/index.js";
|
|
3
|
+
const DEFAULT_CAPTURE_TIMEOUT_MS = 30_000;
|
|
4
|
+
const DEFAULT_WATCH_DURATION_MS = 10 * 60_000;
|
|
5
|
+
const MAX_WATCH_DURATION_MS = 10 * 60_000;
|
|
6
|
+
function durationFlag(flags, key, fallbackMs, maximumMs) {
|
|
7
|
+
const durationMs = parseDurationMs(flags[key], fallbackMs);
|
|
8
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0 || durationMs > maximumMs) {
|
|
9
|
+
throw new CliError(`Invalid --${key}: expected a positive duration no greater than 10m`, "INVALID_ARGUMENT");
|
|
10
|
+
}
|
|
11
|
+
return durationMs;
|
|
12
|
+
}
|
|
13
|
+
function printTelemetry(telemetry) {
|
|
14
|
+
console.log(JSON.stringify(telemetry));
|
|
15
|
+
}
|
|
16
|
+
export async function handleDiagnostics({ domain, action, flags }) {
|
|
17
|
+
if (domain !== "diagnostics")
|
|
18
|
+
return false;
|
|
19
|
+
const config = await readConfig(flags);
|
|
20
|
+
const client = new UiProbeClient(config);
|
|
21
|
+
try {
|
|
22
|
+
if (action === "surfaces") {
|
|
23
|
+
const surfaces = await client.listSurfaces();
|
|
24
|
+
if (wantsJson(flags))
|
|
25
|
+
ok("diagnostics.surfaces", { surfaces });
|
|
26
|
+
else if (surfaces.length === 0)
|
|
27
|
+
console.log("No Device is sharing live diagnostics.");
|
|
28
|
+
else {
|
|
29
|
+
for (const surface of surfaces) {
|
|
30
|
+
console.log(`${surface.deviceId} ${surface.surfaceId} ${surface.kind} expires ${new Date(surface.expiresAt).toISOString()}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (action === "capture") {
|
|
36
|
+
const deviceId = requireString(flags, "device");
|
|
37
|
+
const surfaceId = requireString(flags, "surface");
|
|
38
|
+
const timeoutMs = durationFlag(flags, "timeout", DEFAULT_CAPTURE_TIMEOUT_MS, MAX_WATCH_DURATION_MS);
|
|
39
|
+
const telemetry = await client.capture({ deviceId, surfaceId, timeoutMs });
|
|
40
|
+
if (wantsJson(flags))
|
|
41
|
+
ok("diagnostics.capture", telemetry);
|
|
42
|
+
else
|
|
43
|
+
console.log(JSON.stringify(telemetry, null, 2));
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (action === "watch") {
|
|
47
|
+
if (flags.jsonl !== true) {
|
|
48
|
+
throw new CliError("diagnostics watch requires --jsonl", "MISSING_ARGUMENT");
|
|
49
|
+
}
|
|
50
|
+
const deviceId = requireString(flags, "device");
|
|
51
|
+
const surfaceId = requireString(flags, "surface");
|
|
52
|
+
const durationMs = durationFlag(flags, "duration", DEFAULT_WATCH_DURATION_MS, MAX_WATCH_DURATION_MS);
|
|
53
|
+
const abortController = new AbortController();
|
|
54
|
+
const onSigint = () => abortController.abort();
|
|
55
|
+
process.once("SIGINT", onSigint);
|
|
56
|
+
try {
|
|
57
|
+
const watch = await client.watch({ deviceId, surfaceId, durationMs, signal: abortController.signal });
|
|
58
|
+
for await (const telemetry of watch)
|
|
59
|
+
printTelemetry(telemetry);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
process.removeListener("SIGINT", onSigint);
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
throw new CliError(`Unknown diagnostics action: ${action ?? "(missing)"}`, "INVALID_ARGUMENT");
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
await client.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -25,7 +25,9 @@ const valueFlags = new Set([
|
|
|
25
25
|
"device-id",
|
|
26
26
|
"device-name",
|
|
27
27
|
"display-name",
|
|
28
|
+
"device",
|
|
28
29
|
"domain",
|
|
30
|
+
"duration",
|
|
29
31
|
"from",
|
|
30
32
|
"host",
|
|
31
33
|
"host-machine",
|
|
@@ -69,6 +71,7 @@ const valueFlags = new Set([
|
|
|
69
71
|
"runtime-limit",
|
|
70
72
|
"runtime-session",
|
|
71
73
|
"shell",
|
|
74
|
+
"surface",
|
|
72
75
|
"actions",
|
|
73
76
|
"expires-in",
|
|
74
77
|
"format",
|
|
@@ -122,6 +125,7 @@ const booleanFlags = new Set([
|
|
|
122
125
|
"include-archived",
|
|
123
126
|
"isolated",
|
|
124
127
|
"json",
|
|
128
|
+
"jsonl",
|
|
125
129
|
"keep-source",
|
|
126
130
|
"local",
|
|
127
131
|
"longterm",
|
|
@@ -12,6 +12,15 @@ Show the current Account, Daemon, and latest Session state.
|
|
|
12
12
|
doctor: `Usage: happy-elves doctor [--relay <url>] [--json]
|
|
13
13
|
|
|
14
14
|
Check local Device access, Account consistency, service reachability, and Daemon state.
|
|
15
|
+
`,
|
|
16
|
+
diagnostics: `Usage:
|
|
17
|
+
happy-elves diagnostics surfaces --json
|
|
18
|
+
happy-elves diagnostics capture --device <deviceId> --surface <surfaceId> [--timeout 30s] --json
|
|
19
|
+
happy-elves diagnostics watch --device <deviceId> --surface <surfaceId> [--duration 10m] --jsonl
|
|
20
|
+
|
|
21
|
+
Observe privacy-filtered layout, focus, viewport, pointer, and scroll telemetry
|
|
22
|
+
from a Device that explicitly enabled Settings > Diagnostics > Share live diagnostics.
|
|
23
|
+
Probe traffic is end-to-end encrypted and expires after ten minutes.
|
|
15
24
|
`,
|
|
16
25
|
account: `Usage:
|
|
17
26
|
happy-elves account create --relay <url> [--device-id <id>] [--device-name <name>] [--reveal-secrets] --json
|
|
@@ -300,6 +309,11 @@ Advanced and compatibility commands:
|
|
|
300
309
|
relay status [--relay <url>] [--json]
|
|
301
310
|
relay doctor [--relay <url>] [--json]
|
|
302
311
|
|
|
312
|
+
# Privacy-filtered diagnostics shared by another trusted Device.
|
|
313
|
+
diagnostics surfaces --json
|
|
314
|
+
diagnostics capture --device <deviceId> --surface <surfaceId> --json
|
|
315
|
+
diagnostics watch --device <deviceId> --surface <surfaceId> --jsonl
|
|
316
|
+
|
|
303
317
|
Usage:
|
|
304
318
|
happy-elves <domain> <action> [args] [flags]
|
|
305
319
|
happy-elves <domain> --help
|
|
@@ -316,7 +330,7 @@ More:
|
|
|
316
330
|
happy-elves --version
|
|
317
331
|
happy-elves version --json
|
|
318
332
|
happy-elves <domain> --help
|
|
319
|
-
start, status, doctor, collect, config, account, daemon, relay, token, gateway, loop.
|
|
333
|
+
start, status, doctor, collect, config, account, daemon, relay, diagnostics, token, gateway, loop.
|
|
320
334
|
orchestrator, skill.
|
|
321
335
|
`;
|
|
322
336
|
}
|
|
@@ -18137,8 +18137,241 @@ function isSessionNameValid(value) {
|
|
|
18137
18137
|
}
|
|
18138
18138
|
}
|
|
18139
18139
|
|
|
18140
|
+
// ../../packages/shared/dist/ui-probe.js
|
|
18141
|
+
var encoder2 = new TextEncoder();
|
|
18142
|
+
var opaqueIdSchema = external_exports.string().min(1).max(200);
|
|
18143
|
+
var identifierSchema = external_exports.string().min(1).max(160).regex(/^[A-Za-z0-9._~-]+$/u);
|
|
18144
|
+
var anchorSchema = external_exports.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9._-]*$/u);
|
|
18145
|
+
var markerIdSchema = external_exports.string().regex(/^probe_marker_[A-Za-z0-9_-]{16,115}$/u);
|
|
18146
|
+
var finiteNumberSchema = external_exports.number().finite();
|
|
18147
|
+
var nonNegativeIntegerSchema = external_exports.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
18148
|
+
var positiveSequenceSchema = external_exports.number().int().min(1).max(Number.MAX_SAFE_INTEGER);
|
|
18149
|
+
var encodedCiphertextSchema = external_exports.string().min(1).max(128 * 1024).regex(/^[A-Za-z0-9+/_=-]+$/u);
|
|
18150
|
+
var uiProbeMaxEncryptedEnvelopeBytes = 126 * 1024;
|
|
18151
|
+
var uiProbeEncryptedEnvelopeSchema = external_exports.strictObject({
|
|
18152
|
+
v: external_exports.literal(1),
|
|
18153
|
+
alg: external_exports.literal("A256GCM"),
|
|
18154
|
+
scope: external_exports.string().min(1).max(512),
|
|
18155
|
+
key: external_exports.strictObject({
|
|
18156
|
+
nonce: encodedCiphertextSchema.max(128),
|
|
18157
|
+
ciphertext: encodedCiphertextSchema.max(1024)
|
|
18158
|
+
}),
|
|
18159
|
+
data: external_exports.strictObject({
|
|
18160
|
+
nonce: encodedCiphertextSchema.max(128),
|
|
18161
|
+
ciphertext: encodedCiphertextSchema
|
|
18162
|
+
})
|
|
18163
|
+
}).superRefine((envelope, context) => {
|
|
18164
|
+
if (encoder2.encode(JSON.stringify(envelope)).byteLength > uiProbeMaxEncryptedEnvelopeBytes) {
|
|
18165
|
+
context.addIssue({ code: "custom", message: "UI Probe encrypted payload exceeds the 128 KiB frame budget" });
|
|
18166
|
+
}
|
|
18167
|
+
});
|
|
18168
|
+
var uiProbeCapabilitiesSchema = external_exports.strictObject({
|
|
18169
|
+
versions: external_exports.tuple([external_exports.literal(1)]),
|
|
18170
|
+
telemetry: external_exports.literal(true),
|
|
18171
|
+
driver: external_exports.boolean()
|
|
18172
|
+
});
|
|
18173
|
+
var uiProbeSurfaceRegistrationSchema = external_exports.strictObject({
|
|
18174
|
+
version: external_exports.literal(1),
|
|
18175
|
+
surfaceId: identifierSchema,
|
|
18176
|
+
instanceId: identifierSchema,
|
|
18177
|
+
kind: external_exports.enum(["browser", "pwa", "desktop-webview"]),
|
|
18178
|
+
capabilities: uiProbeCapabilitiesSchema,
|
|
18179
|
+
expiresAt: nonNegativeIntegerSchema
|
|
18180
|
+
});
|
|
18181
|
+
var uiProbeSurfaceSchema = uiProbeSurfaceRegistrationSchema.extend({
|
|
18182
|
+
deviceId: opaqueIdSchema,
|
|
18183
|
+
deviceName: external_exports.string().min(1).max(160),
|
|
18184
|
+
connectedAt: nonNegativeIntegerSchema
|
|
18185
|
+
});
|
|
18186
|
+
var uiProbeSessionSchema = external_exports.strictObject({
|
|
18187
|
+
version: external_exports.literal(1),
|
|
18188
|
+
probeId: identifierSchema,
|
|
18189
|
+
observerDeviceId: opaqueIdSchema,
|
|
18190
|
+
observerDeviceName: external_exports.string().min(1).max(160),
|
|
18191
|
+
targetDeviceId: opaqueIdSchema,
|
|
18192
|
+
surfaceId: identifierSchema,
|
|
18193
|
+
instanceId: identifierSchema,
|
|
18194
|
+
openedAt: nonNegativeIntegerSchema,
|
|
18195
|
+
expiresAt: nonNegativeIntegerSchema
|
|
18196
|
+
});
|
|
18197
|
+
var uiProbeCloseReasonSchema = external_exports.enum([
|
|
18198
|
+
"requested",
|
|
18199
|
+
"expired",
|
|
18200
|
+
"observer-disconnected",
|
|
18201
|
+
"surface-disconnected",
|
|
18202
|
+
"surface-replaced",
|
|
18203
|
+
"auth-revoked",
|
|
18204
|
+
"backpressure",
|
|
18205
|
+
"rate-limited",
|
|
18206
|
+
"relay-shutdown"
|
|
18207
|
+
]);
|
|
18208
|
+
var uiProbeOpenSemanticsSchema = external_exports.strictObject({
|
|
18209
|
+
version: external_exports.literal(1),
|
|
18210
|
+
requestId: opaqueIdSchema,
|
|
18211
|
+
targetDeviceId: opaqueIdSchema,
|
|
18212
|
+
surfaceId: identifierSchema,
|
|
18213
|
+
instanceId: identifierSchema
|
|
18214
|
+
});
|
|
18215
|
+
var uiProbeElementRefSchema = external_exports.strictObject({
|
|
18216
|
+
anchor: anchorSchema.optional(),
|
|
18217
|
+
tag: external_exports.string().min(1).max(48).regex(/^[a-z][a-z0-9-]*$/u),
|
|
18218
|
+
role: external_exports.string().min(1).max(48).regex(/^[a-z][a-z0-9-]*$/u).optional(),
|
|
18219
|
+
inputType: external_exports.string().min(1).max(32).regex(/^[a-z][a-z0-9-]*$/u).optional(),
|
|
18220
|
+
disabled: external_exports.boolean().optional(),
|
|
18221
|
+
readOnly: external_exports.boolean().optional()
|
|
18222
|
+
});
|
|
18223
|
+
var uiProbeRectSchema = external_exports.strictObject({
|
|
18224
|
+
x: finiteNumberSchema,
|
|
18225
|
+
y: finiteNumberSchema,
|
|
18226
|
+
width: finiteNumberSchema.min(0),
|
|
18227
|
+
height: finiteNumberSchema.min(0)
|
|
18228
|
+
});
|
|
18229
|
+
var uiProbeSampleSchema = external_exports.discriminatedUnion("kind", [
|
|
18230
|
+
external_exports.strictObject({
|
|
18231
|
+
kind: external_exports.literal("viewport"),
|
|
18232
|
+
at: nonNegativeIntegerSchema,
|
|
18233
|
+
layout: external_exports.strictObject({ width: finiteNumberSchema.min(0), height: finiteNumberSchema.min(0) }),
|
|
18234
|
+
visual: external_exports.strictObject({
|
|
18235
|
+
width: finiteNumberSchema.min(0),
|
|
18236
|
+
height: finiteNumberSchema.min(0),
|
|
18237
|
+
offsetLeft: finiteNumberSchema,
|
|
18238
|
+
offsetTop: finiteNumberSchema,
|
|
18239
|
+
scale: finiteNumberSchema.min(0)
|
|
18240
|
+
}).nullable()
|
|
18241
|
+
}),
|
|
18242
|
+
external_exports.strictObject({
|
|
18243
|
+
kind: external_exports.literal("focus"),
|
|
18244
|
+
at: nonNegativeIntegerSchema,
|
|
18245
|
+
event: external_exports.enum(["focus", "blur", "focusin", "focusout"]),
|
|
18246
|
+
active: uiProbeElementRefSchema.nullable(),
|
|
18247
|
+
isTrusted: external_exports.boolean()
|
|
18248
|
+
}),
|
|
18249
|
+
external_exports.strictObject({
|
|
18250
|
+
kind: external_exports.literal("pointer"),
|
|
18251
|
+
at: nonNegativeIntegerSchema,
|
|
18252
|
+
event: external_exports.enum(["pointerdown", "pointerup", "pointercancel", "touchstart", "touchend", "touchcancel"]),
|
|
18253
|
+
x: finiteNumberSchema,
|
|
18254
|
+
y: finiteNumberSchema,
|
|
18255
|
+
isTrusted: external_exports.boolean(),
|
|
18256
|
+
target: uiProbeElementRefSchema.nullable()
|
|
18257
|
+
}),
|
|
18258
|
+
external_exports.strictObject({
|
|
18259
|
+
kind: external_exports.literal("scroll"),
|
|
18260
|
+
at: nonNegativeIntegerSchema,
|
|
18261
|
+
target: uiProbeElementRefSchema.nullable(),
|
|
18262
|
+
scrollLeft: finiteNumberSchema,
|
|
18263
|
+
scrollTop: finiteNumberSchema,
|
|
18264
|
+
clientWidth: finiteNumberSchema.min(0),
|
|
18265
|
+
clientHeight: finiteNumberSchema.min(0),
|
|
18266
|
+
scrollWidth: finiteNumberSchema.min(0),
|
|
18267
|
+
scrollHeight: finiteNumberSchema.min(0)
|
|
18268
|
+
}),
|
|
18269
|
+
external_exports.strictObject({
|
|
18270
|
+
kind: external_exports.literal("anchors"),
|
|
18271
|
+
at: nonNegativeIntegerSchema,
|
|
18272
|
+
anchors: external_exports.array(external_exports.strictObject({
|
|
18273
|
+
anchor: anchorSchema,
|
|
18274
|
+
tag: external_exports.string().min(1).max(48).regex(/^[a-z][a-z0-9-]*$/u),
|
|
18275
|
+
role: external_exports.string().min(1).max(48).regex(/^[a-z][a-z0-9-]*$/u).optional(),
|
|
18276
|
+
inputType: external_exports.string().min(1).max(32).regex(/^[a-z][a-z0-9-]*$/u).optional(),
|
|
18277
|
+
disabled: external_exports.boolean().optional(),
|
|
18278
|
+
readOnly: external_exports.boolean().optional(),
|
|
18279
|
+
rect: uiProbeRectSchema,
|
|
18280
|
+
style: external_exports.strictObject({
|
|
18281
|
+
position: external_exports.enum(["static", "relative", "absolute", "fixed", "sticky"]),
|
|
18282
|
+
overflowX: external_exports.enum(["visible", "hidden", "clip", "scroll", "auto"]),
|
|
18283
|
+
overflowY: external_exports.enum(["visible", "hidden", "clip", "scroll", "auto"]),
|
|
18284
|
+
touchAction: external_exports.string().min(1).max(64),
|
|
18285
|
+
pointerEvents: external_exports.enum(["auto", "none"]),
|
|
18286
|
+
zIndex: external_exports.string().min(1).max(32),
|
|
18287
|
+
fontSize: external_exports.string().min(1).max(32),
|
|
18288
|
+
transform: external_exports.string().min(1).max(256)
|
|
18289
|
+
})
|
|
18290
|
+
})).max(64)
|
|
18291
|
+
}),
|
|
18292
|
+
external_exports.strictObject({
|
|
18293
|
+
kind: external_exports.literal("layout-shift"),
|
|
18294
|
+
at: nonNegativeIntegerSchema,
|
|
18295
|
+
value: finiteNumberSchema.min(0),
|
|
18296
|
+
hadRecentInput: external_exports.boolean()
|
|
18297
|
+
}),
|
|
18298
|
+
external_exports.strictObject({
|
|
18299
|
+
kind: external_exports.literal("lifecycle"),
|
|
18300
|
+
at: nonNegativeIntegerSchema,
|
|
18301
|
+
event: external_exports.enum(["load", "pageshow", "pagehide", "visible", "hidden", "online", "offline"]),
|
|
18302
|
+
persisted: external_exports.boolean().optional()
|
|
18303
|
+
}),
|
|
18304
|
+
external_exports.strictObject({
|
|
18305
|
+
kind: external_exports.literal("error"),
|
|
18306
|
+
at: nonNegativeIntegerSchema,
|
|
18307
|
+
category: external_exports.enum(["console", "unhandled-error", "unhandled-rejection", "layout-observer"]),
|
|
18308
|
+
messageHash: external_exports.string().regex(/^[a-f0-9]{64}$/u)
|
|
18309
|
+
}),
|
|
18310
|
+
external_exports.strictObject({
|
|
18311
|
+
kind: external_exports.literal("marker"),
|
|
18312
|
+
at: nonNegativeIntegerSchema,
|
|
18313
|
+
markerId: markerIdSchema
|
|
18314
|
+
})
|
|
18315
|
+
]);
|
|
18316
|
+
var uiProbeBindingSchema = {
|
|
18317
|
+
version: external_exports.literal(1),
|
|
18318
|
+
probeId: identifierSchema,
|
|
18319
|
+
observerDeviceId: opaqueIdSchema,
|
|
18320
|
+
targetDeviceId: opaqueIdSchema,
|
|
18321
|
+
surfaceId: identifierSchema,
|
|
18322
|
+
instanceId: identifierSchema,
|
|
18323
|
+
sequence: positiveSequenceSchema
|
|
18324
|
+
};
|
|
18325
|
+
var uiProbeCommandActionSchema = external_exports.discriminatedUnion("action", [
|
|
18326
|
+
external_exports.strictObject({ action: external_exports.literal("snapshot"), anchors: external_exports.array(anchorSchema).max(64).optional() }),
|
|
18327
|
+
external_exports.strictObject({ action: external_exports.literal("setSampling"), intervalMs: external_exports.number().int().min(250).max(2e3) }),
|
|
18328
|
+
external_exports.strictObject({ action: external_exports.literal("mark"), markerId: markerIdSchema }),
|
|
18329
|
+
external_exports.strictObject({ action: external_exports.literal("close") })
|
|
18330
|
+
]);
|
|
18331
|
+
var uiProbeCommandSchema = external_exports.strictObject({
|
|
18332
|
+
...uiProbeBindingSchema,
|
|
18333
|
+
command: uiProbeCommandActionSchema
|
|
18334
|
+
});
|
|
18335
|
+
var uiProbeTelemetrySchema = external_exports.strictObject({
|
|
18336
|
+
...uiProbeBindingSchema,
|
|
18337
|
+
sampledAt: nonNegativeIntegerSchema,
|
|
18338
|
+
fullSnapshot: external_exports.boolean(),
|
|
18339
|
+
dropped: nonNegativeIntegerSchema,
|
|
18340
|
+
samples: external_exports.array(uiProbeSampleSchema).max(64)
|
|
18341
|
+
});
|
|
18342
|
+
|
|
18140
18343
|
// ../../packages/shared/dist/protocol.js
|
|
18141
18344
|
var controllerMessageSchema = external_exports.discriminatedUnion("type", [
|
|
18345
|
+
external_exports.object({
|
|
18346
|
+
type: external_exports.literal("controller:uiProbeList"),
|
|
18347
|
+
requestId: external_exports.string().min(1).max(200)
|
|
18348
|
+
}),
|
|
18349
|
+
external_exports.object({
|
|
18350
|
+
type: external_exports.literal("controller:uiProbeOpen"),
|
|
18351
|
+
requestId: external_exports.string().min(1).max(200),
|
|
18352
|
+
targetDeviceId: external_exports.string().min(1).max(200),
|
|
18353
|
+
surfaceId: external_exports.string().min(1).max(160).regex(/^[A-Za-z0-9._~-]+$/u),
|
|
18354
|
+
instanceId: external_exports.string().min(1).max(160).regex(/^[A-Za-z0-9._~-]+$/u),
|
|
18355
|
+
semanticDigest: external_exports.string().max(200).regex(/^ui-probe-v1:[A-Za-z0-9_-]+$/u)
|
|
18356
|
+
}),
|
|
18357
|
+
external_exports.object({
|
|
18358
|
+
type: external_exports.literal("controller:uiProbeCommand"),
|
|
18359
|
+
requestId: external_exports.string().min(1).max(200),
|
|
18360
|
+
probeId: external_exports.string().min(1).max(160).regex(/^[A-Za-z0-9._~-]+$/u),
|
|
18361
|
+
sequence: external_exports.number().int().positive(),
|
|
18362
|
+
encryptedPayload: uiProbeEncryptedEnvelopeSchema
|
|
18363
|
+
}),
|
|
18364
|
+
external_exports.object({
|
|
18365
|
+
type: external_exports.literal("controller:uiProbeTelemetry"),
|
|
18366
|
+
probeId: external_exports.string().min(1).max(160).regex(/^[A-Za-z0-9._~-]+$/u),
|
|
18367
|
+
sequence: external_exports.number().int().positive(),
|
|
18368
|
+
encryptedPayload: uiProbeEncryptedEnvelopeSchema
|
|
18369
|
+
}),
|
|
18370
|
+
external_exports.object({
|
|
18371
|
+
type: external_exports.literal("controller:uiProbeClose"),
|
|
18372
|
+
requestId: external_exports.string().min(1).max(200),
|
|
18373
|
+
probeId: external_exports.string().min(1).max(160).regex(/^[A-Za-z0-9._~-]+$/u)
|
|
18374
|
+
}),
|
|
18142
18375
|
external_exports.object({
|
|
18143
18376
|
type: external_exports.literal("controller:createSession"),
|
|
18144
18377
|
requestId: external_exports.string().min(1),
|
|
@@ -18598,11 +18831,14 @@ var clientMessageSchema = external_exports.union([
|
|
|
18598
18831
|
clientType: external_exports.enum(["controller", "machine"]),
|
|
18599
18832
|
token: external_exports.string().min(1),
|
|
18600
18833
|
machineId: external_exports.string().min(1).optional(),
|
|
18834
|
+
purpose: external_exports.enum(["app", "probe"]).optional(),
|
|
18835
|
+
uiProbeSurface: uiProbeSurfaceRegistrationSchema.optional(),
|
|
18601
18836
|
capabilities: external_exports.object({
|
|
18602
18837
|
daemonLifecycleEventsV1: external_exports.boolean().optional(),
|
|
18603
18838
|
daemonAutostartV1: external_exports.boolean().optional(),
|
|
18604
18839
|
resourcePreferenceInvalidationV1: external_exports.boolean().optional(),
|
|
18605
|
-
daemonProjectsPreferenceV1: external_exports.boolean().optional()
|
|
18840
|
+
daemonProjectsPreferenceV1: external_exports.boolean().optional(),
|
|
18841
|
+
uiProbe: uiProbeCapabilitiesSchema.optional()
|
|
18606
18842
|
}).optional()
|
|
18607
18843
|
}),
|
|
18608
18844
|
controllerMessageSchema,
|
|
@@ -18850,9 +19086,39 @@ var serverMessageSchema = external_exports.union([
|
|
|
18850
19086
|
sessionStarsV1: external_exports.boolean().optional(),
|
|
18851
19087
|
daemonAuthorizedDeviceBootstrapV1: external_exports.boolean().optional(),
|
|
18852
19088
|
browserBootstrapInviteV1: external_exports.boolean().optional(),
|
|
18853
|
-
daemonAutostartV1: external_exports.boolean().optional()
|
|
19089
|
+
daemonAutostartV1: external_exports.boolean().optional(),
|
|
19090
|
+
uiProbe: uiProbeCapabilitiesSchema.optional()
|
|
18854
19091
|
}).optional()
|
|
18855
19092
|
}),
|
|
19093
|
+
external_exports.object({
|
|
19094
|
+
type: external_exports.literal("server:uiProbeSurfaces"),
|
|
19095
|
+
requestId: external_exports.string().min(1),
|
|
19096
|
+
surfaces: external_exports.array(uiProbeSurfaceSchema)
|
|
19097
|
+
}),
|
|
19098
|
+
external_exports.object({
|
|
19099
|
+
type: external_exports.literal("server:uiProbeOpened"),
|
|
19100
|
+
requestId: external_exports.string().min(1),
|
|
19101
|
+
session: uiProbeSessionSchema
|
|
19102
|
+
}),
|
|
19103
|
+
external_exports.object({
|
|
19104
|
+
type: external_exports.literal("server:uiProbeCommand"),
|
|
19105
|
+
requestId: external_exports.string().min(1),
|
|
19106
|
+
session: uiProbeSessionSchema,
|
|
19107
|
+
sequence: external_exports.number().int().positive(),
|
|
19108
|
+
encryptedPayload: uiProbeEncryptedEnvelopeSchema
|
|
19109
|
+
}),
|
|
19110
|
+
external_exports.object({
|
|
19111
|
+
type: external_exports.literal("server:uiProbeTelemetry"),
|
|
19112
|
+
session: uiProbeSessionSchema,
|
|
19113
|
+
sequence: external_exports.number().int().positive(),
|
|
19114
|
+
encryptedPayload: uiProbeEncryptedEnvelopeSchema
|
|
19115
|
+
}),
|
|
19116
|
+
external_exports.object({
|
|
19117
|
+
type: external_exports.literal("server:uiProbeClosed"),
|
|
19118
|
+
requestId: external_exports.string().min(1).optional(),
|
|
19119
|
+
probeId: external_exports.string().min(1),
|
|
19120
|
+
reason: uiProbeCloseReasonSchema
|
|
19121
|
+
}),
|
|
18856
19122
|
external_exports.object({
|
|
18857
19123
|
type: external_exports.literal("server:snapshot"),
|
|
18858
19124
|
machines: external_exports.array(machineSnapshotSchema),
|
package/apps/daemon/package.json
CHANGED
|
@@ -57,6 +57,7 @@ export type PendingCommandContext = {
|
|
|
57
57
|
export declare function machineKey(accountId: string, machineId: string): string;
|
|
58
58
|
export declare function createConnectionState(): {
|
|
59
59
|
controllersByAccount: Map<string, Set<ControllerConnection>>;
|
|
60
|
+
probeControllersByAccount: Map<string, Set<ControllerConnection>>;
|
|
60
61
|
machinesByKey: Map<string, MachineConnection>;
|
|
61
62
|
pendingCommandRequests: Map<string, PendingCommandContext>;
|
|
62
63
|
};
|
|
@@ -100,6 +100,7 @@ export declare function createRelayContext(options: CreateRelayContextOptions):
|
|
|
100
100
|
sessionId: string;
|
|
101
101
|
turnId: string;
|
|
102
102
|
}) => PendingCommandContext | undefined;
|
|
103
|
+
probeControllersByAccount: Map<string, Set<ControllerConnection>>;
|
|
103
104
|
isFullControllerToken: typeof isFullControllerToken;
|
|
104
105
|
machineCapabilities: (accountId: string, machineId: string) => MachineSnapshot["capabilities"] | undefined;
|
|
105
106
|
machineSnapshot: (row: MachineRow) => MachineSnapshot;
|
|
@@ -9,7 +9,7 @@ import { clearAcceptedRun as clearConnectionAcceptedRun, clearAcceptedRunsForSes
|
|
|
9
9
|
import { bearerToken, encodePairingCode, tokenDigest } from "./security.js";
|
|
10
10
|
const REWRITE_HEAD_BASIS_PREFIX = "rewrite:";
|
|
11
11
|
export function createRelayContext(options) {
|
|
12
|
-
const { controllersByAccount, machinesByKey, pendingCommandRequests } = createConnectionState();
|
|
12
|
+
const { controllersByAccount, probeControllersByAccount, machinesByKey, pendingCommandRequests } = createConnectionState();
|
|
13
13
|
const now = options.now ?? Date.now;
|
|
14
14
|
function connectedMachine(accountId, machineId) {
|
|
15
15
|
return machinesByKey.get(machineKey(accountId, machineId));
|
|
@@ -86,17 +86,18 @@ export function createRelayContext(options) {
|
|
|
86
86
|
assertScopedAction(tokenScope(record), action);
|
|
87
87
|
}
|
|
88
88
|
function closeRevokedControllerConnections(accountId, deviceId) {
|
|
89
|
-
const controllers
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
for (const controllers of [controllersByAccount.get(accountId), probeControllersByAccount.get(accountId)]) {
|
|
90
|
+
if (!controllers)
|
|
91
|
+
continue;
|
|
92
|
+
for (const connection of controllers) {
|
|
93
|
+
if (connection.deviceId === deviceId) {
|
|
94
|
+
send(connection.socket, {
|
|
95
|
+
type: "server:error",
|
|
96
|
+
code: "AUTH_TOKEN_REVOKED",
|
|
97
|
+
message: "Device has been revoked",
|
|
98
|
+
});
|
|
99
|
+
connection.socket.close(1008, "Device has been revoked");
|
|
100
|
+
}
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
}
|
|
@@ -1021,6 +1022,7 @@ export function createRelayContext(options) {
|
|
|
1021
1022
|
isCommandPending,
|
|
1022
1023
|
isSessionCommandPending,
|
|
1023
1024
|
pendingRunCommand,
|
|
1025
|
+
probeControllersByAccount,
|
|
1024
1026
|
isFullControllerToken,
|
|
1025
1027
|
machineCapabilities,
|
|
1026
1028
|
machineSnapshot,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MachineSnapshot, SessionSnapshot } from "../../../packages/shared/dist/index.js";
|
|
1
|
+
import type { MachineSnapshot, SessionSnapshot, UiProbeCapabilities, UiProbeSurfaceRegistration } from "../../../packages/shared/dist/index.js";
|
|
2
2
|
export type TokenKind = "controller" | "machine";
|
|
3
3
|
export type ScopedTokenScope = {
|
|
4
4
|
name?: string;
|
|
@@ -120,6 +120,8 @@ export type PairingRow = {
|
|
|
120
120
|
};
|
|
121
121
|
export type WebSocketLike = {
|
|
122
122
|
readyState: number;
|
|
123
|
+
/** Bytes queued by the transport for this exact recipient socket. */
|
|
124
|
+
bufferedAmount?: number;
|
|
123
125
|
send(data: string): void;
|
|
124
126
|
close(code?: number, reason?: string): void;
|
|
125
127
|
on(event: "message", cb: (data: Buffer) => void): void;
|
|
@@ -131,12 +133,16 @@ export type ControllerConnection = {
|
|
|
131
133
|
accountId: string;
|
|
132
134
|
token: string;
|
|
133
135
|
deviceId: string | null;
|
|
136
|
+
deviceName: string;
|
|
137
|
+
purpose: "app" | "probe";
|
|
138
|
+
uiProbeSurface?: UiProbeSurfaceRegistration;
|
|
134
139
|
scope: ScopedTokenScope | null;
|
|
135
140
|
clientCapabilities?: {
|
|
136
141
|
daemonLifecycleEventsV1?: boolean;
|
|
137
142
|
daemonAutostartV1?: boolean;
|
|
138
143
|
resourcePreferenceInvalidationV1?: boolean;
|
|
139
144
|
daemonProjectsPreferenceV1?: boolean;
|
|
145
|
+
uiProbe?: UiProbeCapabilities;
|
|
140
146
|
};
|
|
141
147
|
activeRunTurns?: Set<string>;
|
|
142
148
|
expiresAt: number | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ClientMessage, type ServerMessage, type UiProbeCloseReason } from "../../../packages/shared/dist/index.js";
|
|
2
|
+
import type { ControllerConnection } from "./types.js";
|
|
3
|
+
export declare const UI_PROBE_TTL_MS: number;
|
|
4
|
+
export declare const UI_PROBE_REGISTRATION_CLOCK_SKEW_MS = 60000;
|
|
5
|
+
export declare const UI_PROBE_MAX_ACTIVE_PER_ACCOUNT = 5;
|
|
6
|
+
export declare const UI_PROBE_MAX_CONTROL_MESSAGES_PER_SECOND = 20;
|
|
7
|
+
export declare const UI_PROBE_MAX_RECIPIENT_BUFFER_BYTES: number;
|
|
8
|
+
export type UiProbeClientMessage = Extract<ClientMessage, {
|
|
9
|
+
type: "controller:uiProbeList" | "controller:uiProbeOpen" | "controller:uiProbeCommand" | "controller:uiProbeTelemetry" | "controller:uiProbeClose";
|
|
10
|
+
}>;
|
|
11
|
+
type AuthError = {
|
|
12
|
+
code: string;
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
type TimerHandle = ReturnType<typeof setTimeout> | number;
|
|
16
|
+
type UiProbeRouterOptions = {
|
|
17
|
+
now(): number;
|
|
18
|
+
send(connection: ControllerConnection, message: ServerMessage): boolean;
|
|
19
|
+
authError(connection: ControllerConnection): AuthError | null;
|
|
20
|
+
setTimer?(callback: () => void, delayMs: number): TimerHandle;
|
|
21
|
+
clearTimer?(timer: TimerHandle): void;
|
|
22
|
+
};
|
|
23
|
+
export declare function createUiProbeRouter(options: UiProbeRouterOptions): {
|
|
24
|
+
closeAll: (reason?: UiProbeCloseReason) => void;
|
|
25
|
+
disconnect: (connection: ControllerConnection) => void;
|
|
26
|
+
handle: (connection: ControllerConnection, message: UiProbeClientMessage) => void;
|
|
27
|
+
register: (connection: ControllerConnection) => {
|
|
28
|
+
replaced?: ControllerConnection;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare class UiProbeRegistrationError extends Error {
|
|
32
|
+
readonly code: string;
|
|
33
|
+
constructor(code: string, message: string);
|
|
34
|
+
}
|
|
35
|
+
export {};
|