mindwire 0.1.34 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/binary-cache-CG52TXCZ.js +3 -0
  2. package/dist/{binary-cache-VM7VHCEU.js.map → binary-cache-CG52TXCZ.js.map} +1 -1
  3. package/dist/{chunk-3QFDSYKC.js → chunk-CKTVMUS4.js} +547 -177
  4. package/dist/chunk-CKTVMUS4.js.map +1 -0
  5. package/dist/{chunk-3FR7BFAW.js → chunk-PFYKJ6LX.js} +56 -4
  6. package/dist/chunk-PFYKJ6LX.js.map +1 -0
  7. package/dist/chunk-QODJKRFK.js +20 -0
  8. package/dist/chunk-QODJKRFK.js.map +1 -0
  9. package/dist/cli.js +763 -32
  10. package/dist/cli.js.map +1 -1
  11. package/dist/client.d.ts +6 -8
  12. package/dist/computer/connection-change.d.ts +13 -0
  13. package/dist/computer/connection-flow.d.ts +3 -3
  14. package/dist/computer/directory-http.d.ts +8 -0
  15. package/dist/computer/discovery.d.ts +13 -0
  16. package/dist/computer/lifecycle.d.ts +2 -1
  17. package/dist/computer/managed-relay.d.ts +27 -0
  18. package/dist/computer/oblien-auth.d.ts +25 -0
  19. package/dist/computer/provider-errors.d.ts +9 -0
  20. package/dist/computer/provider-info.d.ts +7 -0
  21. package/dist/computer/provider-setup.d.ts +20 -0
  22. package/dist/computer/relay-health.d.ts +2 -2
  23. package/dist/computer/relay.d.ts +11 -3
  24. package/dist/computer/setup-prompt.d.ts +9 -0
  25. package/dist/computer.d.ts +27 -1
  26. package/dist/connection-change-5F4WNCYV.js +41 -0
  27. package/dist/connection-change-5F4WNCYV.js.map +1 -0
  28. package/dist/{controller-AGT6MCJG.js → controller-3SZI7TBW.js} +199 -279
  29. package/dist/controller-3SZI7TBW.js.map +1 -0
  30. package/dist/{controller-handoff-3LAYBJUL.js → controller-handoff-KV7WRDAI.js} +4 -4
  31. package/dist/{controller-handoff-3LAYBJUL.js.map → controller-handoff-KV7WRDAI.js.map} +1 -1
  32. package/dist/conversations.d.ts +57 -0
  33. package/dist/index.cjs +37 -8
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +37 -9
  37. package/dist/index.js.map +1 -1
  38. package/dist/types.d.ts +16 -0
  39. package/dist/workspace.d.ts +2 -0
  40. package/package.json +5 -5
  41. package/dist/binary-cache-VM7VHCEU.js +0 -4
  42. package/dist/chunk-3FR7BFAW.js.map +0 -1
  43. package/dist/chunk-3QFDSYKC.js.map +0 -1
  44. package/dist/controller-AGT6MCJG.js.map +0 -1
package/dist/client.d.ts CHANGED
@@ -7,7 +7,7 @@ import { ExecutionApi } from "./execution.js";
7
7
  import { ComputerApi } from "./computer.js";
8
8
  import { type Target } from "./target/index.js";
9
9
  import type { EnsureEvent } from "./target/host.js";
