@cjhyy/code-shell-server 0.9.8 → 0.9.10

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 (111) hide show
  1. package/README.md +85 -23
  2. package/dist/bin/code-shell-project-runtime.d.ts +2 -0
  3. package/dist/bin/code-shell-project-runtime.js +8 -0
  4. package/dist/desktop-web/http-api.d.ts +43 -0
  5. package/dist/desktop-web/http-api.js +376 -0
  6. package/dist/hub/approval-lease.d.ts +30 -0
  7. package/dist/hub/approval-lease.js +89 -0
  8. package/dist/hub/auth-http.d.ts +23 -0
  9. package/dist/hub/auth-http.js +259 -0
  10. package/dist/hub/auth-store.d.ts +52 -0
  11. package/dist/hub/auth-store.js +316 -0
  12. package/dist/hub/configuration.d.ts +64 -0
  13. package/dist/hub/configuration.js +555 -0
  14. package/dist/hub/files.d.ts +9 -0
  15. package/dist/hub/files.js +278 -0
  16. package/dist/hub/mcp-configuration.d.ts +51 -0
  17. package/dist/hub/mcp-configuration.js +558 -0
  18. package/dist/hub/mcp-probe.d.ts +24 -0
  19. package/dist/hub/mcp-probe.js +392 -0
  20. package/dist/hub/model-probe.d.ts +20 -0
  21. package/dist/hub/model-probe.js +193 -0
  22. package/dist/hub/outbound-transport.d.ts +36 -0
  23. package/dist/hub/outbound-transport.js +122 -0
  24. package/dist/hub/run-replay.d.ts +42 -0
  25. package/dist/hub/run-replay.js +89 -0
  26. package/dist/hub/session-management.d.ts +36 -0
  27. package/dist/hub/session-management.js +405 -0
  28. package/dist/hub/skills-management.d.ts +43 -0
  29. package/dist/hub/skills-management.js +487 -0
  30. package/dist/hub/uploads.d.ts +22 -0
  31. package/dist/hub/uploads.js +241 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.desktop-web.d.ts +2 -0
  34. package/dist/index.desktop-web.js +2 -0
  35. package/dist/index.js +3 -0
  36. package/dist/index.links.d.ts +6 -0
  37. package/dist/index.links.js +6 -0
  38. package/dist/index.panels.d.ts +9 -0
  39. package/dist/index.panels.js +8 -0
  40. package/dist/index.serve.d.ts +1 -0
  41. package/dist/index.serve.js +1 -0
  42. package/dist/index.storage.d.ts +2 -0
  43. package/dist/index.storage.js +2 -0
  44. package/dist/links/catalog.d.ts +10 -0
  45. package/dist/links/catalog.js +41 -0
  46. package/dist/links/device-oauth.d.ts +52 -0
  47. package/dist/links/device-oauth.js +255 -0
  48. package/dist/links/http.d.ts +34 -0
  49. package/dist/links/http.js +144 -0
  50. package/dist/links/service.d.ts +54 -0
  51. package/dist/links/service.js +578 -0
  52. package/dist/links/types.d.ts +6 -0
  53. package/dist/links/types.js +1 -0
  54. package/dist/mobile-remote/mobile-client-event-validator.js +5 -2
  55. package/dist/mobile-remote/pending-approvals.js +2 -0
  56. package/dist/mobile-remote/remote-host-manager.d.ts +8 -0
  57. package/dist/mobile-remote/remote-host-manager.js +90 -15
  58. package/dist/mobile-remote/room-manager.js +17 -5
  59. package/dist/panels/agent-task-host.d.ts +32 -0
  60. package/dist/panels/agent-task-host.js +457 -0
  61. package/dist/panels/agent-task-models.d.ts +27 -0
  62. package/dist/panels/agent-task-models.js +51 -0
  63. package/dist/panels/agent-task-service.d.ts +105 -0
  64. package/dist/panels/agent-task-service.js +283 -0
  65. package/dist/panels/http.d.ts +34 -0
  66. package/dist/panels/http.js +37 -0
  67. package/dist/panels/hub-binding.d.ts +12 -0
  68. package/dist/panels/hub-binding.js +174 -0
  69. package/dist/panels/management-http.d.ts +29 -0
  70. package/dist/panels/management-http.js +119 -0
  71. package/dist/panels/management.d.ts +39 -0
  72. package/dist/panels/management.js +494 -0
  73. package/dist/panels/process-files.d.ts +14 -0
  74. package/dist/panels/process-files.js +299 -0
  75. package/dist/panels/process-service.d.ts +96 -0
  76. package/dist/panels/process-service.js +501 -0
  77. package/dist/panels/runtime-services.d.ts +24 -0
  78. package/dist/panels/runtime-services.js +462 -0
  79. package/dist/panels/runtime.d.ts +51 -0
  80. package/dist/panels/runtime.js +1063 -0
  81. package/dist/panels/storage-store.d.ts +9 -0
  82. package/dist/panels/storage-store.js +154 -0
  83. package/dist/panels/types.d.ts +52 -0
  84. package/dist/panels/types.js +1 -0
  85. package/dist/project-runtime/control-server.d.ts +19 -0
  86. package/dist/project-runtime/control-server.js +300 -0
  87. package/dist/project-runtime/docker-provider.d.ts +33 -0
  88. package/dist/project-runtime/docker-provider.js +487 -0
  89. package/dist/project-runtime/lock.d.ts +6 -0
  90. package/dist/project-runtime/lock.js +43 -0
  91. package/dist/project-runtime/managed-entry.d.ts +10 -0
  92. package/dist/project-runtime/managed-entry.js +113 -0
  93. package/dist/project-runtime/manager.d.ts +40 -0
  94. package/dist/project-runtime/manager.js +249 -0
  95. package/dist/project-runtime/proxy.d.ts +22 -0
  96. package/dist/project-runtime/proxy.js +706 -0
  97. package/dist/project-runtime/registry.d.ts +54 -0
  98. package/dist/project-runtime/registry.js +286 -0
  99. package/dist/project-runtime/types.d.ts +43 -0
  100. package/dist/project-runtime/types.js +1 -0
  101. package/dist/serve/cli.d.ts +5 -0
  102. package/dist/serve/cli.js +82 -23
  103. package/dist/serve/headless-server.d.ts +11 -0
  104. package/dist/serve/headless-server.js +737 -83
  105. package/dist/session-titles-store.d.ts +17 -0
  106. package/dist/session-titles-store.js +174 -0
  107. package/dist/sessions-service.d.ts +2 -0
  108. package/dist/sessions-service.js +8 -0
  109. package/dist/static-files.js +1 -0
  110. package/dist/worker-bridge-core.js +17 -6
  111. package/package.json +19 -5
