@ai-matrx/desktop-protocol 0.17.1 → 0.17.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.
@@ -127,6 +127,12 @@ declare class DesktopProtocolError extends Error {
127
127
  data?: ErrorData;
128
128
  cause?: unknown;
129
129
  });
130
+ /**
131
+ * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
132
+ * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
133
+ * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
134
+ */
135
+ static from(caught: unknown): DesktopProtocolError;
130
136
  /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
131
137
  toBody(): {
132
138
  code: ErrorCodeName;
@@ -765,8 +771,8 @@ declare const OPS: {
765
771
  page_token: z.ZodOptional<z.ZodString>;
766
772
  show_hidden: z.ZodDefault<z.ZodBoolean>;
767
773
  sort: z.ZodDefault<z.ZodEnum<{
768
- kind: "kind";
769
774
  name: "name";
775
+ kind: "kind";
770
776
  size: "size";
771
777
  mtime: "mtime";
772
778
  }>>;
@@ -3206,8 +3212,8 @@ declare const NAMED_SCHEMAS: {
3206
3212
  page_token: z.ZodOptional<z.ZodString>;
3207
3213
  show_hidden: z.ZodDefault<z.ZodBoolean>;
3208
3214
  sort: z.ZodDefault<z.ZodEnum<{
3209
- kind: "kind";
3210
3215
  name: "name";
3216
+ kind: "kind";
3211
3217
  size: "size";
3212
3218
  mtime: "mtime";
3213
3219
  }>>;
@@ -127,6 +127,12 @@ declare class DesktopProtocolError extends Error {
127
127
  data?: ErrorData;
128
128
  cause?: unknown;
129
129
  });
130
+ /**
131
+ * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
132
+ * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
133
+ * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
134
+ */
135
+ static from(caught: unknown): DesktopProtocolError;
130
136
  /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
131
137
  toBody(): {
132
138
  code: ErrorCodeName;
@@ -765,8 +771,8 @@ declare const OPS: {
765
771
  page_token: z.ZodOptional<z.ZodString>;
766
772
  show_hidden: z.ZodDefault<z.ZodBoolean>;
767
773
  sort: z.ZodDefault<z.ZodEnum<{
768
- kind: "kind";
769
774
  name: "name";
775
+ kind: "kind";
770
776
  size: "size";
771
777
  mtime: "mtime";
772
778
  }>>;
@@ -3206,8 +3212,8 @@ declare const NAMED_SCHEMAS: {
3206
3212
  page_token: z.ZodOptional<z.ZodString>;
3207
3213
  show_hidden: z.ZodDefault<z.ZodBoolean>;
3208
3214
  sort: z.ZodDefault<z.ZodEnum<{
3209
- kind: "kind";
3210
3215
  name: "name";
3216
+ kind: "kind";
3211
3217
  size: "size";
3212
3218
  mtime: "mtime";
3213
3219
  }>>;
package/dist/react.cjs CHANGED
@@ -54,7 +54,7 @@ function bindDesktopWake(client, options = {}) {
54
54
  }
55
55
 
56
56
  // src/error.ts
57
- var DesktopProtocolError = class extends Error {
57
+ var DesktopProtocolError = class _DesktopProtocolError extends Error {
58
58
  code;
59
59
  retryable;
60
60
  data;
@@ -65,6 +65,23 @@ var DesktopProtocolError = class extends Error {
65
65
  this.retryable = options.retryable ?? false;
66
66
  this.data = options.data;
67
67
  }
68
+ /**
69
+ * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
70
+ * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
71
+ * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
72
+ */
73
+ static from(caught) {
74
+ if (caught instanceof _DesktopProtocolError) return caught;
75
+ const e = caught;
76
+ if (e && typeof e === "object" && e.name === "DesktopProtocolError" && typeof e.code === "string") {
77
+ return new _DesktopProtocolError(e.code, typeof e.message === "string" ? e.message : String(e.code), {
78
+ retryable: e.retryable === true,
79
+ ...e.data && typeof e.data === "object" ? { data: e.data } : {},
80
+ cause: caught
81
+ });
82
+ }
83
+ return new _DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught });
84
+ }
68
85
  /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
69
86
  toBody() {
70
87
  return this.data === void 0 ? { code: this.code, message: this.message, retryable: this.retryable } : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };
@@ -120,7 +137,7 @@ function useDesktopRequest(op, params, options = {}) {
120
137
  }
121
138
  return result;
122
139
  } catch (caught) {
123
- const err = caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError("INTERNAL", String(caught), { cause: caught });
140
+ const err = DesktopProtocolError.from(caught);
124
141
  if (runId === runRef.current && err.code !== "CANCELLED") setError(err);
125
142
  return void 0;
126
143
  } finally {
@@ -182,7 +199,8 @@ function useDesktopAction(options = {}) {
182
199
  await onDoneRef.current?.();
183
200
  return true;
184
201
  } catch (caught) {
185
- setError(caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught }));
202
+ const err = DesktopProtocolError.from(caught);
203
+ if (err.code !== "CANCELLED") setError(err);
186
204
  return false;
187
205
  } finally {
188
206
  setBusy(false);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/client/wake.ts","../src/error.ts"],"sourcesContent":["/**\n * `@ai-matrx/desktop-protocol/react` — hooks over the client: provider, connection state, typed\n * one-shot requests, events. The client owns every hard part (reconnect, reattach, credits); these\n * hooks only bind it to React's lifecycle.\n */\nimport { createContext, createElement, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { Context, ReactNode } from \"react\";\nimport type { DesktopClient, DesktopClientState, EventHandler, EventName, RequestOptions } from \"./client/types\";\nimport { bindDesktopWake } from \"./client/wake\";\nimport { DesktopProtocolError } from \"./error\";\nimport type { OpName, OpParams, OpResult } from \"./schema/registry\";\n\n// The context lives on globalThis: an ESM and a CJS copy of this entry in one app (Next.js\n// does this) would otherwise create two contexts and a provider the hooks cannot see.\nconst CONTEXT_SLOT = Symbol.for(\"ai-matrx.desktop-protocol.client-context\");\nfunction clientContext(): Context<DesktopClient | null> {\n const slot = globalThis as { [CONTEXT_SLOT]?: Context<DesktopClient | null> };\n return (slot[CONTEXT_SLOT] ??= createContext<DesktopClient | null>(null));\n}\n\nexport function DesktopClientProvider(props: { client: DesktopClient; children?: ReactNode }): ReactNode {\n return createElement(clientContext().Provider, { value: props.client }, props.children);\n}\n\n/** The client from the nearest provider (or the one passed in). Throws when there is neither. */\nexport function useDesktopClient(client?: DesktopClient): DesktopClient {\n const fromContext = useContext(clientContext());\n const resolved = client ?? fromContext;\n if (!resolved) throw new Error(\"useDesktopClient: wrap the tree in <DesktopClientProvider client={…}> or pass a client\");\n return resolved;\n}\n\n/** Live connection state (status, welcome, attempts, retryInMs, lastError). Connects on mount when idle. */\nexport function useDesktopConnection(client?: DesktopClient): DesktopClientState {\n const c = useDesktopClient(client);\n const state = useSyncExternalStore(c.subscribe, c.getState, c.getState);\n useEffect(() => {\n if (c.getState().status === \"idle\") c.connect();\n }, [c]);\n return state;\n}\n\nexport interface DesktopRequestState<N extends OpName> {\n data: OpResult<N> | undefined;\n error: DesktopProtocolError | undefined;\n loading: boolean;\n /** Re-run now; resolves with the fresh result. */\n refetch: () => Promise<OpResult<N> | undefined>;\n}\n\nexport interface UseDesktopRequestOptions extends Omit<RequestOptions, \"signal\"> {\n /** Default true. False = do not run until refetch() or enabled flips true. */\n enabled?: boolean;\n /** Re-run every `refreshMs` while the page is visible, and at once when it becomes visible\n * again. A hidden window never polls. Default: no refresh. */\n refreshMs?: number;\n client?: DesktopClient;\n}\n\n/**\n * Run a unary op and keep its latest result. Re-runs when `op` or the params' JSON changes, and\n * after a reconnect if the last attempt failed with a retryable error. Aborts on unmount.\n */\nexport function useDesktopRequest<N extends OpName>(op: N, params: OpParams<N>, options: UseDesktopRequestOptions = {}): DesktopRequestState<N> {\n const c = useDesktopClient(options.client);\n const enabled = options.enabled ?? true;\n const key = JSON.stringify(params);\n const [data, setData] = useState<OpResult<N> | undefined>(undefined);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(enabled);\n const paramsRef = useRef(params);\n paramsRef.current = params;\n const runRef = useRef(0);\n const abortRef = useRef<AbortController | null>(null);\n const timeoutMs = options.timeoutMs;\n\n const run = useCallback(async (): Promise<OpResult<N> | undefined> => {\n const runId = ++runRef.current;\n abortRef.current?.abort();\n const abort = new AbortController();\n abortRef.current = abort;\n setLoading(true);\n try {\n const result = await c.request(op, paramsRef.current, timeoutMs === undefined ? { signal: abort.signal } : { signal: abort.signal, timeoutMs });\n if (runId === runRef.current) {\n setData(result);\n setError(undefined);\n }\n return result;\n } catch (caught) {\n const err = caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError(\"INTERNAL\", String(caught), { cause: caught });\n if (runId === runRef.current && err.code !== \"CANCELLED\") setError(err);\n return undefined;\n } finally {\n if (runId === runRef.current) setLoading(false);\n }\n // `key` stands for the params' content: a new object with equal JSON is not a new request.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [c, op, key, timeoutMs]);\n\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n void run();\n return () => {\n abortRef.current?.abort();\n };\n }, [enabled, run]);\n\n // Live data: poll while visible, catch up the moment the window is visible again.\n const refreshMs = options.refreshMs;\n useEffect(() => {\n if (!enabled || !refreshMs || typeof document === \"undefined\") return;\n let timer: ReturnType<typeof setInterval> | null = null;\n const start = () => {\n if (timer === null) timer = setInterval(() => void run(), refreshMs);\n };\n const stop = () => {\n if (timer !== null) clearInterval(timer);\n timer = null;\n };\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") {\n void run();\n start();\n } else stop();\n };\n if (document.visibilityState === \"visible\") start();\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => {\n stop();\n document.removeEventListener(\"visibilitychange\", onVisibility);\n };\n }, [enabled, refreshMs, run]);\n\n // A retryable failure (device offline, connection lost) re-runs once the client is open again.\n const status = useSyncExternalStore(c.subscribe, () => c.getState().status, () => c.getState().status);\n useEffect(() => {\n if (enabled && status === \"open\" && error?.retryable) void run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status]);\n\n return { data, error, loading, refetch: run };\n}\n\nexport interface DesktopActionState {\n /** Run one or more ops as one action; resolves true when it finished without an error. */\n run: (work: (client: DesktopClient) => Promise<unknown>) => Promise<boolean>;\n /** An action is in flight (disable its controls). */\n busy: boolean;\n /** The last action's error, until the next action or `clearError()`. */\n error: DesktopProtocolError | undefined;\n clearError: () => void;\n}\n\n/**\n * Actions (create, save, remove, open): one busy flag and one error for a screen's writes, with the\n * same error type as reads. `onDone` runs after every successful action (refetch what it changed).\n */\nexport function useDesktopAction(options: { client?: DesktopClient; onDone?: () => unknown } = {}): DesktopActionState {\n const c = useDesktopClient(options.client);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const onDoneRef = useRef(options.onDone);\n onDoneRef.current = options.onDone;\n const run = useCallback(\n async (work: (client: DesktopClient) => Promise<unknown>): Promise<boolean> => {\n setBusy(true);\n setError(undefined);\n try {\n await work(c);\n await onDoneRef.current?.();\n return true;\n } catch (caught) {\n setError(caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError(\"INTERNAL\", caught instanceof Error ? caught.message : String(caught), { cause: caught }));\n return false;\n } finally {\n setBusy(false);\n }\n },\n [c],\n );\n const clearError = useCallback(() => setError(undefined), []);\n return { run, busy, error, clearError };\n}\n\n/** Subscribe to one protocol event (fs.changed, exec.exited, relay.device_status, …) for the component's life. */\nexport function useDesktopEvent<E extends EventName>(name: E, handler: EventHandler<E>, options: { client?: DesktopClient } = {}): void {\n const c = useDesktopClient(options.client);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n useEffect(() => c.on(name, ((payload, resourceId) => handlerRef.current(payload, resourceId)) as EventHandler<E>), [c, name]);\n}\n\n/**\n * Reconnect the moment the person is back: page visible again, a bfcache restore, the network\n * returning, window focus. Each calls `client.wake()` — dial now if reconnecting, probe if open.\n */\nexport function useDesktopWake(client?: DesktopClient, options: { probeMs?: number } = {}): void {\n const c = useDesktopClient(client);\n const probeMs = options.probeMs;\n useEffect(() => bindDesktopWake(c, probeMs === undefined ? {} : { probeMs }), [c, probeMs]);\n}\n","/**\n * Wire a client's wake() to the browser events that mean \"the person is back\": the page becomes\n * visible again (iOS Safari suspends a background tab's sockets), a back/forward-cache restore\n * (`pageshow` with persisted), and the network returning (`online`). Framework-free; the React\n * binding is `useDesktopWake` in `./react`.\n */\nimport type { DesktopClient, WakeOptions } from \"./types\";\n\n/** The subset of window/document the binding listens on — injectable for tests and non-DOM hosts. */\nexport interface WakeTargets {\n window: Pick<Window, \"addEventListener\" | \"removeEventListener\">;\n document: Pick<Document, \"addEventListener\" | \"removeEventListener\" | \"visibilityState\">;\n}\n\nexport function bindDesktopWake(client: Pick<DesktopClient, \"wake\">, options: WakeOptions & { targets?: WakeTargets } = {}): () => void {\n const targets: WakeTargets | null =\n options.targets ?? (typeof window !== \"undefined\" && typeof document !== \"undefined\" ? { window, document } : null);\n if (!targets) return () => undefined;\n const wakeOptions: WakeOptions = options.probeMs === undefined ? {} : { probeMs: options.probeMs };\n const onVisibility = () => {\n if (targets.document.visibilityState === \"visible\") client.wake(wakeOptions);\n };\n const onWake = () => client.wake(wakeOptions);\n targets.document.addEventListener(\"visibilitychange\", onVisibility);\n targets.window.addEventListener(\"pageshow\", onWake);\n targets.window.addEventListener(\"online\", onWake);\n targets.window.addEventListener(\"focus\", onWake);\n return () => {\n targets.document.removeEventListener(\"visibilitychange\", onVisibility);\n targets.window.removeEventListener(\"pageshow\", onWake);\n targets.window.removeEventListener(\"online\", onWake);\n targets.window.removeEventListener(\"focus\", onWake);\n };\n}\n","/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,mBAAyH;;;ACSlH,SAAS,gBAAgB,QAAqC,UAAmD,CAAC,GAAe;AACtI,QAAM,UACJ,QAAQ,YAAY,OAAO,WAAW,eAAe,OAAO,aAAa,cAAc,EAAE,QAAQ,SAAS,IAAI;AAChH,MAAI,CAAC,QAAS,QAAO,MAAM;AAC3B,QAAM,cAA2B,QAAQ,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;AACjG,QAAM,eAAe,MAAM;AACzB,QAAI,QAAQ,SAAS,oBAAoB,UAAW,QAAO,KAAK,WAAW;AAAA,EAC7E;AACA,QAAM,SAAS,MAAM,OAAO,KAAK,WAAW;AAC5C,UAAQ,SAAS,iBAAiB,oBAAoB,YAAY;AAClE,UAAQ,OAAO,iBAAiB,YAAY,MAAM;AAClD,UAAQ,OAAO,iBAAiB,UAAU,MAAM;AAChD,UAAQ,OAAO,iBAAiB,SAAS,MAAM;AAC/C,SAAO,MAAM;AACX,YAAQ,SAAS,oBAAoB,oBAAoB,YAAY;AACrE,YAAQ,OAAO,oBAAoB,YAAY,MAAM;AACrD,YAAQ,OAAO,oBAAoB,UAAU,MAAM;AACnD,YAAQ,OAAO,oBAAoB,SAAS,MAAM;AAAA,EACpD;AACF;;;AC1BO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAAqB,SAAiB,UAAsE,CAAC,GAAG;AAC1H,UAAM,SAAS,QAAQ,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;AACjF,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,SAAyF;AACvF,WAAO,KAAK,SAAS,SACjB,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,SAAS,WAAW,KAAK,UAAU,IACpE,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,SAAS,WAAW,KAAK,WAAW,MAAM,KAAK,KAAK;AAAA,EAC3F;AACF;;;AFZA,IAAM,eAAe,uBAAO,IAAI,0CAA0C;AAC1E,SAAS,gBAA+C;AACtD,QAAM,OAAO;AACb,SAAQ,KAAK,YAAY,UAAM,4BAAoC,IAAI;AACzE;AAEO,SAAS,sBAAsB,OAAmE;AACvG,aAAO,4BAAc,cAAc,EAAE,UAAU,EAAE,OAAO,MAAM,OAAO,GAAG,MAAM,QAAQ;AACxF;AAGO,SAAS,iBAAiB,QAAuC;AACtE,QAAM,kBAAc,yBAAW,cAAc,CAAC;AAC9C,QAAM,WAAW,UAAU;AAC3B,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,6FAAwF;AACvH,SAAO;AACT;AAGO,SAAS,qBAAqB,QAA4C;AAC/E,QAAM,IAAI,iBAAiB,MAAM;AACjC,QAAM,YAAQ,mCAAqB,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ;AACtE,8BAAU,MAAM;AACd,QAAI,EAAE,SAAS,EAAE,WAAW,OAAQ,GAAE,QAAQ;AAAA,EAChD,GAAG,CAAC,CAAC,CAAC;AACN,SAAO;AACT;AAuBO,SAAS,kBAAoC,IAAO,QAAqB,UAAoC,CAAC,GAA2B;AAC9I,QAAM,IAAI,iBAAiB,QAAQ,MAAM;AACzC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,MAAM,KAAK,UAAU,MAAM;AACjC,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAkC,MAAS;AACnE,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAA2C,MAAS;AAC9E,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAkB,OAAO;AACvD,QAAM,gBAAY,qBAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,aAAS,qBAAO,CAAC;AACvB,QAAM,eAAW,qBAA+B,IAAI;AACpD,QAAM,YAAY,QAAQ;AAE1B,QAAM,UAAM,0BAAY,YAA8C;AACpE,UAAM,QAAQ,EAAE,OAAO;AACvB,aAAS,SAAS,MAAM;AACxB,UAAM,QAAQ,IAAI,gBAAgB;AAClC,aAAS,UAAU;AACnB,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS,MAAM,EAAE,QAAQ,IAAI,UAAU,SAAS,cAAc,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,EAAE,QAAQ,MAAM,QAAQ,UAAU,CAAC;AAC9I,UAAI,UAAU,OAAO,SAAS;AAC5B,gBAAQ,MAAM;AACd,iBAAS,MAAS;AAAA,MACpB;AACA,aAAO;AAAA,IACT,SAAS,QAAQ;AACf,YAAM,MAAM,kBAAkB,uBAAuB,SAAS,IAAI,qBAAqB,YAAY,OAAO,MAAM,GAAG,EAAE,OAAO,OAAO,CAAC;AACpI,UAAI,UAAU,OAAO,WAAW,IAAI,SAAS,YAAa,UAAS,GAAG;AACtE,aAAO;AAAA,IACT,UAAE;AACA,UAAI,UAAU,OAAO,QAAS,YAAW,KAAK;AAAA,IAChD;AAAA,EAGF,GAAG,CAAC,GAAG,IAAI,KAAK,SAAS,CAAC;AAE1B,8BAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,SAAK,IAAI;AACT,WAAO,MAAM;AACX,eAAS,SAAS,MAAM;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,SAAS,GAAG,CAAC;AAGjB,QAAM,YAAY,QAAQ;AAC1B,8BAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,aAAa,OAAO,aAAa,YAAa;AAC/D,QAAI,QAA+C;AACnD,UAAM,QAAQ,MAAM;AAClB,UAAI,UAAU,KAAM,SAAQ,YAAY,MAAM,KAAK,IAAI,GAAG,SAAS;AAAA,IACrE;AACA,UAAM,OAAO,MAAM;AACjB,UAAI,UAAU,KAAM,eAAc,KAAK;AACvC,cAAQ;AAAA,IACV;AACA,UAAM,eAAe,MAAM;AACzB,UAAI,SAAS,oBAAoB,WAAW;AAC1C,aAAK,IAAI;AACT,cAAM;AAAA,MACR,MAAO,MAAK;AAAA,IACd;AACA,QAAI,SAAS,oBAAoB,UAAW,OAAM;AAClD,aAAS,iBAAiB,oBAAoB,YAAY;AAC1D,WAAO,MAAM;AACX,WAAK;AACL,eAAS,oBAAoB,oBAAoB,YAAY;AAAA,IAC/D;AAAA,EACF,GAAG,CAAC,SAAS,WAAW,GAAG,CAAC;AAG5B,QAAM,aAAS,mCAAqB,EAAE,WAAW,MAAM,EAAE,SAAS,EAAE,QAAQ,MAAM,EAAE,SAAS,EAAE,MAAM;AACrG,8BAAU,MAAM;AACd,QAAI,WAAW,WAAW,UAAU,OAAO,UAAW,MAAK,IAAI;AAAA,EAEjE,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO,EAAE,MAAM,OAAO,SAAS,SAAS,IAAI;AAC9C;AAgBO,SAAS,iBAAiB,UAA8D,CAAC,GAAuB;AACrH,QAAM,IAAI,iBAAiB,QAAQ,MAAM;AACzC,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAA2C,MAAS;AAC9E,QAAM,gBAAY,qBAAO,QAAQ,MAAM;AACvC,YAAU,UAAU,QAAQ;AAC5B,QAAM,UAAM;AAAA,IACV,OAAO,SAAwE;AAC7E,cAAQ,IAAI;AACZ,eAAS,MAAS;AAClB,UAAI;AACF,cAAM,KAAK,CAAC;AACZ,cAAM,UAAU,UAAU;AAC1B,eAAO;AAAA,MACT,SAAS,QAAQ;AACf,iBAAS,kBAAkB,uBAAuB,SAAS,IAAI,qBAAqB,YAAY,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,GAAG,EAAE,OAAO,OAAO,CAAC,CAAC;AAC7K,eAAO;AAAA,MACT,UAAE;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AACA,QAAM,iBAAa,0BAAY,MAAM,SAAS,MAAS,GAAG,CAAC,CAAC;AAC5D,SAAO,EAAE,KAAK,MAAM,OAAO,WAAW;AACxC;AAGO,SAAS,gBAAqC,MAAS,SAA0B,UAAsC,CAAC,GAAS;AACtI,QAAM,IAAI,iBAAiB,QAAQ,MAAM;AACzC,QAAM,iBAAa,qBAAO,OAAO;AACjC,aAAW,UAAU;AACrB,8BAAU,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,eAAe,WAAW,QAAQ,SAAS,UAAU,EAAqB,GAAG,CAAC,GAAG,IAAI,CAAC;AAC9H;AAMO,SAAS,eAAe,QAAwB,UAAgC,CAAC,GAAS;AAC/F,QAAM,IAAI,iBAAiB,MAAM;AACjC,QAAM,UAAU,QAAQ;AACxB,8BAAU,MAAM,gBAAgB,GAAG,YAAY,SAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;AAC5F;","names":[]}
1
+ {"version":3,"sources":["../src/react.ts","../src/client/wake.ts","../src/error.ts"],"sourcesContent":["/**\n * `@ai-matrx/desktop-protocol/react` — hooks over the client: provider, connection state, typed\n * one-shot requests, events. The client owns every hard part (reconnect, reattach, credits); these\n * hooks only bind it to React's lifecycle.\n */\nimport { createContext, createElement, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { Context, ReactNode } from \"react\";\nimport type { DesktopClient, DesktopClientState, EventHandler, EventName, RequestOptions } from \"./client/types\";\nimport { bindDesktopWake } from \"./client/wake\";\nimport { DesktopProtocolError } from \"./error\";\nimport type { OpName, OpParams, OpResult } from \"./schema/registry\";\n\n// The context lives on globalThis: an ESM and a CJS copy of this entry in one app (Next.js\n// does this) would otherwise create two contexts and a provider the hooks cannot see.\nconst CONTEXT_SLOT = Symbol.for(\"ai-matrx.desktop-protocol.client-context\");\nfunction clientContext(): Context<DesktopClient | null> {\n const slot = globalThis as { [CONTEXT_SLOT]?: Context<DesktopClient | null> };\n return (slot[CONTEXT_SLOT] ??= createContext<DesktopClient | null>(null));\n}\n\nexport function DesktopClientProvider(props: { client: DesktopClient; children?: ReactNode }): ReactNode {\n return createElement(clientContext().Provider, { value: props.client }, props.children);\n}\n\n/** The client from the nearest provider (or the one passed in). Throws when there is neither. */\nexport function useDesktopClient(client?: DesktopClient): DesktopClient {\n const fromContext = useContext(clientContext());\n const resolved = client ?? fromContext;\n if (!resolved) throw new Error(\"useDesktopClient: wrap the tree in <DesktopClientProvider client={…}> or pass a client\");\n return resolved;\n}\n\n/** Live connection state (status, welcome, attempts, retryInMs, lastError). Connects on mount when idle. */\nexport function useDesktopConnection(client?: DesktopClient): DesktopClientState {\n const c = useDesktopClient(client);\n const state = useSyncExternalStore(c.subscribe, c.getState, c.getState);\n useEffect(() => {\n if (c.getState().status === \"idle\") c.connect();\n }, [c]);\n return state;\n}\n\nexport interface DesktopRequestState<N extends OpName> {\n data: OpResult<N> | undefined;\n error: DesktopProtocolError | undefined;\n loading: boolean;\n /** Re-run now; resolves with the fresh result. */\n refetch: () => Promise<OpResult<N> | undefined>;\n}\n\nexport interface UseDesktopRequestOptions extends Omit<RequestOptions, \"signal\"> {\n /** Default true. False = do not run until refetch() or enabled flips true. */\n enabled?: boolean;\n /** Re-run every `refreshMs` while the page is visible, and at once when it becomes visible\n * again. A hidden window never polls. Default: no refresh. */\n refreshMs?: number;\n client?: DesktopClient;\n}\n\n/**\n * Run a unary op and keep its latest result. Re-runs when `op` or the params' JSON changes, and\n * after a reconnect if the last attempt failed with a retryable error. Aborts on unmount.\n */\nexport function useDesktopRequest<N extends OpName>(op: N, params: OpParams<N>, options: UseDesktopRequestOptions = {}): DesktopRequestState<N> {\n const c = useDesktopClient(options.client);\n const enabled = options.enabled ?? true;\n const key = JSON.stringify(params);\n const [data, setData] = useState<OpResult<N> | undefined>(undefined);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(enabled);\n const paramsRef = useRef(params);\n paramsRef.current = params;\n const runRef = useRef(0);\n const abortRef = useRef<AbortController | null>(null);\n const timeoutMs = options.timeoutMs;\n\n const run = useCallback(async (): Promise<OpResult<N> | undefined> => {\n const runId = ++runRef.current;\n abortRef.current?.abort();\n const abort = new AbortController();\n abortRef.current = abort;\n setLoading(true);\n try {\n const result = await c.request(op, paramsRef.current, timeoutMs === undefined ? { signal: abort.signal } : { signal: abort.signal, timeoutMs });\n if (runId === runRef.current) {\n setData(result);\n setError(undefined);\n }\n return result;\n } catch (caught) {\n const err = DesktopProtocolError.from(caught);\n if (runId === runRef.current && err.code !== \"CANCELLED\") setError(err);\n return undefined;\n } finally {\n if (runId === runRef.current) setLoading(false);\n }\n // `key` stands for the params' content: a new object with equal JSON is not a new request.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [c, op, key, timeoutMs]);\n\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n void run();\n return () => {\n abortRef.current?.abort();\n };\n }, [enabled, run]);\n\n // Live data: poll while visible, catch up the moment the window is visible again.\n const refreshMs = options.refreshMs;\n useEffect(() => {\n if (!enabled || !refreshMs || typeof document === \"undefined\") return;\n let timer: ReturnType<typeof setInterval> | null = null;\n const start = () => {\n if (timer === null) timer = setInterval(() => void run(), refreshMs);\n };\n const stop = () => {\n if (timer !== null) clearInterval(timer);\n timer = null;\n };\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") {\n void run();\n start();\n } else stop();\n };\n if (document.visibilityState === \"visible\") start();\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => {\n stop();\n document.removeEventListener(\"visibilitychange\", onVisibility);\n };\n }, [enabled, refreshMs, run]);\n\n // A retryable failure (device offline, connection lost) re-runs once the client is open again.\n const status = useSyncExternalStore(c.subscribe, () => c.getState().status, () => c.getState().status);\n useEffect(() => {\n if (enabled && status === \"open\" && error?.retryable) void run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status]);\n\n return { data, error, loading, refetch: run };\n}\n\nexport interface DesktopActionState {\n /** Run one or more ops as one action; resolves true when it finished without an error. */\n run: (work: (client: DesktopClient) => Promise<unknown>) => Promise<boolean>;\n /** An action is in flight (disable its controls). */\n busy: boolean;\n /** The last action's error, until the next action or `clearError()`. */\n error: DesktopProtocolError | undefined;\n clearError: () => void;\n}\n\n/**\n * Actions (create, save, remove, open): one busy flag and one error for a screen's writes, with the\n * same error type as reads. `onDone` runs after every successful action (refetch what it changed).\n */\nexport function useDesktopAction(options: { client?: DesktopClient; onDone?: () => unknown } = {}): DesktopActionState {\n const c = useDesktopClient(options.client);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const onDoneRef = useRef(options.onDone);\n onDoneRef.current = options.onDone;\n const run = useCallback(\n async (work: (client: DesktopClient) => Promise<unknown>): Promise<boolean> => {\n setBusy(true);\n setError(undefined);\n try {\n await work(c);\n await onDoneRef.current?.();\n return true;\n } catch (caught) {\n const err = DesktopProtocolError.from(caught);\n // A cancelled action (the person pressed Stop) is not a failure to show.\n if (err.code !== \"CANCELLED\") setError(err);\n return false;\n } finally {\n setBusy(false);\n }\n },\n [c],\n );\n const clearError = useCallback(() => setError(undefined), []);\n return { run, busy, error, clearError };\n}\n\n/** Subscribe to one protocol event (fs.changed, exec.exited, relay.device_status, …) for the component's life. */\nexport function useDesktopEvent<E extends EventName>(name: E, handler: EventHandler<E>, options: { client?: DesktopClient } = {}): void {\n const c = useDesktopClient(options.client);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n useEffect(() => c.on(name, ((payload, resourceId) => handlerRef.current(payload, resourceId)) as EventHandler<E>), [c, name]);\n}\n\n/**\n * Reconnect the moment the person is back: page visible again, a bfcache restore, the network\n * returning, window focus. Each calls `client.wake()` — dial now if reconnecting, probe if open.\n */\nexport function useDesktopWake(client?: DesktopClient, options: { probeMs?: number } = {}): void {\n const c = useDesktopClient(client);\n const probeMs = options.probeMs;\n useEffect(() => bindDesktopWake(c, probeMs === undefined ? {} : { probeMs }), [c, probeMs]);\n}\n","/**\n * Wire a client's wake() to the browser events that mean \"the person is back\": the page becomes\n * visible again (iOS Safari suspends a background tab's sockets), a back/forward-cache restore\n * (`pageshow` with persisted), and the network returning (`online`). Framework-free; the React\n * binding is `useDesktopWake` in `./react`.\n */\nimport type { DesktopClient, WakeOptions } from \"./types\";\n\n/** The subset of window/document the binding listens on — injectable for tests and non-DOM hosts. */\nexport interface WakeTargets {\n window: Pick<Window, \"addEventListener\" | \"removeEventListener\">;\n document: Pick<Document, \"addEventListener\" | \"removeEventListener\" | \"visibilityState\">;\n}\n\nexport function bindDesktopWake(client: Pick<DesktopClient, \"wake\">, options: WakeOptions & { targets?: WakeTargets } = {}): () => void {\n const targets: WakeTargets | null =\n options.targets ?? (typeof window !== \"undefined\" && typeof document !== \"undefined\" ? { window, document } : null);\n if (!targets) return () => undefined;\n const wakeOptions: WakeOptions = options.probeMs === undefined ? {} : { probeMs: options.probeMs };\n const onVisibility = () => {\n if (targets.document.visibilityState === \"visible\") client.wake(wakeOptions);\n };\n const onWake = () => client.wake(wakeOptions);\n targets.document.addEventListener(\"visibilitychange\", onVisibility);\n targets.window.addEventListener(\"pageshow\", onWake);\n targets.window.addEventListener(\"online\", onWake);\n targets.window.addEventListener(\"focus\", onWake);\n return () => {\n targets.document.removeEventListener(\"visibilitychange\", onVisibility);\n targets.window.removeEventListener(\"pageshow\", onWake);\n targets.window.removeEventListener(\"online\", onWake);\n targets.window.removeEventListener(\"focus\", onWake);\n };\n}\n","/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /**\n * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry\n * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an\n * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.\n */\n static from(caught: unknown): DesktopProtocolError {\n if (caught instanceof DesktopProtocolError) return caught;\n const e = caught as { name?: unknown; code?: unknown; message?: unknown; retryable?: unknown; data?: unknown } | null;\n if (e && typeof e === \"object\" && e.name === \"DesktopProtocolError\" && typeof e.code === \"string\") {\n return new DesktopProtocolError(e.code as ErrorCodeName, typeof e.message === \"string\" ? e.message : String(e.code), {\n retryable: e.retryable === true,\n ...(e.data && typeof e.data === \"object\" ? { data: e.data as ErrorData } : {}),\n cause: caught,\n });\n }\n return new DesktopProtocolError(\"INTERNAL\", caught instanceof Error ? caught.message : String(caught), { cause: caught });\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n"],"mappings":"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","names":[]}
package/dist/react.d.cts CHANGED
@@ -55,6 +55,12 @@ declare class DesktopProtocolError extends Error {
55
55
  data?: ErrorData;
56
56
  cause?: unknown;
57
57
  });
58
+ /**
59
+ * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
60
+ * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
61
+ * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
62
+ */
63
+ static from(caught: unknown): DesktopProtocolError;
58
64
  /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
59
65
  toBody(): {
60
66
  code: ErrorCodeName;
package/dist/react.d.ts CHANGED
@@ -55,6 +55,12 @@ declare class DesktopProtocolError extends Error {
55
55
  data?: ErrorData;
56
56
  cause?: unknown;
57
57
  });
58
+ /**
59
+ * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
60
+ * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
61
+ * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
62
+ */
63
+ static from(caught: unknown): DesktopProtocolError;
58
64
  /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
59
65
  toBody(): {
60
66
  code: ErrorCodeName;
package/dist/react.js CHANGED
@@ -25,7 +25,7 @@ function bindDesktopWake(client, options = {}) {
25
25
  }
26
26
 
27
27
  // src/error.ts
28
- var DesktopProtocolError = class extends Error {
28
+ var DesktopProtocolError = class _DesktopProtocolError extends Error {
29
29
  code;
30
30
  retryable;
31
31
  data;
@@ -36,6 +36,23 @@ var DesktopProtocolError = class extends Error {
36
36
  this.retryable = options.retryable ?? false;
37
37
  this.data = options.data;
38
38
  }
39
+ /**
40
+ * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry
41
+ * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an
42
+ * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.
43
+ */
44
+ static from(caught) {
45
+ if (caught instanceof _DesktopProtocolError) return caught;
46
+ const e = caught;
47
+ if (e && typeof e === "object" && e.name === "DesktopProtocolError" && typeof e.code === "string") {
48
+ return new _DesktopProtocolError(e.code, typeof e.message === "string" ? e.message : String(e.code), {
49
+ retryable: e.retryable === true,
50
+ ...e.data && typeof e.data === "object" ? { data: e.data } : {},
51
+ cause: caught
52
+ });
53
+ }
54
+ return new _DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught });
55
+ }
39
56
  /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */
40
57
  toBody() {
41
58
  return this.data === void 0 ? { code: this.code, message: this.message, retryable: this.retryable } : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };
@@ -91,7 +108,7 @@ function useDesktopRequest(op, params, options = {}) {
91
108
  }
92
109
  return result;
93
110
  } catch (caught) {
94
- const err = caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError("INTERNAL", String(caught), { cause: caught });
111
+ const err = DesktopProtocolError.from(caught);
95
112
  if (runId === runRef.current && err.code !== "CANCELLED") setError(err);
96
113
  return void 0;
97
114
  } finally {
@@ -153,7 +170,8 @@ function useDesktopAction(options = {}) {
153
170
  await onDoneRef.current?.();
154
171
  return true;
155
172
  } catch (caught) {
156
- setError(caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError("INTERNAL", caught instanceof Error ? caught.message : String(caught), { cause: caught }));
173
+ const err = DesktopProtocolError.from(caught);
174
+ if (err.code !== "CANCELLED") setError(err);
157
175
  return false;
158
176
  } finally {
159
177
  setBusy(false);
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/client/wake.ts","../src/error.ts"],"sourcesContent":["/**\n * `@ai-matrx/desktop-protocol/react` — hooks over the client: provider, connection state, typed\n * one-shot requests, events. The client owns every hard part (reconnect, reattach, credits); these\n * hooks only bind it to React's lifecycle.\n */\nimport { createContext, createElement, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { Context, ReactNode } from \"react\";\nimport type { DesktopClient, DesktopClientState, EventHandler, EventName, RequestOptions } from \"./client/types\";\nimport { bindDesktopWake } from \"./client/wake\";\nimport { DesktopProtocolError } from \"./error\";\nimport type { OpName, OpParams, OpResult } from \"./schema/registry\";\n\n// The context lives on globalThis: an ESM and a CJS copy of this entry in one app (Next.js\n// does this) would otherwise create two contexts and a provider the hooks cannot see.\nconst CONTEXT_SLOT = Symbol.for(\"ai-matrx.desktop-protocol.client-context\");\nfunction clientContext(): Context<DesktopClient | null> {\n const slot = globalThis as { [CONTEXT_SLOT]?: Context<DesktopClient | null> };\n return (slot[CONTEXT_SLOT] ??= createContext<DesktopClient | null>(null));\n}\n\nexport function DesktopClientProvider(props: { client: DesktopClient; children?: ReactNode }): ReactNode {\n return createElement(clientContext().Provider, { value: props.client }, props.children);\n}\n\n/** The client from the nearest provider (or the one passed in). Throws when there is neither. */\nexport function useDesktopClient(client?: DesktopClient): DesktopClient {\n const fromContext = useContext(clientContext());\n const resolved = client ?? fromContext;\n if (!resolved) throw new Error(\"useDesktopClient: wrap the tree in <DesktopClientProvider client={…}> or pass a client\");\n return resolved;\n}\n\n/** Live connection state (status, welcome, attempts, retryInMs, lastError). Connects on mount when idle. */\nexport function useDesktopConnection(client?: DesktopClient): DesktopClientState {\n const c = useDesktopClient(client);\n const state = useSyncExternalStore(c.subscribe, c.getState, c.getState);\n useEffect(() => {\n if (c.getState().status === \"idle\") c.connect();\n }, [c]);\n return state;\n}\n\nexport interface DesktopRequestState<N extends OpName> {\n data: OpResult<N> | undefined;\n error: DesktopProtocolError | undefined;\n loading: boolean;\n /** Re-run now; resolves with the fresh result. */\n refetch: () => Promise<OpResult<N> | undefined>;\n}\n\nexport interface UseDesktopRequestOptions extends Omit<RequestOptions, \"signal\"> {\n /** Default true. False = do not run until refetch() or enabled flips true. */\n enabled?: boolean;\n /** Re-run every `refreshMs` while the page is visible, and at once when it becomes visible\n * again. A hidden window never polls. Default: no refresh. */\n refreshMs?: number;\n client?: DesktopClient;\n}\n\n/**\n * Run a unary op and keep its latest result. Re-runs when `op` or the params' JSON changes, and\n * after a reconnect if the last attempt failed with a retryable error. Aborts on unmount.\n */\nexport function useDesktopRequest<N extends OpName>(op: N, params: OpParams<N>, options: UseDesktopRequestOptions = {}): DesktopRequestState<N> {\n const c = useDesktopClient(options.client);\n const enabled = options.enabled ?? true;\n const key = JSON.stringify(params);\n const [data, setData] = useState<OpResult<N> | undefined>(undefined);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(enabled);\n const paramsRef = useRef(params);\n paramsRef.current = params;\n const runRef = useRef(0);\n const abortRef = useRef<AbortController | null>(null);\n const timeoutMs = options.timeoutMs;\n\n const run = useCallback(async (): Promise<OpResult<N> | undefined> => {\n const runId = ++runRef.current;\n abortRef.current?.abort();\n const abort = new AbortController();\n abortRef.current = abort;\n setLoading(true);\n try {\n const result = await c.request(op, paramsRef.current, timeoutMs === undefined ? { signal: abort.signal } : { signal: abort.signal, timeoutMs });\n if (runId === runRef.current) {\n setData(result);\n setError(undefined);\n }\n return result;\n } catch (caught) {\n const err = caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError(\"INTERNAL\", String(caught), { cause: caught });\n if (runId === runRef.current && err.code !== \"CANCELLED\") setError(err);\n return undefined;\n } finally {\n if (runId === runRef.current) setLoading(false);\n }\n // `key` stands for the params' content: a new object with equal JSON is not a new request.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [c, op, key, timeoutMs]);\n\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n void run();\n return () => {\n abortRef.current?.abort();\n };\n }, [enabled, run]);\n\n // Live data: poll while visible, catch up the moment the window is visible again.\n const refreshMs = options.refreshMs;\n useEffect(() => {\n if (!enabled || !refreshMs || typeof document === \"undefined\") return;\n let timer: ReturnType<typeof setInterval> | null = null;\n const start = () => {\n if (timer === null) timer = setInterval(() => void run(), refreshMs);\n };\n const stop = () => {\n if (timer !== null) clearInterval(timer);\n timer = null;\n };\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") {\n void run();\n start();\n } else stop();\n };\n if (document.visibilityState === \"visible\") start();\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => {\n stop();\n document.removeEventListener(\"visibilitychange\", onVisibility);\n };\n }, [enabled, refreshMs, run]);\n\n // A retryable failure (device offline, connection lost) re-runs once the client is open again.\n const status = useSyncExternalStore(c.subscribe, () => c.getState().status, () => c.getState().status);\n useEffect(() => {\n if (enabled && status === \"open\" && error?.retryable) void run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status]);\n\n return { data, error, loading, refetch: run };\n}\n\nexport interface DesktopActionState {\n /** Run one or more ops as one action; resolves true when it finished without an error. */\n run: (work: (client: DesktopClient) => Promise<unknown>) => Promise<boolean>;\n /** An action is in flight (disable its controls). */\n busy: boolean;\n /** The last action's error, until the next action or `clearError()`. */\n error: DesktopProtocolError | undefined;\n clearError: () => void;\n}\n\n/**\n * Actions (create, save, remove, open): one busy flag and one error for a screen's writes, with the\n * same error type as reads. `onDone` runs after every successful action (refetch what it changed).\n */\nexport function useDesktopAction(options: { client?: DesktopClient; onDone?: () => unknown } = {}): DesktopActionState {\n const c = useDesktopClient(options.client);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const onDoneRef = useRef(options.onDone);\n onDoneRef.current = options.onDone;\n const run = useCallback(\n async (work: (client: DesktopClient) => Promise<unknown>): Promise<boolean> => {\n setBusy(true);\n setError(undefined);\n try {\n await work(c);\n await onDoneRef.current?.();\n return true;\n } catch (caught) {\n setError(caught instanceof DesktopProtocolError ? caught : new DesktopProtocolError(\"INTERNAL\", caught instanceof Error ? caught.message : String(caught), { cause: caught }));\n return false;\n } finally {\n setBusy(false);\n }\n },\n [c],\n );\n const clearError = useCallback(() => setError(undefined), []);\n return { run, busy, error, clearError };\n}\n\n/** Subscribe to one protocol event (fs.changed, exec.exited, relay.device_status, …) for the component's life. */\nexport function useDesktopEvent<E extends EventName>(name: E, handler: EventHandler<E>, options: { client?: DesktopClient } = {}): void {\n const c = useDesktopClient(options.client);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n useEffect(() => c.on(name, ((payload, resourceId) => handlerRef.current(payload, resourceId)) as EventHandler<E>), [c, name]);\n}\n\n/**\n * Reconnect the moment the person is back: page visible again, a bfcache restore, the network\n * returning, window focus. Each calls `client.wake()` — dial now if reconnecting, probe if open.\n */\nexport function useDesktopWake(client?: DesktopClient, options: { probeMs?: number } = {}): void {\n const c = useDesktopClient(client);\n const probeMs = options.probeMs;\n useEffect(() => bindDesktopWake(c, probeMs === undefined ? {} : { probeMs }), [c, probeMs]);\n}\n","/**\n * Wire a client's wake() to the browser events that mean \"the person is back\": the page becomes\n * visible again (iOS Safari suspends a background tab's sockets), a back/forward-cache restore\n * (`pageshow` with persisted), and the network returning (`online`). Framework-free; the React\n * binding is `useDesktopWake` in `./react`.\n */\nimport type { DesktopClient, WakeOptions } from \"./types\";\n\n/** The subset of window/document the binding listens on — injectable for tests and non-DOM hosts. */\nexport interface WakeTargets {\n window: Pick<Window, \"addEventListener\" | \"removeEventListener\">;\n document: Pick<Document, \"addEventListener\" | \"removeEventListener\" | \"visibilityState\">;\n}\n\nexport function bindDesktopWake(client: Pick<DesktopClient, \"wake\">, options: WakeOptions & { targets?: WakeTargets } = {}): () => void {\n const targets: WakeTargets | null =\n options.targets ?? (typeof window !== \"undefined\" && typeof document !== \"undefined\" ? { window, document } : null);\n if (!targets) return () => undefined;\n const wakeOptions: WakeOptions = options.probeMs === undefined ? {} : { probeMs: options.probeMs };\n const onVisibility = () => {\n if (targets.document.visibilityState === \"visible\") client.wake(wakeOptions);\n };\n const onWake = () => client.wake(wakeOptions);\n targets.document.addEventListener(\"visibilitychange\", onVisibility);\n targets.window.addEventListener(\"pageshow\", onWake);\n targets.window.addEventListener(\"online\", onWake);\n targets.window.addEventListener(\"focus\", onWake);\n return () => {\n targets.document.removeEventListener(\"visibilitychange\", onVisibility);\n targets.window.removeEventListener(\"pageshow\", onWake);\n targets.window.removeEventListener(\"online\", onWake);\n targets.window.removeEventListener(\"focus\", onWake);\n };\n}\n","/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/react.ts","../src/client/wake.ts","../src/error.ts"],"sourcesContent":["/**\n * `@ai-matrx/desktop-protocol/react` — hooks over the client: provider, connection state, typed\n * one-shot requests, events. The client owns every hard part (reconnect, reattach, credits); these\n * hooks only bind it to React's lifecycle.\n */\nimport { createContext, createElement, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { Context, ReactNode } from \"react\";\nimport type { DesktopClient, DesktopClientState, EventHandler, EventName, RequestOptions } from \"./client/types\";\nimport { bindDesktopWake } from \"./client/wake\";\nimport { DesktopProtocolError } from \"./error\";\nimport type { OpName, OpParams, OpResult } from \"./schema/registry\";\n\n// The context lives on globalThis: an ESM and a CJS copy of this entry in one app (Next.js\n// does this) would otherwise create two contexts and a provider the hooks cannot see.\nconst CONTEXT_SLOT = Symbol.for(\"ai-matrx.desktop-protocol.client-context\");\nfunction clientContext(): Context<DesktopClient | null> {\n const slot = globalThis as { [CONTEXT_SLOT]?: Context<DesktopClient | null> };\n return (slot[CONTEXT_SLOT] ??= createContext<DesktopClient | null>(null));\n}\n\nexport function DesktopClientProvider(props: { client: DesktopClient; children?: ReactNode }): ReactNode {\n return createElement(clientContext().Provider, { value: props.client }, props.children);\n}\n\n/** The client from the nearest provider (or the one passed in). Throws when there is neither. */\nexport function useDesktopClient(client?: DesktopClient): DesktopClient {\n const fromContext = useContext(clientContext());\n const resolved = client ?? fromContext;\n if (!resolved) throw new Error(\"useDesktopClient: wrap the tree in <DesktopClientProvider client={…}> or pass a client\");\n return resolved;\n}\n\n/** Live connection state (status, welcome, attempts, retryInMs, lastError). Connects on mount when idle. */\nexport function useDesktopConnection(client?: DesktopClient): DesktopClientState {\n const c = useDesktopClient(client);\n const state = useSyncExternalStore(c.subscribe, c.getState, c.getState);\n useEffect(() => {\n if (c.getState().status === \"idle\") c.connect();\n }, [c]);\n return state;\n}\n\nexport interface DesktopRequestState<N extends OpName> {\n data: OpResult<N> | undefined;\n error: DesktopProtocolError | undefined;\n loading: boolean;\n /** Re-run now; resolves with the fresh result. */\n refetch: () => Promise<OpResult<N> | undefined>;\n}\n\nexport interface UseDesktopRequestOptions extends Omit<RequestOptions, \"signal\"> {\n /** Default true. False = do not run until refetch() or enabled flips true. */\n enabled?: boolean;\n /** Re-run every `refreshMs` while the page is visible, and at once when it becomes visible\n * again. A hidden window never polls. Default: no refresh. */\n refreshMs?: number;\n client?: DesktopClient;\n}\n\n/**\n * Run a unary op and keep its latest result. Re-runs when `op` or the params' JSON changes, and\n * after a reconnect if the last attempt failed with a retryable error. Aborts on unmount.\n */\nexport function useDesktopRequest<N extends OpName>(op: N, params: OpParams<N>, options: UseDesktopRequestOptions = {}): DesktopRequestState<N> {\n const c = useDesktopClient(options.client);\n const enabled = options.enabled ?? true;\n const key = JSON.stringify(params);\n const [data, setData] = useState<OpResult<N> | undefined>(undefined);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const [loading, setLoading] = useState<boolean>(enabled);\n const paramsRef = useRef(params);\n paramsRef.current = params;\n const runRef = useRef(0);\n const abortRef = useRef<AbortController | null>(null);\n const timeoutMs = options.timeoutMs;\n\n const run = useCallback(async (): Promise<OpResult<N> | undefined> => {\n const runId = ++runRef.current;\n abortRef.current?.abort();\n const abort = new AbortController();\n abortRef.current = abort;\n setLoading(true);\n try {\n const result = await c.request(op, paramsRef.current, timeoutMs === undefined ? { signal: abort.signal } : { signal: abort.signal, timeoutMs });\n if (runId === runRef.current) {\n setData(result);\n setError(undefined);\n }\n return result;\n } catch (caught) {\n const err = DesktopProtocolError.from(caught);\n if (runId === runRef.current && err.code !== \"CANCELLED\") setError(err);\n return undefined;\n } finally {\n if (runId === runRef.current) setLoading(false);\n }\n // `key` stands for the params' content: a new object with equal JSON is not a new request.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [c, op, key, timeoutMs]);\n\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n void run();\n return () => {\n abortRef.current?.abort();\n };\n }, [enabled, run]);\n\n // Live data: poll while visible, catch up the moment the window is visible again.\n const refreshMs = options.refreshMs;\n useEffect(() => {\n if (!enabled || !refreshMs || typeof document === \"undefined\") return;\n let timer: ReturnType<typeof setInterval> | null = null;\n const start = () => {\n if (timer === null) timer = setInterval(() => void run(), refreshMs);\n };\n const stop = () => {\n if (timer !== null) clearInterval(timer);\n timer = null;\n };\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") {\n void run();\n start();\n } else stop();\n };\n if (document.visibilityState === \"visible\") start();\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => {\n stop();\n document.removeEventListener(\"visibilitychange\", onVisibility);\n };\n }, [enabled, refreshMs, run]);\n\n // A retryable failure (device offline, connection lost) re-runs once the client is open again.\n const status = useSyncExternalStore(c.subscribe, () => c.getState().status, () => c.getState().status);\n useEffect(() => {\n if (enabled && status === \"open\" && error?.retryable) void run();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status]);\n\n return { data, error, loading, refetch: run };\n}\n\nexport interface DesktopActionState {\n /** Run one or more ops as one action; resolves true when it finished without an error. */\n run: (work: (client: DesktopClient) => Promise<unknown>) => Promise<boolean>;\n /** An action is in flight (disable its controls). */\n busy: boolean;\n /** The last action's error, until the next action or `clearError()`. */\n error: DesktopProtocolError | undefined;\n clearError: () => void;\n}\n\n/**\n * Actions (create, save, remove, open): one busy flag and one error for a screen's writes, with the\n * same error type as reads. `onDone` runs after every successful action (refetch what it changed).\n */\nexport function useDesktopAction(options: { client?: DesktopClient; onDone?: () => unknown } = {}): DesktopActionState {\n const c = useDesktopClient(options.client);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<DesktopProtocolError | undefined>(undefined);\n const onDoneRef = useRef(options.onDone);\n onDoneRef.current = options.onDone;\n const run = useCallback(\n async (work: (client: DesktopClient) => Promise<unknown>): Promise<boolean> => {\n setBusy(true);\n setError(undefined);\n try {\n await work(c);\n await onDoneRef.current?.();\n return true;\n } catch (caught) {\n const err = DesktopProtocolError.from(caught);\n // A cancelled action (the person pressed Stop) is not a failure to show.\n if (err.code !== \"CANCELLED\") setError(err);\n return false;\n } finally {\n setBusy(false);\n }\n },\n [c],\n );\n const clearError = useCallback(() => setError(undefined), []);\n return { run, busy, error, clearError };\n}\n\n/** Subscribe to one protocol event (fs.changed, exec.exited, relay.device_status, …) for the component's life. */\nexport function useDesktopEvent<E extends EventName>(name: E, handler: EventHandler<E>, options: { client?: DesktopClient } = {}): void {\n const c = useDesktopClient(options.client);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n useEffect(() => c.on(name, ((payload, resourceId) => handlerRef.current(payload, resourceId)) as EventHandler<E>), [c, name]);\n}\n\n/**\n * Reconnect the moment the person is back: page visible again, a bfcache restore, the network\n * returning, window focus. Each calls `client.wake()` — dial now if reconnecting, probe if open.\n */\nexport function useDesktopWake(client?: DesktopClient, options: { probeMs?: number } = {}): void {\n const c = useDesktopClient(client);\n const probeMs = options.probeMs;\n useEffect(() => bindDesktopWake(c, probeMs === undefined ? {} : { probeMs }), [c, probeMs]);\n}\n","/**\n * Wire a client's wake() to the browser events that mean \"the person is back\": the page becomes\n * visible again (iOS Safari suspends a background tab's sockets), a back/forward-cache restore\n * (`pageshow` with persisted), and the network returning (`online`). Framework-free; the React\n * binding is `useDesktopWake` in `./react`.\n */\nimport type { DesktopClient, WakeOptions } from \"./types\";\n\n/** The subset of window/document the binding listens on — injectable for tests and non-DOM hosts. */\nexport interface WakeTargets {\n window: Pick<Window, \"addEventListener\" | \"removeEventListener\">;\n document: Pick<Document, \"addEventListener\" | \"removeEventListener\" | \"visibilityState\">;\n}\n\nexport function bindDesktopWake(client: Pick<DesktopClient, \"wake\">, options: WakeOptions & { targets?: WakeTargets } = {}): () => void {\n const targets: WakeTargets | null =\n options.targets ?? (typeof window !== \"undefined\" && typeof document !== \"undefined\" ? { window, document } : null);\n if (!targets) return () => undefined;\n const wakeOptions: WakeOptions = options.probeMs === undefined ? {} : { probeMs: options.probeMs };\n const onVisibility = () => {\n if (targets.document.visibilityState === \"visible\") client.wake(wakeOptions);\n };\n const onWake = () => client.wake(wakeOptions);\n targets.document.addEventListener(\"visibilitychange\", onVisibility);\n targets.window.addEventListener(\"pageshow\", onWake);\n targets.window.addEventListener(\"online\", onWake);\n targets.window.addEventListener(\"focus\", onWake);\n return () => {\n targets.document.removeEventListener(\"visibilitychange\", onVisibility);\n targets.window.removeEventListener(\"pageshow\", onWake);\n targets.window.removeEventListener(\"online\", onWake);\n targets.window.removeEventListener(\"focus\", onWake);\n };\n}\n","/**\n * The one error type every package entry throws. It carries the wire ErrorCode (SPEC §5), so a\n * caller branches on `code`/`retryable`/`data.reason` and never parses a message. Zod-free on\n * purpose: `./frame` (the relay hot path) throws it without pulling the schema graph.\n */\nimport type { ErrorCodeName, ErrorData } from \"./types\";\n\nexport class DesktopProtocolError extends Error {\n readonly code: ErrorCodeName;\n readonly retryable: boolean;\n readonly data: ErrorData | undefined;\n\n constructor(code: ErrorCodeName, message: string, options: { retryable?: boolean; data?: ErrorData; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DesktopProtocolError\";\n this.code = code;\n this.retryable = options.retryable ?? false;\n this.data = options.data;\n }\n\n /**\n * Any thrown value as a DesktopProtocolError, keeping its code, message and data. Each built entry\n * (`.`, `./client`, `./react`) carries its own copy of this class, so `instanceof` alone misses an\n * error thrown by a sibling entry and reports a real NOT_FOUND as INTERNAL; this checks the shape.\n */\n static from(caught: unknown): DesktopProtocolError {\n if (caught instanceof DesktopProtocolError) return caught;\n const e = caught as { name?: unknown; code?: unknown; message?: unknown; retryable?: unknown; data?: unknown } | null;\n if (e && typeof e === \"object\" && e.name === \"DesktopProtocolError\" && typeof e.code === \"string\") {\n return new DesktopProtocolError(e.code as ErrorCodeName, typeof e.message === \"string\" ? e.message : String(e.code), {\n retryable: e.retryable === true,\n ...(e.data && typeof e.data === \"object\" ? { data: e.data as ErrorData } : {}),\n cause: caught,\n });\n }\n return new DesktopProtocolError(\"INTERNAL\", caught instanceof Error ? caught.message : String(caught), { cause: caught });\n }\n\n /** The wire body (ProtocolErrorBody) — what a core or relay sends for this error. */\n toBody(): { code: ErrorCodeName; message: string; retryable: boolean; data?: ErrorData } {\n return this.data === undefined\n ? { code: this.code, message: this.message, retryable: this.retryable }\n : { code: this.code, message: this.message, retryable: this.retryable, data: this.data };\n }\n}\n\nexport function isDesktopProtocolError(value: unknown): value is DesktopProtocolError {\n return value instanceof DesktopProtocolError || (value instanceof Error && value.name === \"DesktopProtocolError\" && \"code\" in value);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/desktop-protocol",
3
- "version": "0.17.1",
3
+ "version": "0.17.2",
4
4
  "description": "The one wire contract between Matrx 2 (the desktop app) and every client that drives it — phone, web app, extension, server: zod schemas for the envelope and every cap.op, the 16-byte binary frame codec, a reconnecting request/stream/credit/reattach client, React hooks, and a generated JSON Schema + Pydantic twin that CI keeps byte-identical.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",