10
- import type { AgentInfo, AuthMethod, AuthState, AuthStatus, Catalog, ChatSummary, CustomProvider, DeleteResult, DoctorReport, Health, MCPServer, MemoryDoc, MemoryScope, Message, MessagePage, ModelInfo, Notification, NotifyChannel, NotifyChannelInput, NotifyChannelTestResult, NotifyConfigInput, NotifyConfigStatus, NotifyRule, NotifyRuleInput, ProcessFrame, PromptTemplate, ResolveOptions, SetupStatus, HarnessSoftware, Stats, Subagent, TurnOptions } from "./types.js";
10
+ import type { AgentInfo, AuthMethod, AuthState, AuthStatus, Catalog, ChatSummary, ChatForkResult, ForkChatOptions, CustomProvider, DeleteResult, DoctorReport, Health, MCPServer, MemoryDoc, MemoryScope, Message, MessagePage, ModelInfo, Notification, NotifyChannel, NotifyChannelInput, NotifyChannelTestResult, NotifyConfigInput, NotifyConfigStatus, NotifyRule, NotifyRuleInput, ProcessFrame, PromptTemplate, ResolveOptions, SetupStatus, HarnessSoftware, Stats, Subagent, TurnOptions } from "./types.js";
11
11
  export interface MindwireOptions {
12
12
  /**
13
13
  * Default agent type for agent-scoped calls (e.g. `"claude-code"`). When omitted, the daemon
@@ -172,14 +172,12 @@ export declare class Mindwire {
172
172
  deleteChat(chatId: string): Promise<DeleteResult>;
173
173
  /**
174
174
  * `POST /chats/{id}/fork` — clone a chat into a new id (generated when `newChatId` is omitted).
175
- * The fork shares the source's native session until its first turn, which branches it (natively
176
- * on Claude via `--fork-session`; a fresh session on agents without native fork). Rejects with a
177
- * 409 if the source has a live turn, 404 if the source is unknown, 400 if the target id is in
178
- * use. Returns the new chat's summary.
175
+ * Codex and Claude preserve native context. `beforeMessageId` excludes that prompt and every
176
+ * later message; send the edited prompt to the returned chat with `turn`. Reuse `newChatId`
177
+ * and the turn's `requestId` on retries. The source chat and filesystem are unchanged.
178
+ * A source with a live turn returns 409; an unknown source returns 404.
179
179
  */
180
- forkChat(chatId: string, opts?: {
181
- newChatId?: string;
182
- }): Promise<ChatSummary>;
180
+ forkChat(chatId: string, opts?: ForkChatOptions): Promise<ChatForkResult>;
183
181
  /**
184
182
  * `GET /chats/{id}/messages` — a chat's transcript (native when the agent supports it, else
185
183
  * the recorded fallback). `limit` caps to the newest N; `before` pages older history.
@@ -0,0 +1,13 @@
1
+ import { type ComputerConfig } from "./lifecycle.js";
2
+ export interface ComputerConnectionChange {
3
+ id: string;
4
+ patch: Pick<Partial<ComputerConfig>, "relay" | "host">;
5
+ status: "queued" | "connecting" | "complete" | "failed";
6
+ message?: string;
7
+ }
8
+ /** The controller verifies a replacement before committing it. Parallel CLI
9
+ * invocations join the same settings change, or run one after the other. */
10
+ export declare function changeComputerConnection(directory: string, patch: ComputerConnectionChange["patch"], options?: {
11
+ signal?: AbortSignal;
12
+ onProgress?: (message: string) => void;
13
+ }): Promise<void>;
@@ -4,12 +4,12 @@ export declare function connectionAction(value: string | undefined): ConnectionA
4
4
  /** Safe to copy into either a Unix shell or PowerShell, even when an ID starts
5
5
  * with a dash. Validate old daemons too; these values originated on a phone. */
6
6
  export declare function approvalCommand(pairingId: string, requestId: string): string;
7
- /** Saved approvals do not prove that the phone still has its key or address.
8
- * Every disconnected phone uses the same QR; the signed handshake chooses resume
9
- * or fresh approval. The old commands remain aliases, not separate UX paths. */
7
+ /** Stable endpoints need no new invitation after network loss or restart.
8
+ * An approval means ready to reconnect, never proof of current connectivity. */
10
9
  export declare function chooseConnectionAction(options: {
11
10
  requested?: ConnectionAction;
12
11
  devices: ComputerDevice[];
12
+ persistent?: boolean;
13
13
  }): Promise<ConnectionAction>;
14
14
  /** A fresh key is never merged by name. Replacement requires the owner's explicit
15
15
  * choice of the exact approvals shown here; a different same-name phone can be added. */
@@ -0,0 +1,8 @@
1
+ export declare class DirectoryRequestError extends Error {
2
+ readonly status: number;
3
+ readonly code: string;
4
+ readonly retryAfter: number;
5
+ constructor(status: number, code: string, retryAfter?: number);
6
+ }
7
+ /** Bounded, same-origin requests. No redirects, cookie jar or retained credentials. */
8
+ export declare function directoryRequest<T>(url: string, path: string, body: unknown): Promise<T>;
@@ -0,0 +1,13 @@
1
+ import type { Mindwire } from "../client.js";
2
+ import type { ComputerDiscoveryStatus } from "../computer.js";
3
+ import type { SetupPrompt } from "./setup-prompt.js";
4
+ export declare const DIRECTORY_API_PATH = "/api/computer-directory/v1";
5
+ export declare const DIRECTORY_URL = "https://console.mindwire.sh/api/computer-directory/v1";
6
+ export declare function directoryURL(value: string): string;
7
+ export declare function enableAddressDiscovery(options: {
8
+ directory: string;
9
+ computer: Mindwire;
10
+ prompt: SetupPrompt;
11
+ url?: string;
12
+ }): Promise<ComputerDiscoveryStatus>;
13
+ export declare function discoveryDescription(status: ComputerDiscoveryStatus | undefined): string;
@@ -12,7 +12,7 @@ export interface ComputerConfig {
12
12
  websocketPort: number;
13
13
  relay: RelayOptions;
14
14
  }
15
- export declare const CONTROLLER_PROTOCOL = 2;
15
+ export declare const CONTROLLER_PROTOCOL = 3;
16
16
  export interface ControllerState extends ProcessState {
17
17
  protocol?: number;
18
18
  cliVersion?: string;
@@ -37,6 +37,7 @@ export declare function ensureComputer(directory: string, cliPath: string, patch
37
37
  resume?: boolean;
38
38
  signal?: AbortSignal;
39
39
  timeoutMs?: number;
40
+ waitForRoutes?: boolean;
40
41
  }): Promise<Mindwire>;
41
42
  export declare function superviseComputer(directory: string): Promise<void>;
42
43
  export declare function stopComputer(directory: string, force?: boolean): Promise<void>;
@@ -0,0 +1,27 @@
1
+ import type { Oblien } from "oblien";
2
+ import { type RelayOptions } from "./relay.js";
3
+ export interface RelayWorkerConfig {
4
+ options: RelayOptions;
5
+ port: number;
6
+ }
7
+ interface TunnelToken {
8
+ token: string;
9
+ connect_url: string;
10
+ expires_at: string;
11
+ }
12
+ /** Cache only the short-lived broker token. Renew early without disconnecting a
13
+ * healthy carrier; a later reconnect uses the newest token for the same tunnel. */
14
+ export declare class OblienTunnelLease {
15
+ private readonly tunnelId;
16
+ private readonly client;
17
+ private readonly now;
18
+ private value?;
19
+ private pending?;
20
+ constructor(tunnelId: number, client: () => Promise<Oblien>, now?: () => number);
21
+ get(force?: boolean): Promise<TunnelToken>;
22
+ }
23
+ export declare function relayFailureMessage(error: unknown, provider: string): string;
24
+ /** A separate owned process survives controller updates just like cloudflared.
25
+ * It does not own the daemon, terminals, identity, or phone authorization. */
26
+ export declare function runManagedRelay(file: string): Promise<void>;
27
+ export {};
@@ -0,0 +1,25 @@
1
+ import { Oblien } from "oblien";
2
+ import type { SetupPrompt } from "./setup-prompt.js";
3
+ export interface OblienCredentials {
4
+ token?: string;
5
+ clientId?: string;
6
+ clientSecret?: string;
7
+ baseUrl?: string;
8
+ }
9
+ export declare class ProviderSignInRequired extends Error {
10
+ readonly code = "provider_sign_in";
11
+ constructor(provider?: string);
12
+ }
13
+ export declare function tokenExpiry(token: string): number | undefined;
14
+ export declare function oblienClient(credentials: OblienCredentials): Oblien;
15
+ /** The SDK's resource methods share this transport but do not yet accept a
16
+ * timeout. Bound every request, including tunnel creation and token renewal,
17
+ * without bypassing the official resource API or retrying mutations. */
18
+ export declare function boundedOblienClient(client: Oblien, timeoutMs?: number): Oblien;
19
+ /** Auth is not yet a public SDK resource. Use the SDK's authenticated transport
20
+ * for the documented browser-session endpoints; all tunnel calls use edgeTunnel. */
21
+ export declare function authenticatedOblien(file: string, options?: {
22
+ now?: () => number;
23
+ client?: typeof oblienClient;
24
+ }): Promise<Oblien>;
25
+ export declare function signInOblien(file: string, prompt: SetupPrompt): Promise<Oblien>;
@@ -0,0 +1,9 @@
1
+ export type ProviderErrorCode = "provider_sign_in" | "provider_limit" | "provider_missing" | "provider_certificate" | "provider_configuration" | "provider_connection";
2
+ export declare class ProviderConnectionError extends Error {
3
+ readonly code: ProviderErrorCode;
4
+ constructor(code: ProviderErrorCode, message: string);
5
+ }
6
+ export declare function providerNeedsAction(code: string | undefined): boolean;
7
+ /** Inspect transport causes, but never forward their raw text or headers. */
8
+ export declare function hasCertificateError(error: unknown): boolean;
9
+ export declare function providerFailure(error: unknown, provider: string): ProviderConnectionError;
@@ -0,0 +1,7 @@
1
+ import type { ComputerConnectionInfo } from "../computer.js";
2
+ import type { RelayOptions } from "./relay.js";
3
+ /** Credential renewal does not change the address already saved by the phone. */
4
+ export declare function connectionAddress(relay: RelayOptions, host?: string): string;
5
+ /** Public, non-secret metadata. Route/SSH identity is independent of provider credentials. */
6
+ export declare function connectionInfo(relay: RelayOptions, host?: string): ComputerConnectionInfo;
7
+ export declare function connectionDescription(info: ComputerConnectionInfo): string;
@@ -0,0 +1,20 @@
1
+ import type { Oblien } from "oblien";
2
+ import { type ComputerConfig } from "./lifecycle.js";
3
+ import type { SetupPrompt } from "./setup-prompt.js";
4
+ export type ConnectionProvider = "oblien" | "cloudflare" | "ngrok" | "vpn" | "custom" | "temporary" | "automatic";
5
+ export declare function chooseProvider(prompt: SetupPrompt): Promise<ConnectionProvider>;
6
+ export declare function connectionProvider(value: string): ConnectionProvider;
7
+ export declare function providerHostname(value: string): string;
8
+ /** No port-based account-wide reuse: two computers commonly use the same port. */
9
+ export declare function prepareOblienTunnel(client: Oblien, stateFile: string, port: number): Promise<{
10
+ id: number;
11
+ url: string;
12
+ }>;
13
+ /** Setup is explicit and serialized. Background startup only consumes the saved
14
+ * result: it never opens a browser, changes provider, or creates another tunnel. */
15
+ export declare function setupProvider(options: {
16
+ directory: string;
17
+ provider: ConnectionProvider;
18
+ prompt: SetupPrompt;
19
+ config?: ComputerConfig;
20
+ }): Promise<Partial<ComputerConfig>>;
@@ -2,9 +2,9 @@ import { type LookupFunction } from "node:net";
2
2
  import type { ComputerRoute } from "../computer.js";
3
3
  import type { RelayOptions } from "./relay.js";
4
4
  export declare class RelayCheckError extends Error {
5
- readonly code: "dns" | "timeout" | "proxy" | "connection" | "response";
5
+ readonly code: "dns" | "timeout" | "proxy" | "connection" | "response" | "provider_certificate";
6
6
  readonly addressMissing: boolean;
7
- constructor(code: "dns" | "timeout" | "proxy" | "connection" | "response", message: string, cause?: Error, addressMissing?: boolean);
7
+ constructor(code: "dns" | "timeout" | "proxy" | "connection" | "response" | "provider_certificate", message: string, cause?: Error, addressMissing?: boolean);
8
8
  }
9
9
  /** Local NXDOMAIN can be a stale cache. A missing address confirmed by the
10
10
  * provider is different: its helper may stay alive after the quick tunnel expires.
@@ -2,25 +2,33 @@ import { type ChildProcess } from "node:child_process";
2
2
  import type { ComputerRoute } from "../computer.js";
3
3
  import { type OwnedProcess } from "./process.js";
4
4
  export interface RelayOptions {
5
- kind: "none" | "cloudflare" | "ngrok" | "custom";
5
+ kind: "none" | "oblien" | "cloudflare" | "ngrok" | "custom";
6
6
  /** A stable, already configured hostname; custom relays must forward WebSocket upgrades. */
7
7
  url?: string;
8
8
  cloudflareTokenFile?: string;
9
+ cloudflareTunnelId?: string;
10
+ cloudflareCredentialsFile?: string;
11
+ ngrokTokenFile?: string;
12
+ oblienTunnelId?: number;
13
+ oblienCredentialsFile?: string;
9
14
  }