@@ -0,0 +1,241 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { chmod, lstat, mkdir, open, readdir, rm } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { markAttachmentsSent, stageFileBytes, stageImageBytes, } from "../attachment-service.js";
6
+ const MAX_BYTES = 20 * 1024 * 1024;
7
+ const TTL_MS = 15 * 60 * 1000;
8
+ const SAFE_ID = /^[a-zA-Z0-9_-]{8,80}$/;
9
+ const emptyUploads = () => ({
10
+ attachments: [],
11
+ commit: async () => { },
12
+ release: () => { },
13
+ });
14
+ export function hubJson(res, status, body) {
15
+ if (res.writableEnded || res.destroyed)
16
+ return;
17
+ res.writeHead(status, {
18
+ "content-type": "application/json; charset=utf-8",
19
+ "cache-control": "no-store",
20
+ "x-content-type-options": "nosniff",
21
+ });
22
+ res.end(JSON.stringify(body));
23
+ }
24
+ /** Temporary, device-owned spools. Only the host can turn IDs into staged attachment paths. */
25
+ export class HubUploads {
26
+ root;
27
+ cwd;
28
+ uploads = new Map();
29
+ active = new Set();
30
+ closed = false;
31
+ constructor(root, cwd) {
32
+ this.root = root;
33
+ this.cwd = cwd;
34
+ }
35
+ async ready() {
36
+ await mkdir(this.root, { recursive: true, mode: 0o700 });
37
+ const info = await lstat(this.root);
38
+ if (!info.isDirectory() || info.isSymbolicLink())
39
+ throw new Error("unsafe upload directory");
40
+ await chmod(this.root, 0o700);
41
+ // A pending draft cannot survive a restart; staged attachments live in the workspace.
42
+ for (const name of await readdir(this.root)) {
43
+ if (/^[a-f0-9-]{36}\.upload$/.test(name))
44
+ await rm(join(this.root, name), { force: true });
45
+ }
46
+ }
47
+ async accept(id, owner, req, res, stillAuthorized) {
48
+ const reject = (status, error) => {
49
+ hubJson(res, status, { error });
50
+ req.resume();
51
+ };
52
+ if (!SAFE_ID.test(id))
53
+ return reject(400, "invalid upload id");
54
+ if (this.closed)
55
+ return reject(503, "server is shutting down");
56
+ await this.sweep();
57
+ if (this.uploads.has(id))
58
+ return reject(409, "upload id already exists");
59
+ if (this.active.size >= 4 ||
60
+ this.uploads.size >= 32 ||
61
+ [...this.uploads.values()].filter((entry) => entry.owner === owner).length >= 16) {
62
+ return reject(429, "too many uploads; send the current attachments first");
63
+ }
64
+ const size = Number(req.headers["content-length"]);
65
+ if (!req.headers["content-length"])
66
+ return reject(411, "content length required");
67
+ if (!Number.isSafeInteger(size) || size <= 0)
68
+ return reject(400, "empty or invalid upload");
69
+ if (size > MAX_BYTES)
70
+ return reject(413, "attachment exceeds 20 MiB");
71
+ if ([...this.uploads.values()].reduce((sum, entry) => sum + entry.size, size) >
72
+ 128 * 1024 * 1024) {
73
+ return reject(429, "upload storage quota exceeded");
74
+ }
75
+ let name;
76
+ try {
77
+ name = decodeURIComponent(String(req.headers["x-file-name"] ?? "attachment"));
78
+ }
79
+ catch {
80
+ return reject(400, "invalid file name");
81
+ }
82
+ if (!name.trim() || name.length > 255 || /[\x00-\x1f/\\]/.test(name)) {
83
+ return reject(400, "invalid file name");
84
+ }
85
+ const mimeType = String(req.headers["content-type"] ?? "application/octet-stream")
86
+ .split(";", 1)[0]
87
+ .trim()
88
+ .toLowerCase();
89
+ if (mimeType.length > 128 || !/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/.test(mimeType)) {
90
+ return reject(400, "invalid content type");
91
+ }
92
+ const upload = {
93
+ id,
94
+ owner,
95
+ name,
96
+ mimeType,
97
+ size,
98
+ path: join(this.root, `${randomUUID()}.upload`),
99
+ createdAt: Date.now(),
100
+ status: "receiving",
101
+ };
102
+ this.uploads.set(id, upload);
103
+ this.active.add(req);
104
+ let handle;
105
+ const deadline = setTimeout(() => req.destroy(new Error("upload timed out")), 30_000);
106
+ deadline.unref?.();
107
+ try {
108
+ // Recheck the managed root: a workspace tool must not redirect the upload writer via symlink.
109
+ if (!(await lstat(this.root)).isDirectory() || (await lstat(this.root)).isSymbolicLink()) {
110
+ throw new Error("unsafe upload directory");
111
+ }
112
+ handle = await open(upload.path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
113
+ let received = 0;
114
+ for await (const chunk of req) {
115
+ received += chunk.length;
116
+ if (received > size || received > MAX_BYTES)
117
+ throw new Error("upload exceeds declared length");
118
+ await handle.writeFile(chunk);
119
+ }
120
+ if (received !== size)
121
+ throw new Error("incomplete upload");
122
+ if (this.closed || !(await stillAuthorized())) {
123
+ hubJson(res, 401, { error: "session expired" });
124
+ throw new Error("session expired");
125
+ }
126
+ await handle.close();
127
+ handle = undefined;
128
+ upload.status = "ready";
129
+ hubJson(res, 201, { id, name, mimeType, size, path: id });
130
+ }
131
+ catch (error) {
132
+ this.uploads.delete(id);
133
+ await handle?.close().catch(() => { });
134
+ await rm(upload.path, { force: true });
135
+ hubJson(res, 400, { error: error instanceof Error ? error.message : "upload failed" });
136
+ }
137
+ finally {
138
+ clearTimeout(deadline);
139
+ this.active.delete(req);
140
+ }
141
+ }
142
+ async prepare(ids, owner, sessionId) {
143
+ if (ids === undefined)
144
+ return emptyUploads();
145
+ if (!Array.isArray(ids) ||
146
+ ids.length > 16 ||
147
+ new Set(ids).size !== ids.length ||
148
+ ids.some((id) => typeof id !== "string" || !SAFE_ID.test(id))) {
149
+ throw new Error("invalid upload ids");
150
+ }
151
+ if (!ids.length)
152
+ return emptyUploads();
153
+ if (typeof sessionId !== "string" || !/^[a-zA-Z0-9_-]{1,128}$/.test(sessionId)) {
154
+ throw new Error("valid sessionId required for attachments");
155
+ }
156
+ const records = ids.map((id) => {
157
+ const record = this.uploads.get(id);
158
+ if (!record ||
159
+ record.owner !== owner ||
160
+ record.status !== "ready" ||
161
+ Date.now() - record.createdAt >= TTL_MS)
162
+ throw new Error("upload not found or expired");
163
+ return record;
164
+ });
165
+ for (const record of records)
166
+ record.status = "claiming";
167
+ try {
168
+ const result = [];
169
+ for (const record of records) {
170
+ const handle = await open(record.path, constants.O_RDONLY | constants.O_NOFOLLOW);
171
+ let bytes;
172
+ try {
173
+ const info = await handle.stat();
174
+ if (!info.isFile() || info.size !== record.size || info.size > MAX_BYTES)
175
+ throw new Error("upload changed");
176
+ bytes = await handle.readFile();
177
+ }
178
+ finally {
179
+ await handle.close();
180
+ }
181
+ const input = {
182
+ cwd: this.cwd,
183
+ sessionId,
184
+ name: record.name,
185
+ mime: record.mimeType,
186
+ bytes,
187
+ origin: "mobile",
188
+ };
189
+ result.push(record.mimeType.startsWith("image/") && record.mimeType !== "image/svg+xml"
190
+ ? await stageImageBytes(input)
191
+ : await stageFileBytes(input));
192
+ }
193
+ let settled = false;
194
+ return {
195
+ attachments: result,
196
+ commit: async () => {
197
+ if (settled)
198
+ return;
199
+ settled = true;
200
+ for (const record of records)
201
+ this.uploads.delete(record.id);
202
+ try {
203
+ await markAttachmentsSent(this.cwd, sessionId, result);
204
+ }
205
+ finally {
206
+ for (const record of records)
207
+ await rm(record.path, { force: true });
208
+ }
209
+ },
210
+ release: () => {
211
+ if (settled)
212
+ return;
213
+ settled = true;
214
+ for (const record of records)
215
+ record.status = "ready";
216
+ },
217
+ };
218
+ }
219
+ catch (error) {
220
+ for (const record of records)
221
+ record.status = "ready";
222
+ throw error;
223
+ }
224
+ }
225
+ async sweep() {
226
+ for (const [id, record] of this.uploads) {
227
+ if (record.status === "ready" && Date.now() - record.createdAt >= TTL_MS) {
228
+ this.uploads.delete(id);
229
+ await rm(record.path, { force: true });
230
+ }
231
+ }
232
+ }
233
+ async close() {
234
+ this.closed = true;
235
+ for (const req of this.active)
236
+ req.destroy();
237
+ for (const record of this.uploads.values())
238
+ await rm(record.path, { force: true });
239
+ this.uploads.clear();
240
+ }
241
+ }
package/dist/index.d.ts CHANGED
@@ -8,3 +8,6 @@ export * from "./index.storage.js";
8
8
  export * from "./index.worker.js";
9
9
  export * from "./index.mobile-remote.js";
10
10
  export * from "./index.serve.js";
11
+ export * from "./index.desktop-web.js";
12
+ export * from "./index.links.js";
13
+ export * from "./index.panels.js";
@@ -0,0 +1,2 @@
1
+ /** Shared Web business HTTP API hosted by Desktop's existing remote listener. */
2
+ export * from "./desktop-web/http-api.js";
@@ -0,0 +1,2 @@
1
+ /** Shared Web business HTTP API hosted by Desktop's existing remote listener. */
2
+ export * from "./desktop-web/http-api.js";
package/dist/index.js CHANGED
@@ -8,3 +8,6 @@ export * from "./index.storage.js";
8
8
  export * from "./index.worker.js";
9
9
  export * from "./index.mobile-remote.js";
10
10
  export * from "./index.serve.js";
11
+ export * from "./index.desktop-web.js";
12
+ export * from "./index.links.js";
13
+ export * from "./index.panels.js";
@@ -0,0 +1,6 @@
1
+ /** Pure Node Link management shared by Desktop and standalone hosts. */
2
+ export * from "./links/catalog.js";
3
+ export * from "./links/device-oauth.js";
4
+ export * from "./links/service.js";
5
+ export * from "./links/types.js";
6
+ export * from "./links/http.js";
@@ -0,0 +1,6 @@
1
+ /** Pure Node Link management shared by Desktop and standalone hosts. */
2
+ export * from "./links/catalog.js";
3
+ export * from "./links/device-oauth.js";
4
+ export * from "./links/service.js";
5
+ export * from "./links/types.js";
6
+ export * from "./links/http.js";
@@ -0,0 +1,9 @@
1
+ export { createPanelHttp } from "./panels/http.js";
2
+ export { createPanelManagement, PanelManagementError } from "./panels/management.js";
3
+ export { createPanelRuntime, panelWebCompatibility } from "./panels/runtime.js";
4
+ export { PanelRuntimeServices } from "./panels/runtime-services.js";
5
+ export * from "./panels/process-service.js";
6
+ export * from "./panels/agent-task-service.js";
7
+ export * from "./panels/agent-task-models.js";
8
+ export * from "./panels/agent-task-host.js";
9
+ export type * from "./panels/types.js";
@@ -0,0 +1,8 @@
1
+ export { createPanelHttp } from "./panels/http.js";
2
+ export { createPanelManagement, PanelManagementError } from "./panels/management.js";
3
+ export { createPanelRuntime, panelWebCompatibility } from "./panels/runtime.js";
4
+ export { PanelRuntimeServices } from "./panels/runtime-services.js";
5
+ export * from "./panels/process-service.js";
6
+ export * from "./panels/agent-task-service.js";
7
+ export * from "./panels/agent-task-models.js";
8
+ export * from "./panels/agent-task-host.js";
@@ -1,3 +1,4 @@
1
1
  /** Ready-made headless HTTP/WebSocket host and CLI composition helpers. */
2
2
  export * from "./serve/headless-server.js";
3
+ export { startProjectControlServer, type ProjectControlServerOptions, } from "./project-runtime/control-server.js";
3
4
  export { parseServeArgs, resolveWebAppRoot, resolveWorkerCapabilityModules, resolveWorkerEntry, runServeCli, } from "./serve/cli.js";
@@ -1,3 +1,4 @@
1
1
  /** Ready-made headless HTTP/WebSocket host and CLI composition helpers. */
2
2
  export * from "./serve/headless-server.js";
3
+ export { startProjectControlServer, } from "./project-runtime/control-server.js";
3
4
  export { parseServeArgs, resolveWebAppRoot, resolveWorkerCapabilityModules, resolveWorkerEntry, runServeCli, } from "./serve/cli.js";
@@ -8,3 +8,5 @@ export * from "./attachment-service.js";
8
8
  export * from "./client-message-id.js";
9
9
  export * from "./image-byte-probe.js";
10
10
  export * from "./sessions-service.js";
11
+ export * from "./session-titles-store.js";
12
+ export * from "./panels/storage-store.js";
@@ -8,3 +8,5 @@ export * from "./attachment-service.js";
8
8
  export * from "./client-message-id.js";
9
9
  export * from "./image-byte-probe.js";
10
10
  export * from "./sessions-service.js";
11
+ export * from "./session-titles-store.js";
12
+ export * from "./panels/storage-store.js";
@@ -0,0 +1,10 @@
1
+ import { type LinkProviderManifest } from "@cjhyy/code-shell-link";
2
+ import { type LocalLinkProviderSummary } from "@cjhyy/code-shell-core";
3
+ export type DesktopLinkProviderView = LinkProviderManifest & Pick<LocalLinkProviderSummary, "tokenLabel" | "tokenPlaceholder" | "actions">;
4
+ /**
5
+ * The independent Link package owns portable provider/auth metadata; Core owns
6
+ * trusted executors. Desktop is the host boundary that validates and combines
7
+ * them before any manifest reaches the renderer.
8
+ */
9
+ export declare function composeLinkProviderCatalog(manifests: readonly LinkProviderManifest[], executors: readonly LocalLinkProviderSummary[]): DesktopLinkProviderView[];
10
+ export declare function listDesktopLinkProviders(): DesktopLinkProviderView[];
@@ -0,0 +1,41 @@
1
+ import { LINK_PROVIDER_MANIFESTS } from "@cjhyy/code-shell-link";
2
+ import { listLocalLinkProviders } from "@cjhyy/code-shell-core";
3
+ function unique(values) {
4
+ return new Set(values).size === values.length;
5
+ }
6
+ /**
7
+ * The independent Link package owns portable provider/auth metadata; Core owns
8
+ * trusted executors. Desktop is the host boundary that validates and combines
9
+ * them before any manifest reaches the renderer.
10
+ */
11
+ export function composeLinkProviderCatalog(manifests, executors) {
12
+ const manifestIds = manifests.map((provider) => provider.id);
13
+ const executorIds = executors.map((provider) => provider.id);
14
+ if (!unique(manifestIds) || !unique(executorIds)) {
15
+ throw new Error("Link provider ids must be unique");
16
+ }
17
+ if (manifestIds.length !== executorIds.length ||
18
+ manifestIds.some((providerId) => !executorIds.includes(providerId))) {
19
+ throw new Error("Link provider manifests and local executors are out of sync");
20
+ }
21
+ return manifests.map((manifest) => {
22
+ const executor = executors.find((candidate) => candidate.id === manifest.id);
23
+ const declared = manifest.actionIds;
24
+ const implemented = executor.actions.map((action) => action.id);
25
+ if (!unique(declared) ||
26
+ !unique(implemented) ||
27
+ declared.length !== implemented.length ||
28
+ declared.some((actionId) => !implemented.includes(actionId))) {
29
+ throw new Error(`Link provider manifest Actions are out of sync: ${manifest.id}`);
30
+ }
31
+ return {
32
+ ...manifest,
33
+ tokenLabel: executor.tokenLabel,
34
+ tokenPlaceholder: executor.tokenPlaceholder,
35
+ actions: executor.actions,
36
+ };
37
+ });
38
+ }
39
+ export function listDesktopLinkProviders() {
40
+ return composeLinkProviderCatalog(LINK_PROVIDER_MANIFESTS, listLocalLinkProviders());
41
+ }
@@ -0,0 +1,52 @@
1
+ export type LocalBrowserLinkProviderId = "github" | "gitlab";
2
+ export interface LocalBrowserAuthStatus {
3
+ providerId: LocalBrowserLinkProviderId;
4
+ configured: boolean;
5
+ flow: "device-code";
6
+ configurationCode?: "client_id_missing";
7
+ }
8
+ export interface LocalBrowserAuthPrompt {
9
+ attemptId: string;
10
+ providerId: LocalBrowserLinkProviderId;
11
+ userCode: string;
12
+ verificationUri: string;
13
+ verificationUriComplete?: string;
14
+ expiresAt: string;
15
+ }
16
+ export interface LocalBrowserAuthToken {
17
+ providerId: LocalBrowserLinkProviderId;
18
+ accessToken: string;
19
+ refreshToken?: string;
20
+ expiresIn?: number;
21
+ refreshTokenExpiresIn?: number;
22
+ clientId: string;
23
+ tokenEndpoint: string;
24
+ tokenType: "Bearer";
25
+ scope?: string;
26
+ }
27
+ interface LinkDeviceOAuthBrokerOptions {
28
+ fetch?: typeof fetch;
29
+ now?: () => number;
30
+ sleep?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
31
+ createId?: () => string;
32
+ clientIds?: Partial<Record<LocalBrowserLinkProviderId, string>>;
33
+ environment?: Record<string, string | undefined>;
34
+ }
35
+ export declare function isLocalBrowserLinkProvider(value: string): value is LocalBrowserLinkProviderId;
36
+ /** Main-process-only broker. Provider tokens never cross the renderer IPC. */
37
+ export declare class LinkDeviceOAuthBroker {
38
+ private readonly fetchFn;
39
+ private readonly now;
40
+ private readonly sleep;
41
+ private readonly createId;
42
+ private readonly clientIds;
43
+ private readonly environment;
44
+ private readonly attempts;
45
+ constructor(options?: LinkDeviceOAuthBrokerOptions);
46
+ status(providerId: LocalBrowserLinkProviderId): LocalBrowserAuthStatus;
47
+ start(providerId: LocalBrowserLinkProviderId, signal?: AbortSignal): Promise<LocalBrowserAuthPrompt>;
48
+ complete(attemptId: string): Promise<LocalBrowserAuthToken>;
49
+ cancel(attemptId: string): boolean;
50
+ private clientId;
51
+ }
52
+ export {};