@sjhmars/happy-bridge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ //#region lib/types/invariant.js
2
+ /** Package invariant companion for `@sjhmars/happy-bridge`. */
3
+ const PACKAGE_NAME = "@sjhmars/happy-bridge";
4
+ const name = "happy-bridge-invariant";
5
+ const inject = ["invariants"];
6
+ /** No runtime invariant: Happy relay I/O is an external event stream this package does not own. */
7
+ const install = () => {};
8
+ /**
9
+ * Register this package's invariant companion.
10
+ * @param ctx - Host context carrying the invariant registry.
11
+ * @returns the registration disposer after setup succeeds.
12
+ */
13
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
14
+ //#endregion
15
+ export { apply, inject, name };
@@ -0,0 +1,38 @@
1
+ /** Hide/show a harness session in the sidebar archive set without a Host unarchive RPC. */
2
+ import { SessionId } from '@deepseek-ai/dsh-session';
3
+ /** `workspaceRegistry` fields this plugin uses for archive sync. */
4
+ export interface ArchiveRegistry {
5
+ readonly archivedSessionIds: readonly SessionId[];
6
+ archiveSession(sessionId: SessionId): Promise<void>;
7
+ }
8
+ /**
9
+ * Ids that entered or left the archive set.
10
+ * @param previous - last observed set.
11
+ * @param next - current `archivedSessionIds`.
12
+ */
13
+ export declare function archiveSetDiff(previous: ReadonlySet<string>, next: ReadonlySet<string>): {
14
+ hidden: string[];
15
+ shown: string[];
16
+ };
17
+ /**
18
+ * Keep Happy online when the row is still in the unarchived web sidebar.
19
+ * Opening an offline phone row does not start a heartbeat by itself.
20
+ * @param parked - plugin park flag.
21
+ * @param liveOnWeb - id is in the unarchived web sidebar.
22
+ */
23
+ export declare function phoneParkAction(parked: boolean, liveOnWeb: boolean): 'park' | 'unpark' | 'keep';
24
+ /**
25
+ * Archive on the Host (public API). Idempotent when already archived.
26
+ * @param registry - `ctx.workspaceRegistry`, if the profile loaded it.
27
+ * @param sessionId - harness session id.
28
+ */
29
+ export declare function hideOnHarness(registry: ArchiveRegistry | undefined, sessionId: string): Promise<void>;
30
+ /**
31
+ * Take one id out of the Host archive set so grouping surfaces show it again
32
+ * in its kept `sessionIds` slot. Uses the registry write chain; does not add
33
+ * a Harness API.
34
+ * @param registry - `ctx.workspaceRegistry`, if the profile loaded it.
35
+ * @param sessionId - harness session id.
36
+ */
37
+ export declare function revealOnHarness(registry: ArchiveRegistry | undefined, sessionId: string): Promise<void>;
38
+ //# sourceMappingURL=archive-sync.d.ts.map
@@ -0,0 +1,19 @@
1
+ /** Phone file events: sniff image bytes and split them from other attachments. */
2
+ import type { EncodedImageAttachment, ImageMediaType } from '@deepseek-ai/dsh-attachment/types';
3
+ import type { PendingFile } from './types.ts';
4
+ /**
5
+ * Detect a DSH image media type from magic bytes, then declared MIME, then filename.
6
+ * @param file - decrypted Happy attachment.
7
+ * @returns a version-one image media type, or `undefined` for other files.
8
+ */
9
+ export declare function sniffImageMime(file: PendingFile): ImageMediaType | undefined;
10
+ /**
11
+ * Split decrypted Happy files into DSH image uploads and leftover binaries.
12
+ * @param files - drained phone attachments in arrival order.
13
+ * @returns encoded images plus non-image files.
14
+ */
15
+ export declare function splitPendingFiles(files: readonly PendingFile[]): {
16
+ encoded: EncodedImageAttachment[];
17
+ extras: PendingFile[];
18
+ };
19
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1,227 @@
1
+ /** Host orchestrator: pair, mirror sessions, map chat/approvals/questions onto Happy. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { Config, PairingStatus } from './types.ts';
4
+ /**
5
+ * Live Happy bridge for one Host process.
6
+ */
7
+ export declare class HappyBridge {
8
+ private readonly ctx;
9
+ private config;
10
+ private readonly log;
11
+ private credentials;
12
+ private pairing;
13
+ private machine;
14
+ private readonly links;
15
+ private readonly happyToDsh;
16
+ private readonly models;
17
+ /** Last model/effort we wrote to Happy, so the metadata echo is not a phone pick. */
18
+ private readonly lastPublished;
19
+ /** Count of in-flight phone-originated Host `selectModel` calls. */
20
+ private hostSelectFromPhone;
21
+ /** Per-agent selection installed on phone wake / spawn, matching Host `selectionFor`. */
22
+ private readonly selections;
23
+ /** In-flight phone wakes, so two inbound texts do not double-resume. */
24
+ private readonly waking;
25
+ /** Phone stop-session / archive: do not recreate these Happy rows. */
26
+ private readonly dismissed;
27
+ /** Phone New (spawn) blanks stay linked; web placeholders do not. */
28
+ private readonly phoneSpawned;
29
+ /** Last Host archive set, so web archive/restore can park or unpark Happy. */
30
+ private lastArchivedIds;
31
+ /** Serialize phone text so two messages cannot split one attachment batch. */
32
+ private readonly inboundTail;
33
+ private error;
34
+ private running;
35
+ private scanTimer;
36
+ /**
37
+ * @param ctx - Host context.
38
+ * @param config - resolved plugin config.
39
+ * @param log - logger.
40
+ */
41
+ constructor(ctx: Context, config: Config, log: (message: string) => void);
42
+ /** Replace config after a settings write that keeps the same relay. */
43
+ setConfig(config: Config): void;
44
+ /**
45
+ * Apply a settings write in place when the Happy relay identity is unchanged.
46
+ * Grant changes take effect immediately; URL / credential-dir / enabled
47
+ * changes must rebuild.
48
+ * @param next - resolved settings section.
49
+ * @returns true when the live bridge kept running.
50
+ */
51
+ acceptSettings(next: Config): boolean;
52
+ /** Snapshot for the settings card. */
53
+ status(): PairingStatus;
54
+ /** Load credentials, connect machine, mirror live root agents. */
55
+ start(): Promise<void>;
56
+ /** Tear down sockets. Web UI keeps running. */
57
+ dispose(): void;
58
+ /** Start or resume pairing. */
59
+ beginPairing(): Promise<void>;
60
+ /**
61
+ * Drop the current Happy login and show a new QR. Keeps the same machine id
62
+ * so already-mirrored sessions stay on this Host after the phone scans again.
63
+ */
64
+ rePair(): Promise<void>;
65
+ /** Disconnect Happy without killing dsh web. */
66
+ disconnect(): Promise<void>;
67
+ private connectCloud;
68
+ private installHooks;
69
+ private isHarnessArchived;
70
+ private seedArchiveSet;
71
+ private reconcileArchiveSet;
72
+ private listedUnarchived;
73
+ private syncMirrors;
74
+ private ensureMirror;
75
+ private dropLink;
76
+ /**
77
+ * Remove a web New Session placeholder from Happy without remembering a
78
+ * dismiss: the first real turn should remirror it.
79
+ */
80
+ private abandonBlankMirror;
81
+ /**
82
+ * Drop Happy rows for dismissed or blank dsh tags. The App archive button
83
+ * only sets inactive; without this sweep a keepalive ghost stays in the list.
84
+ */
85
+ private sweepHappyGhosts;
86
+ private sessionIsBlank;
87
+ private linkEvents;
88
+ private requireAgent;
89
+ /** Bind a live agent onto an existing Happy socket without reminting it. */
90
+ private attachAgent;
91
+ /** Copy log events newer than {@link Link.lastForwardedSeq} onto Happy. */
92
+ private drainNewEvents;
93
+ /**
94
+ * Live agent for this Happy socket, resuming the persisted session when the
95
+ * web has never opened it. Same preset the Host would mount on a web open.
96
+ */
97
+ private ensureAgent;
98
+ private wakeAgent;
99
+ private resumeSession;
100
+ /**
101
+ * Resume/create composition matching Host `composeAgent`: install model
102
+ * selection, then mount the preset when a roster exists.
103
+ * @param presetHint - logged or requested preset id; omitted uses the roster default.
104
+ */
105
+ private composeAgentSetup;
106
+ /**
107
+ * Same lazy selection Host `selectionFor` installs: remembered pick, else
108
+ * the session's last `request/header`, else `agentDefaultModel`. A missing
109
+ * thinking level keeps the web picker's effort when it is the same model.
110
+ * Unlike Host `installModelSelection`, an absent effort does not clear
111
+ * inherited thinking.
112
+ */
113
+ private installWakeSelection;
114
+ /**
115
+ * Pin provider/model for a phone-woken agent without wiping thinking when
116
+ * the selection names no effort.
117
+ */
118
+ private bindWakeSelection;
119
+ /** Host default model, when the web profile mounted `agentDefaultModel`. */
120
+ private defaultModelSelection;
121
+ /** Registered workspace for this session, or `undefined` when it is not in the sidebar. */
122
+ private workspaceFor;
123
+ private loadStored;
124
+ private shouldSkip;
125
+ private workspaces;
126
+ private mirrorAgent;
127
+ private mirrorDormant;
128
+ private unmapHappy;
129
+ /**
130
+ * Phone archive: park a real conversation so a later send resumes it.
131
+ * Blank placeholders are forgotten and not remirrored.
132
+ */
133
+ private onPhoneArchive;
134
+ private handlePhoneArchive;
135
+ /**
136
+ * Park a real conversation on Happy. `hideHost` archives the same row on
137
+ * the web (phone archive). Web-initiated archive only parks Happy.
138
+ */
139
+ private parkPhoneSession;
140
+ private onPhoneRestore;
141
+ private undismiss;
142
+ /**
143
+ * Put this Happy row back online. Opening an offline chat on the phone
144
+ * does not start heartbeats; the web sidebar still showing the row, a
145
+ * phone send, or Happy's resume RPC all come through here.
146
+ * @param refreshCatalog - republish metadata (web open / phone resume).
147
+ */
148
+ private wakePhone;
149
+ private unpark;
150
+ /**
151
+ * Honor a phone archive/delete: stop keepalive, drop the Happy row, and do
152
+ * not remirror this harness session until the plugin is unpaired.
153
+ */
154
+ private forgetPhoneSession;
155
+ /**
156
+ * Happy App continues an offline row with `resume-happy-session`.
157
+ * Reuse the existing harness session; do not mint a new one.
158
+ * @param happySessionId - Happy cloud session id from the App.
159
+ */
160
+ private resumeHappySession;
161
+ private spawn;
162
+ private applySpawnMeta;
163
+ private queueInbound;
164
+ private onInbound;
165
+ private applyMessageMeta;
166
+ private applyPhoneCatalog;
167
+ private applyModel;
168
+ private applyEffort;
169
+ /** Keep the last Host pick so Happy metadata can echo the web composer. */
170
+ private rememberModel;
171
+ /**
172
+ * Write the web picker's Host selection (`session.selectModel`) so the
173
+ * composer model seat reloads without a click on the computer.
174
+ */
175
+ private syncHostSelection;
176
+ /**
177
+ * After the web picker (or any Host caller) lands a selection, publish it
178
+ * to Happy. Phone-originated calls set {@link hostSelectFromPhone} and push themselves.
179
+ */
180
+ private afterHostSelect;
181
+ /**
182
+ * Put a concrete reasoningEffort on a phone-spawned / phone-woken agent
183
+ * before the first LLM request, matching the effort Happy metadata advertises.
184
+ */
185
+ private ensurePinnedEffort;
186
+ private applyPermission;
187
+ /**
188
+ * Queue the phone text as a user followup. The App already shows the typed
189
+ * bubble; do not send a second user envelope. Images ride as DSH
190
+ * attachments; other files land under `happy-inbox` for Harness `read`.
191
+ */
192
+ private followup;
193
+ /**
194
+ * Claim every download started before this text, wait, keep the successes.
195
+ * Swap-then-await so a later file event cannot join this batch.
196
+ */
197
+ private drainPhoneFiles;
198
+ private downloadPhoneFile;
199
+ private onSessionEvent;
200
+ private onApproval;
201
+ private onAsk;
202
+ /**
203
+ * Happy App `sessionAbort` for Rig: empty params, RPC name `abort`.
204
+ * Watch grant keeps the button from doing work; chat and above cancel the turn.
205
+ */
206
+ private onPhoneAbort;
207
+ private onPermission;
208
+ private pushRequests;
209
+ private clearRequest;
210
+ private pushMetadata;
211
+ private pushAllMetadata;
212
+ private queueOutboundUser;
213
+ /**
214
+ * Upload web-side images with Happy CLI's encrypt-then-request-upload path,
215
+ * then emit file events and any remaining user text.
216
+ */
217
+ private pushUserToPhone;
218
+ private uploadOutboundImage;
219
+ private replayHistory;
220
+ private emitHistory;
221
+ private pulseThink;
222
+ private flushReasoning;
223
+ private finishThink;
224
+ private startTool;
225
+ private currentModel;
226
+ }
227
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1,16 @@
1
+ /** Base64 helpers matching Happy CLI `encodeBase64` / `decodeBase64`. */
2
+ /**
3
+ * Encode bytes as standard or URL-safe base64.
4
+ * @param buffer - bytes to encode.
5
+ * @param variant - `base64` (default) or `base64url` without padding.
6
+ * @returns encoded string.
7
+ */
8
+ export declare function encodeBase64(buffer: Uint8Array, variant?: 'base64' | 'base64url'): string;
9
+ /**
10
+ * Decode a standard or URL-safe base64 string.
11
+ * @param value - encoded string.
12
+ * @param variant - encoding used by `value`.
13
+ * @returns decoded bytes.
14
+ */
15
+ export declare function decodeBase64(value: string, variant?: 'base64' | 'base64url'): Uint8Array;
16
+ //# sourceMappingURL=bytes.d.ts.map
@@ -0,0 +1,119 @@
1
+ /** Build Happy session metadata catalogs from the live harness context. */
2
+ import type { Agent } from '@deepseek-ai/dsh-agent';
3
+ import type { Context } from '@deepseek-ai/cordis';
4
+ import type { RemoteGrant } from './types.ts';
5
+ /** One model row Happy's Rig picker understands (slash `code` plus provider/id). */
6
+ export interface HappyModelRow {
7
+ code: string;
8
+ value: string;
9
+ description?: string;
10
+ id: string;
11
+ name: string;
12
+ providerId: string;
13
+ providerKind: 'custom';
14
+ providerName: string;
15
+ thinkingLevels: string[];
16
+ defaultThinkingLevel?: string;
17
+ effortOptions: {
18
+ code: string;
19
+ value: string;
20
+ }[];
21
+ }
22
+ /** Current Host pick published so the phone can echo model + effort. */
23
+ export interface HappySelection {
24
+ provider: string;
25
+ model: string;
26
+ reasoningEffort?: string;
27
+ }
28
+ /** Plain metadata object written into the encrypted Happy session metadata field. */
29
+ export interface SessionMetadata {
30
+ path: string;
31
+ host: string;
32
+ homeDir: string;
33
+ version: string;
34
+ name: string;
35
+ summary: {
36
+ text: string;
37
+ updatedAt: number;
38
+ };
39
+ os: string;
40
+ machineId: string;
41
+ flavor: 'acp';
42
+ startedBy: 'terminal';
43
+ lifecycleState: string;
44
+ lifecycleStateSince: number;
45
+ happyHomeDir: string;
46
+ happyLibDir: string;
47
+ happyToolsDir: string;
48
+ slashCommands: string[];
49
+ skills: string[];
50
+ models: HappyModelRow[];
51
+ operatingModes: {
52
+ code: string;
53
+ value: string;
54
+ description: string;
55
+ }[];
56
+ currentModelCode?: string;
57
+ currentModelProviderId?: string;
58
+ modelMode?: string;
59
+ currentOperatingModeCode?: string;
60
+ thoughtLevels?: {
61
+ code: string;
62
+ value: string;
63
+ }[];
64
+ currentThoughtLevelCode?: string;
65
+ effortLevel?: string;
66
+ reasoning?: {
67
+ current: string | null;
68
+ levels: string[];
69
+ };
70
+ client: {
71
+ id: 'rig';
72
+ name: string;
73
+ version: string;
74
+ };
75
+ rigMetadataVersion: 1;
76
+ capabilities: {
77
+ abort: true;
78
+ attachments: {
79
+ enabled: true;
80
+ maxBytes: number;
81
+ mediaTypes: string[];
82
+ };
83
+ files: {
84
+ browse: false;
85
+ read: false;
86
+ search: false;
87
+ write: false;
88
+ };
89
+ modelSelection: true;
90
+ reasoningSelection: true;
91
+ permissionModeSelection: true;
92
+ resume: false;
93
+ rpcMethods: string[];
94
+ shell: false;
95
+ steering: false;
96
+ };
97
+ tools?: string[];
98
+ }
99
+ /**
100
+ * Snapshot catalogs for one session, live or still sitting in the sidebar.
101
+ * @param ctx - Host context.
102
+ * @param source - real cwd for skills, POSIX `happyPath` for the App list, log, and optional live agent.
103
+ * @param machineId - Happy machine id.
104
+ * @param title - session display name.
105
+ * @param selection - current provider/model/effort, when known.
106
+ * @param grant - unused in metadata; kept for future capability bits.
107
+ * @returns plaintext metadata.
108
+ */
109
+ export declare function buildSessionMetadata(ctx: Context, source: {
110
+ cwd: string;
111
+ /** POSIX path under {@link VIRTUAL_HOME}; Happy groups the list by this. */
112
+ happyPath: string;
113
+ events: readonly {
114
+ type: string;
115
+ data: unknown;
116
+ }[];
117
+ agent?: Agent;
118
+ }, machineId: string, title: string, selection: HappySelection | undefined, _grant: RemoteGrant): Promise<SessionMetadata>;
119
+ //# sourceMappingURL=catalogs.d.ts.map
@@ -0,0 +1,41 @@
1
+ /** Settings → Plugins card: pairing QR, copy links, remote grant. */
2
+ import { type ReactNode } from 'react';
3
+ import type { SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
4
+ import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
5
+ import type { PairingStatus, RemoteGrant } from '../types.ts';
6
+ import { NS } from './locales.ts';
7
+ /** Fields this card reads from the `happy-bridge` settings namespace. */
8
+ export interface HappyBridgeSettings {
9
+ enabled?: boolean;
10
+ remoteGrant?: RemoteGrant;
11
+ }
12
+ /** Injected Host RPC + settings writes. Snapshot rides `hooks.happySettings`. */
13
+ export interface HappyBridgeInjected {
14
+ /** Poll pairing status (includes QR data URL). */
15
+ getStatus: () => Promise<PairingStatus>;
16
+ /** Start or resume pairing. */
17
+ startPairing: () => Promise<PairingStatus>;
18
+ /** Disconnect Happy; the web UI stays up. */
19
+ disconnect: () => Promise<PairingStatus>;
20
+ /** Drop the current login and show a new QR. */
21
+ rePair: () => Promise<PairingStatus>;
22
+ /** Persist the remote-grant field. */
23
+ setGrant: (grant: RemoteGrant) => Promise<void>;
24
+ /** Persist the enabled field. */
25
+ setEnabled: (enabled: boolean) => Promise<void>;
26
+ hooks: {
27
+ /** Live settings snapshot for this namespace. */
28
+ happySettings: {
29
+ getSnapshot(): SettingsScopeSnapshot<HappyBridgeSettings>;
30
+ subscribe(fn: () => void): () => void;
31
+ };
32
+ };
33
+ }
34
+ /** Card props: locale seat plus injected RPC. */
35
+ export type HappyBridgeCardProps = PropsLocale<typeof NS> & InjectFace<HappyBridgeInjected>;
36
+ /**
37
+ * Render the Happy remote plugin card.
38
+ * @param props - locale + injected Host RPC.
39
+ */
40
+ export declare function HappyBridgeCard(props: HappyBridgeCardProps): ReactNode;
41
+ //# sourceMappingURL=HappyBridgeCard.d.ts.map
@@ -0,0 +1,16 @@
1
+ /** Browser half: Happy remote card on Settings → Plugins. */
2
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
3
+ import { type HappyBridgeKey } from './locales.ts';
4
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
5
+ interface LocaleNamespaceMap {
6
+ 'happy-bridge': HappyBridgeKey;
7
+ }
8
+ }
9
+ /** Required client services. */
10
+ export declare const inject: string[];
11
+ /**
12
+ * Register the Happy remote card under the `happy-bridge` settings namespace.
13
+ * @param ctx - browser plugin context.
14
+ */
15
+ export declare function apply(ctx: ClientContext): void;
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,34 @@
1
+ /** Locale namespace owned by the Happy remote settings card. */
2
+ export declare const NS = "happy-bridge";
3
+ /** Simplified-Chinese copy. */
4
+ export declare const zh: {
5
+ readonly title: "Happy 远程";
6
+ readonly description: "用手机 Happy App 遥控已经在跑的对话。";
7
+ readonly expand: "展开";
8
+ readonly collapse: "收起";
9
+ readonly 'status.unpaired': "还没配对";
10
+ readonly 'status.pairing': "等待手机扫码…请在 Happy App 里扫,不要用系统相机。";
11
+ readonly 'status.paired': "已连接到 Happy";
12
+ readonly 'status.linked': "已接通对话";
13
+ readonly 'qr.alt': "Happy 配对二维码";
14
+ readonly 'copy.mobile': "复制手机链接";
15
+ readonly 'copy.web': "复制网页链接";
16
+ readonly copied: "已复制";
17
+ readonly start: "开始配对";
18
+ readonly repair: "重新配对";
19
+ readonly disconnect: "断开";
20
+ readonly grant: "手机能管多深";
21
+ readonly 'grant.watch': "只看";
22
+ readonly 'grant.chat': "能聊";
23
+ readonly 'grant.approve': "能批";
24
+ readonly 'grant.full': "完整";
25
+ readonly 'grant.hint': "改成「完整」后,手机换模型和思考强度会同步到电脑。电脑上换也会同步到手机。";
26
+ readonly readOnly: "这份设置现在不能改。";
27
+ readonly enabled: "启用";
28
+ readonly error: "出错";
29
+ };
30
+ /** English copy. */
31
+ export declare const en: Record<keyof typeof zh, string>;
32
+ /** Stable locale keys. */
33
+ export type HappyBridgeKey = keyof typeof zh;
34
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,59 @@
1
+ /** Load and store Happy credentials. Prefer `~/.happy/access.key`, else plugin dir. */
2
+ import type { Credentials } from './types.ts';
3
+ /**
4
+ * Resolve the plugin credential directory.
5
+ * @param configured - Config.credentialDir; empty means the default under home.
6
+ * @returns absolute directory path.
7
+ */
8
+ export declare function resolveCredentialDir(configured: string): string;
9
+ /**
10
+ * Load credentials: Happy CLI file first, then this plugin's file.
11
+ * @param credentialDir - plugin directory.
12
+ * @returns credentials, or `undefined` when nothing usable is on disk.
13
+ */
14
+ export declare function loadCredentials(credentialDir: string): Promise<Credentials | undefined>;
15
+ /**
16
+ * Persist credentials in the plugin directory. Does not overwrite `~/.happy/access.key`.
17
+ * @param credentialDir - plugin directory.
18
+ * @param credentials - token + encryption + machineId.
19
+ */
20
+ export declare function saveCredentials(credentialDir: string, credentials: Credentials): Promise<void>;
21
+ /**
22
+ * Mark the plugin disconnected without deleting Happy CLI credentials.
23
+ * Clears phone-dismissed ids so a later pair remirrors those sessions.
24
+ * @param credentialDir - plugin directory.
25
+ * @param machineId - last known machine id to keep stable.
26
+ */
27
+ export declare function markDisconnected(credentialDir: string, machineId?: string): Promise<void>;
28
+ /**
29
+ * Clear the local disconnected flag so existing credentials can be reused.
30
+ * @param credentialDir - plugin directory.
31
+ * @param machineId - machine id to keep.
32
+ */
33
+ export declare function markConnected(credentialDir: string, machineId: string): Promise<void>;
34
+ /**
35
+ * Last stored machine id, even when locally disconnected.
36
+ * @param credentialDir - plugin directory.
37
+ * @returns machine id, or `undefined`.
38
+ */
39
+ export declare function peekMachineId(credentialDir: string): Promise<string | undefined>;
40
+ /**
41
+ * Session ids the phone asked to stop mirroring. stop-session / archive
42
+ * persist here so a later scan does not recreate the Happy row.
43
+ * @param credentialDir - plugin directory.
44
+ * @returns harness session ids, possibly empty.
45
+ */
46
+ export declare function loadDismissed(credentialDir: string): Promise<string[]>;
47
+ /**
48
+ * Forget a previously dismissed harness session so it can remirror.
49
+ * @param credentialDir - plugin directory.
50
+ * @param dshId - harness session id.
51
+ */
52
+ export declare function removeDismissed(credentialDir: string, dshId: string): Promise<void>;
53
+ /**
54
+ * Remember that the phone dismissed this harness session.
55
+ * @param credentialDir - plugin directory.
56
+ * @param dshId - harness session id.
57
+ */
58
+ export declare function addDismissed(credentialDir: string, dshId: string): Promise<void>;
59
+ //# sourceMappingURL=credentials.d.ts.map