10
15
  export interface RelayHandle {
11
16
  route?: ComputerRoute;
12
17
  process?: ChildProcess;
13
18
  owner?: OwnedProcess;
14
19
  outputFile?: string;
20
+ configFile?: string;
15
21
  close(): void | Promise<void>;
16
22
  }
17
- export declare function adoptRelay(owner: OwnedProcess, route: ComputerRoute | undefined, directory: string, outputFile?: string): Promise<RelayHandle>;
23
+ export declare function adoptRelay(owner: OwnedProcess, route: ComputerRoute | undefined, directory: string, outputFile?: string, configFile?: string): Promise<RelayHandle>;
18
24
  export declare function websocketURL(value: string): string;
25
+ export declare function validateRelayOptions(options: RelayOptions): void;
19
26
  /** Both providers forward encrypted SSH bytes to one loopback WebSocket bridge. */
20
27
  export declare function startRelay(options: RelayOptions, port: number, runtime?: {
21
28
  cacheDir?: string;
22
29
  outputDirectory?: string;
30
+ cliPath?: string;
23
31
  onProgress?: (message: string) => void | Promise<void>;
24
32
  signal?: AbortSignal;
25
- onSpawn?: (owner: OwnedProcess, outputFile: string) => Promise<void>;
33
+ onSpawn?: (owner: OwnedProcess, outputFile: string, configFile?: string) => Promise<void>;
26
34
  }): Promise<RelayHandle>;
