@ai-matrx/desktop-protocol 0.6.0 → 0.10.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.
- package/CHANGELOG.md +66 -0
- package/README.md +13 -0
- package/dist/client.cjs +272 -42
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +9 -2
- package/dist/client.d.ts +9 -2
- package/dist/client.js +272 -42
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +776 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1048 -5
- package/dist/index.d.ts +1048 -5
- package/dist/index.js +776 -5
- package/dist/index.js.map +1 -1
- package/dist/react.d.cts +305 -1
- package/dist/react.d.ts +305 -1
- package/dist/{registry-jTmq1CsI.d.cts → registry-B3NqARgh.d.cts} +1314 -3
- package/dist/{registry-jTmq1CsI.d.ts → registry-B3NqARgh.d.ts} +1314 -3
- package/generated/desktop-protocol.schema.json +4554 -0
- package/package.json +3 -1
- package/scripts/gen_pydantic.py +335 -0
package/dist/client.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BackoffOptions } from '@ai-matrx/realtime';
|
|
2
2
|
export { RECONNECT_ALARM_ATTEMPTS } from '@ai-matrx/realtime';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { W as Welcome, D as DesktopProtocolError, O as OpName, a as OpParams, b as OpResult, c as OPS, S as StreamMeta, E as EndReason, d as EVENTS,
|
|
5
|
-
export { i as isDesktopProtocolError } from './registry-
|
|
4
|
+
import { W as Welcome, D as DesktopProtocolError, C as CloseOutcome, O as OpName, a as OpParams, b as OpResult, c as OPS, S as StreamMeta, E as EndReason, d as EVENTS, e as ClientType } from './registry-B3NqARgh.cjs';
|
|
5
|
+
export { i as isDesktopProtocolError } from './registry-B3NqARgh.cjs';
|
|
6
6
|
import { FrameHeader } from './frame.cjs';
|
|
7
7
|
|
|
8
8
|
/** The subset of the WHATWG WebSocket the client uses — browser, React Native, `ws` and Workers all fit. */
|
|
@@ -49,6 +49,8 @@ interface DesktopClientState {
|
|
|
49
49
|
retryInMs: number | null;
|
|
50
50
|
lastError: DesktopProtocolError | null;
|
|
51
51
|
lastCloseCode: number | null;
|
|
52
|
+
/** Why dialing stopped, when the far side refused us for good (CLOSE_RULES); null otherwise. */
|
|
53
|
+
outcome: CloseOutcome | null;
|
|
52
54
|
}
|
|
53
55
|
type DesktopClientDiagnostic = {
|
|
54
56
|
kind: "malformed_message";
|
|
@@ -112,6 +114,11 @@ interface DesktopClientOptions {
|
|
|
112
114
|
interface RequestOptions {
|
|
113
115
|
signal?: AbortSignal;
|
|
114
116
|
timeoutMs?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Called with the request's id before it is sent, so progress events for it (e.g.
|
|
119
|
+
* `models.progress`, whose payload carries `request_id`) can be matched to this call.
|
|
120
|
+
*/
|
|
121
|
+
onRequestId?: (id: string) => void;
|
|
115
122
|
}
|
|
116
123
|
type StreamOpName = {
|
|
117
124
|
[N in OpName]: (typeof OPS)[N]["kind"] extends "unary" ? never : N;
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BackoffOptions } from '@ai-matrx/realtime';
|
|
2
2
|
export { RECONNECT_ALARM_ATTEMPTS } from '@ai-matrx/realtime';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { W as Welcome, D as DesktopProtocolError, O as OpName, a as OpParams, b as OpResult, c as OPS, S as StreamMeta, E as EndReason, d as EVENTS,
|
|
5
|
-
export { i as isDesktopProtocolError } from './registry-
|
|
4
|
+
import { W as Welcome, D as DesktopProtocolError, C as CloseOutcome, O as OpName, a as OpParams, b as OpResult, c as OPS, S as StreamMeta, E as EndReason, d as EVENTS, e as ClientType } from './registry-B3NqARgh.js';
|
|
5
|
+
export { i as isDesktopProtocolError } from './registry-B3NqARgh.js';
|
|
6
6
|
import { FrameHeader } from './frame.js';
|
|
7
7
|
|
|
8
8
|
/** The subset of the WHATWG WebSocket the client uses — browser, React Native, `ws` and Workers all fit. */
|
|
@@ -49,6 +49,8 @@ interface DesktopClientState {
|
|
|
49
49
|
retryInMs: number | null;
|
|
50
50
|
lastError: DesktopProtocolError | null;
|
|
51
51
|
lastCloseCode: number | null;
|
|
52
|
+
/** Why dialing stopped, when the far side refused us for good (CLOSE_RULES); null otherwise. */
|
|
53
|
+
outcome: CloseOutcome | null;
|
|
52
54
|
}
|
|
53
55
|
type DesktopClientDiagnostic = {
|
|
54
56
|
kind: "malformed_message";
|
|
@@ -112,6 +114,11 @@ interface DesktopClientOptions {
|
|
|
112
114
|
interface RequestOptions {
|
|
113
115
|
signal?: AbortSignal;
|
|
114
116
|
timeoutMs?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Called with the request's id before it is sent, so progress events for it (e.g.
|
|
119
|
+
* `models.progress`, whose payload carries `request_id`) can be matched to this call.
|
|
120
|
+
*/
|
|
121
|
+
onRequestId?: (id: string) => void;
|
|
115
122
|
}
|
|
116
123
|
type StreamOpName = {
|
|
117
124
|
[N in OpName]: (typeof OPS)[N]["kind"] extends "unary" ? never : N;
|
package/dist/client.js
CHANGED
|
@@ -94,7 +94,7 @@ import { z as z2 } from "zod";
|
|
|
94
94
|
// src/schema/primitives.ts
|
|
95
95
|
import { z } from "zod";
|
|
96
96
|
var PROTOCOL_MAJOR = 1;
|
|
97
|
-
var PROTOCOL_MINOR =
|
|
97
|
+
var PROTOCOL_MINOR = 2;
|
|
98
98
|
var PROTOCOL_VERSION = `${PROTOCOL_MAJOR}.${PROTOCOL_MINOR}`;
|
|
99
99
|
var ProtocolVersion = z.string().regex(/^[1-9][0-9]{0,2}\.[0-9]{1,3}$/);
|
|
100
100
|
var RequestId = z.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
|
|
@@ -152,7 +152,22 @@ var ErrorReason = z2.enum([
|
|
|
152
152
|
"resource_expired",
|
|
153
153
|
"control_taken",
|
|
154
154
|
// another client of the same user took control
|
|
155
|
-
"window_exhausted"
|
|
155
|
+
"window_exhausted",
|
|
156
|
+
// 1.2 (REASONS_SINCE): the `models` capability and its supervised Python service
|
|
157
|
+
"runtime_not_installed",
|
|
158
|
+
// NOT_FOUND: install the inference runtime first (models.install)
|
|
159
|
+
"model_not_installed",
|
|
160
|
+
// NOT_FOUND: that model's weights are not on the device
|
|
161
|
+
"unsupported_media",
|
|
162
|
+
// INVALID_PARAMS: the file is not audio or video the decoder reads
|
|
163
|
+
"download_failed",
|
|
164
|
+
// install could not fetch a package or model file
|
|
165
|
+
"service_starting",
|
|
166
|
+
// RESOURCE_BUSY (retryable): the service is starting or restarting
|
|
167
|
+
"service_restarted",
|
|
168
|
+
// RESOURCE_GONE: the service died while this request ran
|
|
169
|
+
"service_failed"
|
|
170
|
+
// INTERNAL: the service could not be started (see models.status)
|
|
156
171
|
]);
|
|
157
172
|
var FsPatchEditFailure = z2.strictObject({ edit_index: nonNegInt, reason: z2.string().max(300) });
|
|
158
173
|
var ProtocolErrorBody = z2.strictObject({
|
|
@@ -281,6 +296,29 @@ var CloseCode = {
|
|
|
281
296
|
};
|
|
282
297
|
var CloseCodeValue = z3.literal([1e3, 1001, 1006, 1012, 1013, 4001, 4003, 4004, 4008, 4009, 4010, 4400, 4426]);
|
|
283
298
|
|
|
299
|
+
// src/close-policy.ts
|
|
300
|
+
var CLOSE_RULES = {
|
|
301
|
+
[CloseCode.NORMAL]: { action: "reconnect", error: ["DEVICE_OFFLINE", "Connection closed"] },
|
|
302
|
+
[CloseCode.GOING_AWAY]: { action: "reconnect_now", error: ["DEVICE_OFFLINE", "Connection lost"] },
|
|
303
|
+
[CloseCode.ABNORMAL]: { action: "reconnect_now", error: ["DEVICE_OFFLINE", "Connection lost"] },
|
|
304
|
+
[CloseCode.DEVICE_RECONNECTED]: { action: "reconnect_now", error: ["DEVICE_OFFLINE", "Device reconnected"] },
|
|
305
|
+
[CloseCode.TRY_AGAIN_LATER]: { action: "reconnect", error: ["DEVICE_OFFLINE", "Relay busy; retrying"] },
|
|
306
|
+
[CloseCode.AUTH_INVALID]: { action: "reauth", outcome: "signed_out", error: ["AUTH_INVALID", "Sign-in was not accepted"] },
|
|
307
|
+
[CloseCode.AUTH_FORBIDDEN]: { action: "stop", outcome: "not_owned", error: ["AUTH_FORBIDDEN", "Not this device's owner"] },
|
|
308
|
+
[CloseCode.DEVICE_NOT_FOUND]: { action: "stop", outcome: "removed", error: ["NOT_FOUND", "Device not found"] },
|
|
309
|
+
[CloseCode.AUTH_EXPIRED]: { action: "reauth", outcome: "signed_out", error: ["AUTH_EXPIRED", "Sign-in expired"] },
|
|
310
|
+
[CloseCode.REPLACED]: { action: "stop", outcome: "replaced", error: ["CONFLICT", "Replaced by a newer connection"] },
|
|
311
|
+
[CloseCode.DEVICE_REVOKED]: { action: "stop", outcome: "revoked", error: ["AUTH_DEVICE_REVOKED", "This device was revoked"] },
|
|
312
|
+
// A malformed message, or a connection the relay lost track of: a fresh socket starts clean.
|
|
313
|
+
[CloseCode.PROTOCOL_ERROR]: { action: "reconnect", error: ["PROTOCOL_ERROR", "Connection reset by the relay"] },
|
|
314
|
+
[CloseCode.UPGRADE_REQUIRED]: { action: "stop", outcome: "upgrade_required", error: ["PROTOCOL_VERSION_UNSUPPORTED", "Protocol upgrade required"] }
|
|
315
|
+
};
|
|
316
|
+
var UNKNOWN = { action: "reconnect", error: ["DEVICE_OFFLINE", "Connection lost"] };
|
|
317
|
+
function closeRule(code) {
|
|
318
|
+
if (code === null || code === void 0) return CLOSE_RULES[CloseCode.ABNORMAL];
|
|
319
|
+
return CLOSE_RULES[code] ?? UNKNOWN;
|
|
320
|
+
}
|
|
321
|
+
|
|
284
322
|
// src/schema/frame.ts
|
|
285
323
|
import { z as z4 } from "zod";
|
|
286
324
|
var FrameHeader = z4.strictObject({
|
|
@@ -612,34 +650,115 @@ var SessionListParams = z8.strictObject({});
|
|
|
612
650
|
var SessionListResult = z8.strictObject({ resources: z8.array(ResourceInfo) });
|
|
613
651
|
var SessionControlChangedEvent = z8.strictObject({ controller_client_id: z8.string().max(64).nullable(), your_mode: z8.enum(["control", "read_only"]) });
|
|
614
652
|
|
|
615
|
-
// src/schema/
|
|
653
|
+
// src/schema/models.ts
|
|
616
654
|
import { z as z9 } from "zod";
|
|
617
|
-
var
|
|
618
|
-
|
|
655
|
+
var TranscriptionModelId = z9.enum(["tiny", "base", "small", "large-v3-turbo"]);
|
|
656
|
+
var MlRuntimeState = z9.enum(["not_installed", "installing", "installed", "failed"]);
|
|
657
|
+
var MlInstallPhase = z9.enum(["packages", "model", "verifying"]);
|
|
658
|
+
var MlInstallProgress = z9.strictObject({
|
|
659
|
+
phase: MlInstallPhase,
|
|
660
|
+
/** What is being fetched now: a package or a model file name. */
|
|
661
|
+
item: z9.string().max(200),
|
|
662
|
+
bytes_done: nonNegInt,
|
|
663
|
+
/** Null while the total is not known yet. */
|
|
664
|
+
bytes_total: nonNegInt.nullable()
|
|
665
|
+
});
|
|
666
|
+
var TranscriptionModelInfo = z9.strictObject({
|
|
667
|
+
id: TranscriptionModelId,
|
|
668
|
+
/** Download size of the weights. */
|
|
669
|
+
size_bytes: nonNegInt,
|
|
670
|
+
multilingual: z9.boolean(),
|
|
671
|
+
installed: z9.boolean()
|
|
672
|
+
});
|
|
673
|
+
var TranscriptSegment = z9.strictObject({
|
|
674
|
+
start_s: z9.number().min(0),
|
|
675
|
+
end_s: z9.number().min(0),
|
|
676
|
+
text: z9.string().max(4e3)
|
|
677
|
+
});
|
|
678
|
+
var MlStatusParams = z9.strictObject({});
|
|
679
|
+
var MlStatusResult = z9.strictObject({
|
|
680
|
+
runtime: MlRuntimeState,
|
|
681
|
+
/** Non-null while runtime or model files are being installed. */
|
|
682
|
+
install: MlInstallProgress.nullable(),
|
|
683
|
+
/** The last install failure, one human line; null when the last install succeeded or none ran. */
|
|
684
|
+
error: z9.string().max(300).nullable(),
|
|
685
|
+
/** Where inference runs once the runtime is installed: `cuda` only when a usable GPU is present. */
|
|
686
|
+
accelerator: z9.enum(["cpu", "cuda"]),
|
|
687
|
+
models: z9.array(TranscriptionModelInfo).max(16),
|
|
688
|
+
default_model: TranscriptionModelId
|
|
689
|
+
});
|
|
690
|
+
var ModelsInstallParams = z9.strictObject({ model: TranscriptionModelId.optional() });
|
|
691
|
+
var ModelsInstallResult = z9.strictObject({ model: TranscriptionModelId, elapsed_ms: nonNegInt });
|
|
692
|
+
var ModelsTranscribeParams = z9.strictObject({
|
|
693
|
+
path: AbsPath,
|
|
694
|
+
model: TranscriptionModelId.optional(),
|
|
695
|
+
language: z9.string().regex(/^[a-z]{2,3}$/).optional(),
|
|
696
|
+
task: z9.enum(["transcribe", "translate"]).default("transcribe")
|
|
697
|
+
});
|
|
698
|
+
var ModelsTranscribeResult = z9.strictObject({
|
|
699
|
+
text: z9.string().max(4e5),
|
|
700
|
+
language: z9.string().max(8),
|
|
701
|
+
language_probability: z9.number().min(0).max(1),
|
|
702
|
+
duration_s: z9.number().min(0),
|
|
703
|
+
model: TranscriptionModelId,
|
|
704
|
+
segments: z9.array(TranscriptSegment).max(2e4),
|
|
705
|
+
elapsed_ms: nonNegInt
|
|
706
|
+
});
|
|
707
|
+
var MlProgress = z9.strictObject({
|
|
708
|
+
kind: z9.enum(["install", "transcribe"]),
|
|
709
|
+
/** 0–1 when known (transcribe: audio covered so far; install: bytes so far). */
|
|
710
|
+
fraction: z9.number().min(0).max(1).nullable(),
|
|
711
|
+
install: MlInstallProgress.nullable(),
|
|
712
|
+
/** The segment just transcribed (transcribe only): text appears as it is recognised. */
|
|
713
|
+
segment: TranscriptSegment.nullable()
|
|
714
|
+
});
|
|
715
|
+
var MlServiceState = z9.enum(["starting", "ready", "restarting", "failed", "stopped"]);
|
|
716
|
+
var MlServiceStatus = z9.strictObject({
|
|
717
|
+
state: MlServiceState,
|
|
718
|
+
pid: nonNegInt.nullable(),
|
|
719
|
+
/** Restarts since the app started (crashes, missed health checks). */
|
|
720
|
+
restarts: nonNegInt,
|
|
721
|
+
/** The bundled interpreter's version once the service has answered, e.g. "3.13.15". */
|
|
722
|
+
python: z9.string().max(32).nullable(),
|
|
723
|
+
/** Why the service is restarting or failed, one human line. */
|
|
724
|
+
error: z9.string().max(300).nullable()
|
|
725
|
+
});
|
|
726
|
+
var ModelsStatusParams = z9.strictObject({});
|
|
727
|
+
var ModelsStatusResult = z9.strictObject({
|
|
728
|
+
service: MlServiceStatus,
|
|
729
|
+
/** Null until the service has answered (starting, restarting, failed). */
|
|
730
|
+
transcription: MlStatusResult.nullable()
|
|
731
|
+
});
|
|
732
|
+
var ModelsProgressEvent = z9.strictObject({ request_id: RequestId, progress: MlProgress });
|
|
733
|
+
|
|
734
|
+
// src/schema/relay.ts
|
|
735
|
+
import { z as z10 } from "zod";
|
|
736
|
+
var RelayDeviceStatusEvent = z10.strictObject({
|
|
737
|
+
online: z10.boolean(),
|
|
619
738
|
/**
|
|
620
739
|
* When the device last went online/offline; null = it has never connected (since 1.1 —
|
|
621
740
|
* NULL_SINCE: a 1.0 reader is sent 0, its old meaning).
|
|
622
741
|
*/
|
|
623
742
|
since_ms: epochMs.nullable(),
|
|
624
|
-
app_version:
|
|
743
|
+
app_version: z10.string().max(32).nullable(),
|
|
625
744
|
protocol_version: ProtocolVersion.nullable()
|
|
626
745
|
});
|
|
627
|
-
var RelayAuthExpiringEvent =
|
|
628
|
-
var RelayCallRequest =
|
|
629
|
-
cap:
|
|
630
|
-
op:
|
|
631
|
-
params:
|
|
632
|
-
timeout_ms:
|
|
746
|
+
var RelayAuthExpiringEvent = z10.strictObject({ expires_at_ms: epochMs });
|
|
747
|
+
var RelayCallRequest = z10.strictObject({
|
|
748
|
+
cap: z10.string().min(1).max(32),
|
|
749
|
+
op: z10.string().min(1).max(64),
|
|
750
|
+
params: z10.unknown(),
|
|
751
|
+
timeout_ms: z10.int().min(100).max(37e5).default(7e4)
|
|
633
752
|
});
|
|
634
|
-
var RelayCallResponse =
|
|
635
|
-
|
|
636
|
-
ok:
|
|
637
|
-
result:
|
|
753
|
+
var RelayCallResponse = z10.discriminatedUnion("ok", [
|
|
754
|
+
z10.strictObject({
|
|
755
|
+
ok: z10.literal(true),
|
|
756
|
+
result: z10.unknown(),
|
|
638
757
|
/** Since 1.1: the version the device negotiated for this call (its welcome). */
|
|
639
758
|
protocol_version: ProtocolVersion.optional()
|
|
640
759
|
}),
|
|
641
|
-
|
|
642
|
-
ok:
|
|
760
|
+
z10.strictObject({
|
|
761
|
+
ok: z10.literal(false),
|
|
643
762
|
error: ProtocolErrorBody,
|
|
644
763
|
/** Since 1.1, as above (absent when the device never answered hello). */
|
|
645
764
|
protocol_version: ProtocolVersion.optional()
|
|
@@ -647,19 +766,111 @@ var RelayCallResponse = z9.discriminatedUnion("ok", [
|
|
|
647
766
|
]);
|
|
648
767
|
var RELAY_CALL_HELLO_ID = "hello";
|
|
649
768
|
var RELAY_CALL_REQUEST_ID = "call";
|
|
650
|
-
var RelayCallHello =
|
|
651
|
-
type:
|
|
652
|
-
id:
|
|
769
|
+
var RelayCallHello = z10.strictObject({
|
|
770
|
+
type: z10.literal("hello"),
|
|
771
|
+
id: z10.literal(RELAY_CALL_HELLO_ID),
|
|
653
772
|
protocolVersion: ProtocolVersion,
|
|
654
|
-
clientType:
|
|
655
|
-
clientVersion:
|
|
773
|
+
clientType: z10.literal("server"),
|
|
774
|
+
clientVersion: z10.string().min(1).max(32)
|
|
775
|
+
});
|
|
776
|
+
var RelayCallEnvelope = z10.strictObject({
|
|
777
|
+
id: z10.literal(RELAY_CALL_REQUEST_ID),
|
|
778
|
+
type: z10.literal("request"),
|
|
779
|
+
cap: z10.string().min(1).max(32),
|
|
780
|
+
op: z10.string().min(1).max(64),
|
|
781
|
+
params: z10.unknown()
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// src/schema/apps.ts
|
|
785
|
+
import { z as z11 } from "zod";
|
|
786
|
+
var AppKey = z11.string().regex(/^[a-z][a-z0-9_-]{0,39}$/);
|
|
787
|
+
var AppProfileId = z11.string().regex(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/);
|
|
788
|
+
var AppProfileLocator = z11.string().min(1).max(512).regex(/^[A-Za-z0-9 _()+@,-][A-Za-z0-9 ._()+@,-]{0,127}(\/[A-Za-z0-9 _()+@,-][A-Za-z0-9 ._()+@,-]{0,127}){0,7}$/);
|
|
789
|
+
var AppSupport = z11.enum(["supported", "partial", "unsupported"]);
|
|
790
|
+
var AppFeatureStatus = z11.strictObject({
|
|
791
|
+
status: AppSupport,
|
|
792
|
+
/** One line, set whenever status is not "supported". */
|
|
793
|
+
reason: z11.string().max(140).nullable()
|
|
794
|
+
});
|
|
795
|
+
var AppInfo = z11.strictObject({
|
|
796
|
+
app_key: AppKey,
|
|
797
|
+
name: z11.string().max(64),
|
|
798
|
+
installed: z11.boolean(),
|
|
799
|
+
/** The app's own version string, or null when it is not installed or does not say. */
|
|
800
|
+
version: z11.string().max(64).nullable(),
|
|
801
|
+
/** The install found (an .app bundle, an .exe, a binary), or null. */
|
|
802
|
+
location: AbsPath.nullable(),
|
|
803
|
+
/** Open the app at all on this OS. */
|
|
804
|
+
open: AppFeatureStatus,
|
|
805
|
+
/** Separate profiles (one account each) on this OS. */
|
|
806
|
+
profiles: AppFeatureStatus,
|
|
807
|
+
/** Plan usage read from the app's own files on this OS. */
|
|
808
|
+
usage: AppFeatureStatus
|
|
809
|
+
});
|
|
810
|
+
var AppUsageReading = z11.strictObject({
|
|
811
|
+
window: z11.enum(["five_hour", "weekly", "extra"]),
|
|
812
|
+
/** 0–100 for five_hour and weekly; extra-usage spend as a percent of its cap (may pass 100). */
|
|
813
|
+
percent: z11.number().min(0).max(1e4),
|
|
814
|
+
/** When the app recorded it. */
|
|
815
|
+
sampled_at_ms: epochMs,
|
|
816
|
+
/** True when the window has rolled over since the sample, so the figure has reset. */
|
|
817
|
+
reset_since: z11.boolean()
|
|
818
|
+
});
|
|
819
|
+
var AppProfile = z11.strictObject({
|
|
820
|
+
/** The row's id; null for a folder found on disk that no row names yet. */
|
|
821
|
+
id: AppProfileId.nullable(),
|
|
822
|
+
app_key: AppKey,
|
|
823
|
+
display_name: z11.string().min(1).max(120),
|
|
824
|
+
/** The account the person means to be signed in here; null when not stated (never guessed). */
|
|
825
|
+
account_identity: z11.string().min(3).max(320).nullable(),
|
|
826
|
+
profile_locator: AppProfileLocator.nullable(),
|
|
827
|
+
/** The device the row is tied to; null when the row names none. */
|
|
828
|
+
device_id: DeviceId.nullable(),
|
|
829
|
+
/** The folder this profile resolves to on this device; null for a row tied to another device. */
|
|
830
|
+
folder: AbsPath.nullable(),
|
|
831
|
+
/** The folder exists on this device. */
|
|
832
|
+
on_disk: z11.boolean(),
|
|
833
|
+
/** An instance with this profile is running; null when it could not be determined. */
|
|
834
|
+
running: z11.boolean().nullable(),
|
|
835
|
+
/** Usage the app recorded for this profile; null when the app exposes none here. */
|
|
836
|
+
usage: z11.array(AppUsageReading).nullable(),
|
|
837
|
+
last_opened_ms: epochMs.nullable()
|
|
656
838
|
});
|
|
657
|
-
var
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
839
|
+
var AppsListParams = z11.strictObject({});
|
|
840
|
+
var AppsListResult = z11.strictObject({ apps: z11.array(AppInfo) });
|
|
841
|
+
var AppsProfilesListParams = z11.strictObject({
|
|
842
|
+
/** Only this app's profiles; every app's when absent. */
|
|
843
|
+
app_key: AppKey.optional()
|
|
844
|
+
});
|
|
845
|
+
var AppsProfilesListResult = z11.strictObject({
|
|
846
|
+
profiles: z11.array(AppProfile),
|
|
847
|
+
/**
|
|
848
|
+
* Whether the saved rows could be read: false while signed out or offline, when only folders
|
|
849
|
+
* found on disk are listed.
|
|
850
|
+
*/
|
|
851
|
+
registry: z11.enum(["ok", "signed_out", "unavailable"])
|
|
852
|
+
});
|
|
853
|
+
var AppsProfilesSaveParams = z11.strictObject({
|
|
854
|
+
id: AppProfileId.optional(),
|
|
855
|
+
app_key: AppKey,
|
|
856
|
+
display_name: z11.string().min(1).max(120),
|
|
857
|
+
account_identity: z11.string().min(3).max(320).nullable(),
|
|
858
|
+
/** On create: the folder to use (one found on disk), or null for the app's default profile. Ignored on update. */
|
|
859
|
+
profile_locator: AppProfileLocator.nullable(),
|
|
860
|
+
/** On create with profile_locator null: make a new folder named after display_name instead. */
|
|
861
|
+
new_folder: z11.boolean().default(false)
|
|
862
|
+
});
|
|
863
|
+
var AppsProfilesSaveResult = AppProfile;
|
|
864
|
+
var AppsProfilesRemoveParams = z11.strictObject({ id: AppProfileId });
|
|
865
|
+
var AppsProfilesRemoveResult = z11.strictObject({ removed: z11.boolean() });
|
|
866
|
+
var AppsOpenParams = z11.strictObject({
|
|
867
|
+
app_key: AppKey,
|
|
868
|
+
profile_id: AppProfileId.optional()
|
|
869
|
+
});
|
|
870
|
+
var AppsOpenResult = z11.strictObject({
|
|
871
|
+
/** True when an instance with this profile was already running; it is brought forward. */
|
|
872
|
+
already_running: z11.boolean(),
|
|
873
|
+
folder: AbsPath.nullable()
|
|
663
874
|
});
|
|
664
875
|
|
|
665
876
|
// src/schema/registry.ts
|
|
@@ -688,7 +899,17 @@ var OPS = {
|
|
|
688
899
|
"session.attach": { params: SessionAttachParams, result: SessionAttachResult, kind: "stream", since: "1.0", errors: ["RESOURCE_GONE", "AUTH_FORBIDDEN", "RESOURCE_BUSY"] },
|
|
689
900
|
"session.detach": { params: SessionDetachParams, result: SessionDetachResult, kind: "unary", since: "1.0", errors: ["RESOURCE_GONE"] },
|
|
690
901
|
"session.release": { params: SessionReleaseParams, result: SessionReleaseResult, kind: "unary", since: "1.0", errors: ["RESOURCE_GONE", "AUTH_FORBIDDEN"] },
|
|
691
|
-
"session.list": { params: SessionListParams, result: SessionListResult, kind: "unary", since: "1.0", errors: ["INTERNAL"] }
|
|
902
|
+
"session.list": { params: SessionListParams, result: SessionListResult, kind: "unary", since: "1.0", errors: ["INTERNAL"] },
|
|
903
|
+
// Local models (SPEC §6), served by the `ml` Python service through SERVICES below.
|
|
904
|
+
"models.status": { params: ModelsStatusParams, result: ModelsStatusResult, kind: "unary", since: "1.2", errors: ["INTERNAL"] },
|
|
905
|
+
"models.install": { params: ModelsInstallParams, result: ModelsInstallResult, kind: "unary", since: "1.2", errors: ["RESOURCE_BUSY", "RESOURCE_GONE", "TIMEOUT", "CANCELLED", "INTERNAL"] },
|
|
906
|
+
"models.transcribe": { params: ModelsTranscribeParams, result: ModelsTranscribeResult, kind: "unary", since: "1.2", errors: ["NOT_FOUND", "INVALID_PARAMS", "PERMISSION_DENIED_OS", "RESOURCE_BUSY", "RESOURCE_GONE", "CANCELLED", "PAYLOAD_TOO_LARGE", "INTERNAL"] },
|
|
907
|
+
// Desktop apps and their profiles (SPEC §6 apps); profiles are rows in users.desktop_app_profile.
|
|
908
|
+
"apps.list": { params: AppsListParams, result: AppsListResult, kind: "unary", since: "1.2", errors: ["INTERNAL"] },
|
|
909
|
+
"apps.profiles.list": { params: AppsProfilesListParams, result: AppsProfilesListResult, kind: "unary", since: "1.2", errors: ["NOT_FOUND", "INVALID_PARAMS", "INTERNAL"] },
|
|
910
|
+
"apps.profiles.save": { params: AppsProfilesSaveParams, result: AppsProfilesSaveResult, kind: "unary", since: "1.2", errors: ["AUTH_REQUIRED", "AUTH_FORBIDDEN", "NOT_FOUND", "INVALID_PARAMS", "CONFLICT", "PLATFORM_UNSUPPORTED", "DEVICE_OFFLINE", "INTERNAL"] },
|
|
911
|
+
"apps.profiles.remove": { params: AppsProfilesRemoveParams, result: AppsProfilesRemoveResult, kind: "unary", since: "1.2", errors: ["AUTH_REQUIRED", "AUTH_FORBIDDEN", "NOT_FOUND", "DEVICE_OFFLINE", "INTERNAL"] },
|
|
912
|
+
"apps.open": { params: AppsOpenParams, result: AppsOpenResult, kind: "unary", since: "1.2", errors: ["NOT_FOUND", "PLATFORM_UNSUPPORTED", "AUTH_REQUIRED", "DEVICE_OFFLINE", "INTERNAL"] }
|
|
692
913
|
};
|
|
693
914
|
var EVENTS = {
|
|
694
915
|
"fs.changed": FsChangedEvent,
|
|
@@ -696,19 +917,13 @@ var EVENTS = {
|
|
|
696
917
|
"exec.title": ExecTitleEvent,
|
|
697
918
|
"exec.cwd": ExecCwdEvent,
|
|
698
919
|
"session.control_changed": SessionControlChangedEvent,
|
|
920
|
+
"models.progress": ModelsProgressEvent,
|
|
699
921
|
"relay.device_status": RelayDeviceStatusEvent,
|
|
700
922
|
"relay.auth_expiring": RelayAuthExpiringEvent
|
|
701
923
|
};
|
|
702
924
|
|
|
703
925
|
// src/client/client.ts
|
|
704
926
|
var WS_OPEN = 1;
|
|
705
|
-
var TERMINAL_CLOSE = /* @__PURE__ */ new Map([
|
|
706
|
-
[CloseCode.DEVICE_REVOKED, ["AUTH_DEVICE_REVOKED", "This device was revoked"]],
|
|
707
|
-
[CloseCode.AUTH_FORBIDDEN, ["AUTH_FORBIDDEN", "Not this device's owner"]],
|
|
708
|
-
[CloseCode.DEVICE_NOT_FOUND, ["NOT_FOUND", "Device not found"]],
|
|
709
|
-
[CloseCode.REPLACED, ["CONFLICT", "Replaced by a newer connection"]],
|
|
710
|
-
[CloseCode.UPGRADE_REQUIRED, ["PROTOCOL_VERSION_UNSUPPORTED", "Protocol upgrade required"]]
|
|
711
|
-
]);
|
|
712
927
|
var defaultTimers = {
|
|
713
928
|
setTimeout: (fn, ms) => globalThis.setTimeout(fn, ms),
|
|
714
929
|
clearTimeout: (handle) => globalThis.clearTimeout(handle),
|
|
@@ -751,8 +966,10 @@ function createDesktopClient(options) {
|
|
|
751
966
|
attempts: 0,
|
|
752
967
|
retryInMs: null,
|
|
753
968
|
lastError: null,
|
|
754
|
-
lastCloseCode: null
|
|
969
|
+
lastCloseCode: null,
|
|
970
|
+
outcome: null
|
|
755
971
|
};
|
|
972
|
+
let authRefusals = 0;
|
|
756
973
|
const listeners = /* @__PURE__ */ new Set();
|
|
757
974
|
const readyWaiters = /* @__PURE__ */ new Set();
|
|
758
975
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -787,6 +1004,7 @@ function createDesktopClient(options) {
|
|
|
787
1004
|
}
|
|
788
1005
|
function connect() {
|
|
789
1006
|
if (state.status === "connecting" || state.status === "handshaking" || state.status === "open") return;
|
|
1007
|
+
if (state.status === "closed" || state.status === "idle") authRefusals = 0;
|
|
790
1008
|
closedByUser = false;
|
|
791
1009
|
if (reconnectTimer !== null) {
|
|
792
1010
|
timers.clearTimeout(reconnectTimer);
|
|
@@ -868,8 +1086,11 @@ function createDesktopClient(options) {
|
|
|
868
1086
|
timers.clearTimeout(probeTimer);
|
|
869
1087
|
probeTimer = null;
|
|
870
1088
|
}
|
|
871
|
-
const
|
|
872
|
-
|
|
1089
|
+
const rule = code === null ? null : closeRule(code);
|
|
1090
|
+
if (rule?.action === "reauth") authRefusals++;
|
|
1091
|
+
const stopped = rule !== null && (rule.action === "stop" || rule.action === "reauth" && authRefusals >= 2);
|
|
1092
|
+
const terminal = stopped && rule ? rule : void 0;
|
|
1093
|
+
const lost = error ?? (terminal ? new DesktopProtocolError(terminal.error[0], terminal.error[1]) : rule && rule.action === "reauth" ? new DesktopProtocolError(rule.error[0], rule.error[1], { retryable: true }) : new DesktopProtocolError("DEVICE_OFFLINE", "Connection lost", { retryable: true }));
|
|
873
1094
|
for (const [id, p] of pending) {
|
|
874
1095
|
pending.delete(id);
|
|
875
1096
|
p.cleanup();
|
|
@@ -886,7 +1107,14 @@ function createDesktopClient(options) {
|
|
|
886
1107
|
endStream(s, null, lost);
|
|
887
1108
|
}
|
|
888
1109
|
if (closedByUser || terminal) {
|
|
889
|
-
setState({
|
|
1110
|
+
setState({
|
|
1111
|
+
status: "closed",
|
|
1112
|
+
welcome: null,
|
|
1113
|
+
lastError: closedByUser ? state.lastError : lost,
|
|
1114
|
+
lastCloseCode: code,
|
|
1115
|
+
retryInMs: null,
|
|
1116
|
+
outcome: closedByUser ? null : terminal?.outcome ?? null
|
|
1117
|
+
});
|
|
890
1118
|
rejectReadyWaiters(lost);
|
|
891
1119
|
return;
|
|
892
1120
|
}
|
|
@@ -1040,7 +1268,8 @@ function createDesktopClient(options) {
|
|
|
1040
1268
|
}
|
|
1041
1269
|
helloId = null;
|
|
1042
1270
|
backoff.markConnected();
|
|
1043
|
-
|
|
1271
|
+
authRefusals = 0;
|
|
1272
|
+
setState({ status: "open", welcome: welcome.data, attempts: 0, retryInMs: null, lastError: null, outcome: null });
|
|
1044
1273
|
for (const w of [...readyWaiters]) {
|
|
1045
1274
|
readyWaiters.delete(w);
|
|
1046
1275
|
w.resolve(welcome.data);
|
|
@@ -1202,6 +1431,7 @@ function createDesktopClient(options) {
|
|
|
1202
1431
|
}
|
|
1203
1432
|
await ready(opts);
|
|
1204
1433
|
const id = newId("r");
|
|
1434
|
+
if (opts.onRequestId) safeCall("onRequestId", () => opts.onRequestId?.(id));
|
|
1205
1435
|
return new Promise((resolve, reject) => {
|
|
1206
1436
|
const timeoutMs = opts.timeoutMs ?? requestTimeoutMs;
|
|
1207
1437
|
const timer = timers.setTimeout(() => {
|