@@ -0,0 +1,9 @@
1
+ export interface SetupPrompt {
2
+ question(text: string): Promise<string>;
3
+ secret(text: string): Promise<string>;
4
+ print(text: string): void;
5
+ open(url: string): void;
6
+ }
7
+ /** No shell interpolation, even for a URL returned by a provider. */
8
+ export declare function openSetupURL(value: string): void;
9
+ export declare const terminalSetupPrompt: SetupPrompt;
@@ -7,6 +7,10 @@ export type ComputerRoute = {
7
7
  kind: "websocket";
8
8
  url: string;
9
9
  };
10
+ export interface ComputerConnectionInfo {
11
+ provider: "oblien" | "cloudflare" | "ngrok" | "custom" | "direct";
12
+ address: "persistent" | "temporary" | "network";
13
+ }
10
14
  export interface ComputerInvitation {
11
15
  version: 1;
12
16
  computerId: string;
@@ -37,6 +41,23 @@ export interface ComputerDevice {
37
41
  createdAt: string;
38
42
  revoked: boolean;
39
43
  connected?: boolean;
44
+ /** This phone acknowledged securely saving its address-discovery key. */
45
+ addressRecovery?: boolean;
46
+ }
47
+ export interface ComputerDiscoveryStatus {
48
+ version: 1;
49
+ enabled: boolean;
50
+ directoryId: string;
51
+ url?: string;
52
+ sequence?: number;
53
+ publishedSequence?: number;
54
+ publishedAt?: number;
55
+ error?: string;
56
+ }
57
+ export interface ComputerDirectoryEnvelope {
58
+ publicKey: string;
59
+ payload: string;
60
+ signature: string;
40
61
  }
41
62
  /** Signature is a base64 Ed25519 proof over
42
63
  * `mindwire-computer-pairing-v1\n${pairingId}\n${id}\n${name}\n${publicKey}`.
@@ -79,6 +100,8 @@ export interface ComputerInfo {
79
100
  /** 2 supports signed reconnection and atomic replacement of lost device keys.
80
101
  * 3 requires signed pairing acknowledgements and expires/revokes pairing transports. */
81
102
  pairingVersion?: number;
103
+ connection?: ComputerConnectionInfo;
104
+ discovery?: ComputerDiscoveryStatus;
82
105
  }
83
106
  export interface ComputerForwardRequest {
84
107
  id: string;
@@ -93,7 +116,10 @@ export declare class ComputerApi {
93
116
  private readonly client;
94
117
  constructor(client: Mindwire);
95
118
  info(): Promise<ComputerInfo>;
96
- setRoutes(routes: ComputerRoute[]): Promise<ComputerInfo>;
119
+ discovery(): Promise<ComputerDiscoveryStatus>;
120
+ configureDiscovery(enabled: boolean, url?: string): Promise<ComputerDiscoveryStatus>;
121
+ directoryEnrollment(url: string, challenge: string): Promise<ComputerDirectoryEnvelope>;
122
+ setRoutes(routes: ComputerRoute[], connection?: ComputerConnectionInfo): Promise<ComputerInfo>;
97
123
  updateStatus(): Promise<ComputerUpdate>;
98
124
  /** Automatic requests wait for idle. With serviceUpdateVersion >= 2, force
99
125
  * promotes the same active request to an explicit restart without re-downloading. */
@@ -0,0 +1,41 @@
1
+ import { acquireProcessLock, readJSON, writeJSON } from './chunk-CKTVMUS4.js';
2
+ import './chunk-PFYKJ6LX.js';
3
+ import { randomUUID } from 'crypto';
4
+ import * as path from 'path';
5
+ import { setTimeout } from 'timers/promises';
6
+
7
+ async function changeComputerConnection(directory, patch, options = {}) {
8
+ const lock = await acquireProcessLock(path.join(directory, "connection-change.lock"), { signal: options.signal });
9
+ const file = path.join(directory, "computer-connection-change.json");
10
+ try {
11
+ const saved = await readJSON(path.join(directory, "computer-config.json"));
12
+ if (Object.entries(patch).every(([key, value]) => JSON.stringify(saved?.[key]) === JSON.stringify(value))) return;
13
+ const previous = await readJSON(file);
14
+ if (previous && ["queued", "connecting"].includes(previous.status) && JSON.stringify(previous.patch) !== JSON.stringify(patch)) {
15
+ throw new Error("Another connection change is still running. Run mindwire status, then retry.");
16
+ }
17
+ const request = previous && ["queued", "connecting"].includes(previous.status) ? previous : { id: randomUUID(), patch, status: "queued" };
18
+ await writeJSON(file, request);
19
+ let message;
20
+ const deadline = Date.now() + 13e4;
21
+ while (Date.now() < deadline) {
22
+ options.signal?.throwIfAborted();
23
+ const current = await readJSON(file);
24
+ if (current?.id !== request.id) throw new Error("The connection change was replaced. Run mindwire status.");
25
+ if (current.status === "complete") return;
26
+ if (current.status === "failed") throw new Error(current.message ?? "Connection setup failed. The previous connection is unchanged.");
27
+ if (current.message && message !== current.message) {
28
+ message = current.message;
29
+ options.onProgress?.(message);
30
+ }
31
+ await setTimeout(250, void 0, { signal: options.signal });
32
+ }
33
+ throw new Error("Connection setup is still running in the background. Run mindwire status for its result.");
34
+ } finally {
35
+ await lock?.close();
36
+ }
37
+ }
38
+
39
+ export { changeComputerConnection };
40
+ //# sourceMappingURL=connection-change-5F4WNCYV.js.map
41
+ //# sourceMappingURL=connection-change-5F4WNCYV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/computer/connection-change.ts"],"names":["delay"],"mappings":";;;;;;AAeA,eAAsB,wBAAA,CAAyB,SAAA,EAAmB,KAAA,EAA0C,OAAA,GAExG,EAAC,EAAkB;AACrB,EAAA,MAAM,IAAA,GAAO,MAAM,kBAAA,CAAwB,IAAA,CAAA,IAAA,CAAK,SAAA,EAAW,wBAAwB,CAAA,EAAG,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAQ,CAAA;AAChH,EAAA,MAAM,IAAA,GAAY,IAAA,CAAA,IAAA,CAAK,SAAA,EAAW,iCAAiC,CAAA;AACnE,EAAA,IAAI;AACF,IAAA,MAAM,QAAQ,MAAM,QAAA,CAA8B,IAAA,CAAA,IAAA,CAAK,SAAA,EAAW,sBAAsB,CAAC,CAAA;AACzF,IAAA,IAAI,MAAA,CAAO,QAAQ,KAAK,CAAA,CAAE,MAAM,CAAC,CAAC,KAAK,KAAK,CAAA,KAAM,KAAK,SAAA,CAAU,KAAA,GAAQ,GAA2B,CAAC,CAAA,KAAM,KAAK,SAAA,CAAU,KAAK,CAAC,CAAA,EAAG;AACnI,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAmC,IAAI,CAAA;AAC9D,IAAA,IAAI,YAAY,CAAC,QAAA,EAAU,YAAY,CAAA,CAAE,SAAS,QAAA,CAAS,MAAM,CAAA,IAAK,IAAA,CAAK,UAAU,QAAA,CAAS,KAAK,MAAM,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,EAAG;AAC9H,MAAA,MAAM,IAAI,MAAM,8EAA8E,CAAA;AAAA,IAChG;AACA,IAAA,MAAM,UAAU,QAAA,IAAY,CAAC,QAAA,EAAU,YAAY,EAAE,QAAA,CAAS,QAAA,CAAS,MAAM,CAAA,GAAI,WAC7E,EAAE,EAAA,EAAI,YAAW,EAAG,KAAA,EAAO,QAAQ,QAAA,EAAkB;AACzD,IAAA,MAAM,SAAA,CAAU,MAAM,OAAO,CAAA;AAC7B,IAAA,IAAI,OAAA;AACJ,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,IAAA;AAC9B,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,EAAU;AAC5B,MAAA,OAAA,CAAQ,QAAQ,cAAA,EAAe;AAC/B,MAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAmC,IAAI,CAAA;AAC7D,MAAA,IAAI,SAAS,EAAA,KAAO,OAAA,CAAQ,IAAI,MAAM,IAAI,MAAM,0DAA0D,CAAA;AAC1G,MAAA,IAAI,OAAA,CAAQ,WAAW,UAAA,EAAY;AACnC,MAAA,IAAI,OAAA,CAAQ,WAAW,QAAA,EAAU,MAAM,IAAI,KAAA,CAAM,OAAA,CAAQ,WAAW,gEAAgE,CAAA;AACpI,MAAA,IAAI,OAAA,CAAQ,OAAA,IAAW,OAAA,KAAY,OAAA,CAAQ,OAAA,EAAS;AAAE,QAAA,OAAA,GAAU,OAAA,CAAQ,OAAA;AAAS,QAAA,OAAA,CAAQ,aAAa,OAAO,CAAA;AAAA,MAAG;AAChH,MAAA,MAAMA,WAAM,GAAA,EAAK,KAAA,CAAA,EAAW,EAAE,MAAA,EAAQ,OAAA,CAAQ,QAAQ,CAAA;AAAA,IACxD;AACA,IAAA,MAAM,IAAI,MAAM,0FAA0F,CAAA;AAAA,EAC5G,CAAA,SAAE;AAAU,IAAA,MAAM,MAAM,KAAA,EAAM;AAAA,EAAG;AACnC","file":"connection-change-5F4WNCYV.js","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport * as path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { acquireProcessLock } from \"./lock.js\";\nimport { readJSON, writeJSON, type ComputerConfig } from \"./lifecycle.js\";\n\nexport interface ComputerConnectionChange {\n id: string;\n patch: Pick<Partial<ComputerConfig>, \"relay\" | \"host\">;\n status: \"queued\" | \"connecting\" | \"complete\" | \"failed\";\n message?: string;\n}\n\n/** The controller verifies a replacement before committing it. Parallel CLI\n * invocations join the same settings change, or run one after the other. */\nexport async function changeComputerConnection(directory: string, patch: ComputerConnectionChange[\"patch\"], options: {\n signal?: AbortSignal; onProgress?: (message: string) => void;\n} = {}): Promise<void> {\n const lock = await acquireProcessLock(path.join(directory, \"connection-change.lock\"), { signal: options.signal });\n const file = path.join(directory, \"computer-connection-change.json\");\n try {\n const saved = await readJSON<ComputerConfig>(path.join(directory, \"computer-config.json\"));\n if (Object.entries(patch).every(([key, value]) => JSON.stringify(saved?.[key as keyof ComputerConfig]) === JSON.stringify(value))) return;\n const previous = await readJSON<ComputerConnectionChange>(file);\n if (previous && [\"queued\", \"connecting\"].includes(previous.status) && JSON.stringify(previous.patch) !== JSON.stringify(patch)) {\n throw new Error(\"Another connection change is still running. Run mindwire status, then retry.\");\n }\n const request = previous && [\"queued\", \"connecting\"].includes(previous.status) ? previous\n : { id: randomUUID(), patch, status: \"queued\" as const };\n await writeJSON(file, request);\n let message: string | undefined;\n const deadline = Date.now() + 130_000;\n while (Date.now() < deadline) {\n options.signal?.throwIfAborted();\n const current = await readJSON<ComputerConnectionChange>(file);\n if (current?.id !== request.id) throw new Error(\"The connection change was replaced. Run mindwire status.\");\n if (current.status === \"complete\") return;\n if (current.status === \"failed\") throw new Error(current.message ?? \"Connection setup failed. The previous connection is unchanged.\");\n if (current.message && message !== current.message) { message = current.message; options.onProgress?.(message); }\n await delay(250, undefined, { signal: options.signal });\n }\n throw new Error(\"Connection setup is still running in the background. Run mindwire status for its result.\");\n } finally { await lock?.close(); }\n}\n"]}