@xingjisoft/neutrino 0.6.2 → 0.7.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.
Files changed (166) hide show
  1. package/README.md +6 -1
  2. package/assets/fast-planner.txt +57 -0
  3. package/assets/fast-replanner.txt +28 -0
  4. package/dist/agents.d.ts +25 -0
  5. package/dist/app.d.ts +206 -0
  6. package/dist/app.js +15 -0
  7. package/dist/background.d.ts +96 -0
  8. package/dist/bin.d.ts +2 -0
  9. package/dist/bin.js +40 -3
  10. package/dist/browser.d.ts +18 -0
  11. package/dist/clipboard.d.ts +16 -0
  12. package/dist/colors.d.ts +29 -0
  13. package/dist/compact.d.ts +52 -0
  14. package/dist/config.d.ts +221 -0
  15. package/dist/config.js +19 -7
  16. package/dist/cwd.d.ts +19 -0
  17. package/dist/fast/artifacts.d.ts +26 -0
  18. package/dist/fast/artifacts.js +109 -0
  19. package/dist/fast/engine.d.ts +78 -0
  20. package/dist/fast/engine.js +657 -0
  21. package/dist/fast/evidence.d.ts +21 -0
  22. package/dist/fast/evidence.js +68 -0
  23. package/dist/fast/planner.d.ts +10 -0
  24. package/dist/fast/planner.js +51 -0
  25. package/dist/fast/pool.d.ts +17 -0
  26. package/dist/fast/pool.js +78 -0
  27. package/dist/fast/scheduler.d.ts +54 -0
  28. package/dist/fast/scheduler.js +383 -0
  29. package/dist/fast/types.d.ts +100 -0
  30. package/dist/fast/types.js +21 -0
  31. package/dist/fast/worker.d.ts +23 -0
  32. package/dist/fast/worker.js +185 -0
  33. package/dist/header.d.ts +3 -0
  34. package/dist/index.d.ts +5 -0
  35. package/dist/llm/approval.d.ts +204 -0
  36. package/dist/llm/approval.js +14 -3
  37. package/dist/llm/client.d.ts +118 -0
  38. package/dist/llm/client.js +57 -8
  39. package/dist/llm/codeExecutor.d.ts +14 -0
  40. package/dist/llm/codemode.d.ts +15 -0
  41. package/dist/llm/context.d.ts +29 -0
  42. package/dist/llm/diagnostics.d.ts +91 -0
  43. package/dist/llm/diagnostics.js +242 -0
  44. package/dist/llm/plan.d.ts +148 -0
  45. package/dist/llm/retry.d.ts +80 -0
  46. package/dist/llm/risk.d.ts +21 -0
  47. package/dist/llm/risk.js +7 -0
  48. package/dist/llm/runtime.d.ts +189 -0
  49. package/dist/llm/runtime.js +77 -4
  50. package/dist/llm/search.d.ts +2 -0
  51. package/dist/llm/searchNetwork.d.ts +8 -0
  52. package/dist/llm/subagent.d.ts +112 -0
  53. package/dist/llm/subagent.js +4 -2
  54. package/dist/llm/tools.d.ts +20 -0
  55. package/dist/llm/tools.js +4 -1
  56. package/dist/llm/workspace.d.ts +6 -0
  57. package/dist/llm/workspace.js +271 -0
  58. package/dist/markdown.d.ts +58 -0
  59. package/dist/migration.d.ts +8 -0
  60. package/dist/persistence.d.ts +14 -0
  61. package/dist/plugins/catalog.d.ts +20 -0
  62. package/dist/plugins/catalog.js +45 -0
  63. package/dist/plugins/cli.d.ts +1 -0
  64. package/dist/plugins/cli.js +106 -0
  65. package/dist/plugins/host.d.ts +121 -0
  66. package/dist/plugins/host.js +450 -0
  67. package/dist/plugins/manager.d.ts +24 -0
  68. package/dist/plugins/manager.js +288 -0
  69. package/dist/plugins/package.d.ts +5 -0
  70. package/dist/plugins/package.js +53 -0
  71. package/dist/plugins/runner.d.ts +1 -0
  72. package/dist/plugins/runner.js +227 -0
  73. package/dist/plugins/session.d.ts +12 -0
  74. package/dist/plugins/session.js +97 -0
  75. package/dist/plugins/terminal.d.ts +22 -0
  76. package/dist/plugins/terminal.js +33 -0
  77. package/dist/plugins/types.d.ts +178 -0
  78. package/dist/plugins/types.js +6 -0
  79. package/dist/plugins/validation.d.ts +14 -0
  80. package/dist/plugins/validation.js +174 -0
  81. package/dist/plugins/web-sdk.d.ts +58 -0
  82. package/dist/plugins/web-sdk.js +1 -0
  83. package/dist/pro/agents.d.ts +34 -0
  84. package/dist/pro/config.d.ts +75 -0
  85. package/dist/pro/config.js +3 -2
  86. package/dist/pro/preflight.d.ts +21 -0
  87. package/dist/sanitize.d.ts +34 -0
  88. package/dist/screens/agentConversation.d.ts +28 -0
  89. package/dist/screens/agentHistory.d.ts +25 -0
  90. package/dist/screens/agentSelect.d.ts +16 -0
  91. package/dist/screens/chat.d.ts +292 -0
  92. package/dist/screens/chat.js +139 -21
  93. package/dist/screens/history.d.ts +23 -0
  94. package/dist/screens/modelSelect.d.ts +29 -0
  95. package/dist/screens/plugins.d.ts +29 -0
  96. package/dist/screens/plugins.js +196 -0
  97. package/dist/screens/settings.d.ts +42 -0
  98. package/dist/screens/start.d.ts +43 -0
  99. package/dist/screens/start.js +25 -17
  100. package/dist/screens/updatePrompt.d.ts +41 -0
  101. package/dist/selection.d.ts +75 -0
  102. package/dist/state.d.ts +26 -0
  103. package/dist/terminal/buffer.d.ts +47 -0
  104. package/dist/terminal/input.d.ts +70 -0
  105. package/dist/terminal/renderer.d.ts +43 -0
  106. package/dist/terminal/restore.d.ts +41 -0
  107. package/dist/terminal/screen.d.ts +57 -0
  108. package/dist/tray.d.ts +14 -0
  109. package/dist/ui.d.ts +123 -0
  110. package/dist/update.d.ts +71 -0
  111. package/dist/updateWatch.d.ts +43 -0
  112. package/dist/web/approvalQueue.d.ts +8 -0
  113. package/dist/web/approvalQueue.js +59 -0
  114. package/dist/web/auth.d.ts +14 -0
  115. package/dist/web/clientState.d.ts +16 -0
  116. package/dist/web/folderPicker.d.ts +34 -0
  117. package/dist/web/folderPicker.js +544 -0
  118. package/dist/web/hub.d.ts +213 -0
  119. package/dist/web/hub.js +14 -2
  120. package/dist/web/localApi.d.ts +11 -0
  121. package/dist/web/localApi.js +260 -0
  122. package/dist/web/plugins.d.ts +8 -0
  123. package/dist/web/plugins.js +64 -0
  124. package/dist/web/server.d.ts +81 -0
  125. package/dist/web/server.js +127 -19
  126. package/dist/web/service.d.ts +7 -0
  127. package/dist/web/service.js +1 -0
  128. package/dist/web/session.d.ts +176 -0
  129. package/dist/web/session.js +153 -19
  130. package/dist/web/settings.d.ts +65 -0
  131. package/dist/web/terminalBridge.d.ts +15 -0
  132. package/dist/web/terminalBridge.js +8 -2
  133. package/dist/width.d.ts +6 -0
  134. package/docs/plugins/README.md +126 -0
  135. package/docs/plugins/agent-sdk.md +92 -0
  136. package/docs/plugins/architecture.md +64 -0
  137. package/docs/plugins/compatibility.md +34 -0
  138. package/docs/plugins/development.md +55 -0
  139. package/docs/plugins/manifest.md +59 -0
  140. package/docs/plugins/manifest.schema.json +286 -0
  141. package/docs/plugins/security.md +53 -0
  142. package/docs/plugins/skills-prompts.md +69 -0
  143. package/docs/plugins/ui-sdk.md +109 -0
  144. package/examples/local-api.py +105 -0
  145. package/examples/plugins/cards/card.html +6 -0
  146. package/examples/plugins/cards/neutrino.plugin.json +42 -0
  147. package/examples/plugins/composer/editor.html +10 -0
  148. package/examples/plugins/composer/neutrino.plugin.json +10 -0
  149. package/examples/plugins/function-tools/agent.js +32 -0
  150. package/examples/plugins/function-tools/neutrino.plugin.json +15 -0
  151. package/examples/plugins/menus-tabs/neutrino.plugin.json +15 -0
  152. package/examples/plugins/menus-tabs/panel.html +6 -0
  153. package/examples/plugins/skills-prompts/neutrino.plugin.json +13 -0
  154. package/examples/plugins/skills-prompts/prompts/evidence.md +1 -0
  155. package/examples/plugins/skills-prompts/prompts/reviewer.md +1 -0
  156. package/examples/plugins/skills-prompts/skills/review/SKILL.md +8 -0
  157. package/examples/plugins/theme/neutrino.plugin.json +12 -0
  158. package/package.json +66 -42
  159. package/web/LOCAL-API.md +65 -0
  160. package/web/README.md +27 -6
  161. package/web/app.js +201 -32
  162. package/web/i18n.js +15 -0
  163. package/web/index.html +3 -1
  164. package/web/plugins.css +10 -0
  165. package/web/plugins.js +230 -0
  166. package/web/style.css +11 -2
package/dist/ui.d.ts ADDED
@@ -0,0 +1,123 @@
1
+ import type { Buffer } from "./terminal/buffer.js";
2
+ import { type RGB } from "./colors.js";
3
+ import type { ApprovalMode } from "./config.js";
4
+ /** One terminal row; shared geometry keeps rendering and hit testing aligned. */
5
+ export declare function approvalToggle(width: number, y: number, mode: ApprovalMode): {
6
+ x: number;
7
+ y: number;
8
+ w: number;
9
+ h: number;
10
+ text: string;
11
+ };
12
+ export declare function drawApprovalToggle(buf: Buffer, y: number, mode: ApprovalMode, focused?: boolean): void;
13
+ export declare function wrapText(text: string, width: number): string[];
14
+ export interface InputBoxOptions {
15
+ active?: boolean;
16
+ dimmed?: boolean;
17
+ placeholder?: string;
18
+ /** Blink phase of the text cursor. When false the cursor is hidden (the
19
+ * character underneath is shown normally). */
20
+ cursorOn?: boolean;
21
+ }
22
+ export interface InputWindow {
23
+ /** Index (in UTF-16 code points) of the first visible character. */
24
+ startCp: number;
25
+ /** Display columns before the visible window. */
26
+ prefixW: number;
27
+ /** Number of visible characters. */
28
+ visibleCount: number;
29
+ }
30
+ /**
31
+ * Compute which part of the input value is visible inside the box, given the
32
+ * cursor position. Shared by `drawInputBox` and mouse click handling so the
33
+ * rendered text and click-to-position-cursor always agree.
34
+ */
35
+ export declare function computeInputWindow(value: string, cursorPos: number, innerW: number): InputWindow;
36
+ /**
37
+ * Map a column inside the input box (0-based, relative to the inner area) to a
38
+ * cursor index in the full value.
39
+ */
40
+ export declare function cursorFromColumn(value: string, col: number, win: InputWindow): number;
41
+ export interface Rect {
42
+ x: number;
43
+ y: number;
44
+ w: number;
45
+ h: number;
46
+ }
47
+ export declare function inRect(px: number, py: number, r: Rect): boolean;
48
+ export declare function drawHint(buf: Buffer, y: number, text: string): void;
49
+ export interface ScrollbarOptions {
50
+ /** Colour of the groove. Defaults to `C.scrollTrack`. */
51
+ track?: RGB;
52
+ /** Colour of the thumb. Defaults to `C.scrollThumb`. */
53
+ thumb?: RGB;
54
+ }
55
+ /**
56
+ * Draw a vertical scrollbar in column `x`, spanning rows `top`..`bottom`
57
+ * (inclusive). It is a no-op when everything fits, so callers can render it
58
+ * unconditionally.
59
+ *
60
+ * total - total number of scrollable items/lines
61
+ * viewport - how many of them are visible at once
62
+ * offset - index of the topmost visible item (i.e. the current scrollTop)
63
+ *
64
+ * The thumb length is proportional to `viewport / total` (at least one cell)
65
+ * and its position is proportional to `offset / (total - viewport)`, matching
66
+ * the usual GUI scrollbar behaviour.
67
+ */
68
+ export interface ScrollbarGeometry {
69
+ /** Height of the groove in cells. */
70
+ trackH: number;
71
+ /** Height of the thumb in cells (at least 1). */
72
+ thumbH: number;
73
+ /** Largest allowed top row of the thumb, relative to the groove. */
74
+ maxThumbTop: number;
75
+ /** Largest valid scroll offset (total - viewport). */
76
+ maxOffset: number;
77
+ /** Current top row of the thumb, relative to the groove. */
78
+ thumbTop: number;
79
+ }
80
+ /**
81
+ * Geometry of a vertical scrollbar, shared by the drawing code and the app's
82
+ * hit-testing / dragging so the two can never disagree.
83
+ */
84
+ export declare function scrollbarGeometry(top: number, bottom: number, total: number, viewport: number, offset: number): ScrollbarGeometry;
85
+ /**
86
+ * Inverse of the thumb positioning above: the scroll offset that places the
87
+ * thumb's top at 'thumbTop'. Used while dragging the thumb; feeding the result
88
+ * back through scrollbarGeometry reproduces the same 'thumbTop'.
89
+ */
90
+ export declare function offsetFromThumbTop(g: ScrollbarGeometry, thumbTop: number): number;
91
+ export declare function drawScrollbar(buf: Buffer, x: number, top: number, bottom: number, total: number, viewport: number, offset: number, opts?: ScrollbarOptions): void;
92
+ export declare function drawInputBox(buf: Buffer, x: number, y: number, w: number, value: string, cursorPos: number, opts?: InputBoxOptions): void;
93
+ export declare function drawTabButton(buf: Buffer, x: number, y: number, w: number, label: string, selected: boolean, align?: "center" | "left"): void;
94
+ /** Label of the CLI entry point that opens the browser console's Pro workspace. */
95
+ export declare const PRO_MODE_LABEL = "\u542F\u7528Pro\u6A21\u5F0F";
96
+ /** Label of the CLI entry point that opens the browser console itself. */
97
+ export declare const WEB_MODE_LABEL = "\u6253\u5F00Web\u7AEF";
98
+ /** One of the small entry-point boxes painted in the top-left corner. */
99
+ export interface HeaderButton {
100
+ x: number;
101
+ y: number;
102
+ w: number;
103
+ h: number;
104
+ label: string;
105
+ }
106
+ /**
107
+ * The Pro-mode button: flush to the left edge with a single blank column, as
108
+ * the start screen expects.
109
+ */
110
+ export declare function proModeButton(width: number, height: number): HeaderButton | null;
111
+ /**
112
+ * The Web-mode button: glued to the right border of the Pro-mode button so the
113
+ * two read as one row of entry points. It starts where the Pro box ends, which
114
+ * also means it is hidden together with it on terminals too small for the row.
115
+ */
116
+ export declare function webModeButton(width: number, height: number): HeaderButton | null;
117
+ /**
118
+ * Draw one top-left button. It is painted as opaque chrome so content behind it
119
+ * can never bleed through; the label row turns into the highlight background
120
+ * while the pointer is over it.
121
+ */
122
+ export declare function drawHeaderButton(buf: Buffer, button: HeaderButton | null, hovered?: boolean): void;
123
+ export declare function centerVertically(totalHeight: number, contentHeight: number): number;
@@ -0,0 +1,71 @@
1
+ /** The published package whose registry version decides whether an update exists. */
2
+ export declare const UPDATE_PACKAGE = "@xingjisoft/neutrino";
3
+ /** What a user can run by hand when the automatic update fails. */
4
+ export declare const MANUAL_UPDATE_COMMAND = "npm install -g @xingjisoft/neutrino";
5
+ export interface UpdateStatus {
6
+ /** The version this process runs, read from its own package.json. */
7
+ currentVersion: string;
8
+ /** The published version; empty until a registry lookup has succeeded. */
9
+ latestVersion: string;
10
+ /** True only when the registry offers a strictly newer version. */
11
+ hasUpdate: boolean;
12
+ }
13
+ export interface UpdateCheck extends UpdateStatus {
14
+ /** Why the registry could not be read; the status then reports no update. */
15
+ error?: string;
16
+ }
17
+ export interface UpdateInstallResult {
18
+ /** Whether npm reported success. */
19
+ ok: boolean;
20
+ /** The version this run was updating to; empty when none was known. */
21
+ version: string;
22
+ /** Why the install failed; empty on success. */
23
+ error?: string;
24
+ }
25
+ export interface UpdateCheckOptions {
26
+ /**
27
+ * Ask the registry again even though an answer is already remembered. The
28
+ * settings page's manual check and the periodic background check need a
29
+ * fresh answer; waiting for the next start-up would tell them nothing new.
30
+ */
31
+ refresh?: boolean;
32
+ }
33
+ /**
34
+ * The last known answer, for the terminal screen and the Web console payload.
35
+ * Reading this never touches the network: the lookup runs once per process (see
36
+ * checkForUpdate) and costs nothing afterwards.
37
+ */
38
+ export declare function updateStatus(): UpdateStatus;
39
+ /**
40
+ * Ask npm which version is published and remember it. This never rejects: an
41
+ * offline machine, a missing npm or a registry hiccup only means "no update to
42
+ * announce", which is what the next start is for. Concurrent callers share the
43
+ * one lookup, and a successful answer is kept for the rest of the process so
44
+ * the terminal and the Web console cannot disagree.
45
+ *
46
+ * Start-up asks once and is served from that answer from then on. A caller that
47
+ * passes `refresh` — the settings button, the background timer — queries the
48
+ * registry again, so an answer from an hour ago cannot make a check look idle.
49
+ * A failed refresh changes nothing: the last good answer stays, and the caller
50
+ * learns what went wrong from `error`.
51
+ */
52
+ export declare function checkForUpdate(options?: UpdateCheckOptions): Promise<UpdateCheck>;
53
+ /**
54
+ * Install the latest published release with npm.
55
+ *
56
+ * The terminal and both Web workspaces can ask for this, so requests share one
57
+ * install instead of running npm twice over the same files. It never rejects:
58
+ * whoever asked gets a result and, on failure, the command to run by hand.
59
+ *
60
+ * The version this process runs is not replaced — the running code stays on
61
+ * disk until the user restarts — so callers must tell the user to restart.
62
+ */
63
+ export declare function installUpdate(): Promise<UpdateInstallResult>;
64
+ /**
65
+ * Semantic-version order: returns 1 when `left` is newer, -1 when older and 0
66
+ * when they are the same release. A version that cannot be parsed compares as
67
+ * equal, so a malformed registry answer can never claim an update is due.
68
+ */
69
+ export declare function compareVersions(left: string, right: string): number;
70
+ /** Whether `candidate` is a strictly newer release than `current`. */
71
+ export declare function isNewerVersion(candidate: string, current: string): boolean;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The periodic update check.
3
+ *
4
+ * A Web console can stay open for days, so the start-up lookup alone would
5
+ * never tell its windows about a release published afterwards. This module asks
6
+ * the registry again on a timer, in the process that serves those windows, and
7
+ * hands a release it has not seen before to whoever is listening: the console
8
+ * forwards it to every open browser (GET /api/update/events) and the first-open
9
+ * dialog reads the same answer.
10
+ */
11
+ import { type UpdateCheck } from "./update.js";
12
+ /** How often the background service asks the registry again. */
13
+ export declare const UPDATE_CHECK_INTERVAL_MS: number;
14
+ export interface UpdateWatchOptions {
15
+ /** Overridable so a test can watch a timer; the console never changes it. */
16
+ intervalMs?: number;
17
+ /**
18
+ * Called after every check, including the ones that found nothing or failed,
19
+ * so the background log can record a change. Never allowed to stop the timer.
20
+ */
21
+ onCheck?: (check: UpdateCheck) => void;
22
+ }
23
+ type Listener = (check: UpdateCheck) => void;
24
+ /**
25
+ * Listen for a release the periodic check finds. Returns the unsubscribe
26
+ * function.
27
+ *
28
+ * Only news is announced: a version this process already knows — from the
29
+ * start-up lookup, the settings button or an earlier tick — is not sent again,
30
+ * so the dialogs cannot reopen every half hour for a release the user has
31
+ * already declined.
32
+ */
33
+ export declare function subscribeUpdateAnnouncements(listener: Listener): () => void;
34
+ /**
35
+ * Start asking the registry every `intervalMs` (30 minutes by default).
36
+ *
37
+ * Idempotent, and the returned function stops the timer and leaves the module
38
+ * ready to start again. Checks run off the timer rather than at start-up: both
39
+ * the console and the terminal UI already look once when they start, and a
40
+ * second immediate lookup would only spend npm calls on the same answer.
41
+ */
42
+ export declare function startUpdateWatch(options?: UpdateWatchOptions): () => void;
43
+ export {};
@@ -0,0 +1,8 @@
1
+ /** Serialize human prompts without overwriting an unresolved decision. */
2
+ export declare class ApprovalQueue {
3
+ private active;
4
+ private waiting;
5
+ run(start: () => Promise<boolean | null>, signal?: AbortSignal): Promise<boolean | null>;
6
+ cancelWaiting(): void;
7
+ private next;
8
+ }
@@ -0,0 +1,59 @@
1
+ /** Serialize human prompts without overwriting an unresolved decision. */
2
+ export class ApprovalQueue {
3
+ active = false;
4
+ waiting = [];
5
+ run(start, signal) {
6
+ return new Promise((resolve, reject) => {
7
+ if (signal?.aborted) {
8
+ resolve(false);
9
+ return;
10
+ }
11
+ const item = {
12
+ cancel: () => {
13
+ const index = this.waiting.indexOf(item);
14
+ if (index >= 0) {
15
+ this.waiting.splice(index, 1);
16
+ signal?.removeEventListener("abort", item.cancel);
17
+ resolve(false);
18
+ }
19
+ },
20
+ run: () => {
21
+ signal?.removeEventListener("abort", item.cancel);
22
+ if (signal?.aborted) {
23
+ this.next();
24
+ resolve(false);
25
+ return;
26
+ }
27
+ this.active = true;
28
+ let result;
29
+ try {
30
+ result = start();
31
+ }
32
+ catch (error) {
33
+ this.next();
34
+ reject(error);
35
+ return;
36
+ }
37
+ result.then(value => { this.next(); resolve(value); }, error => { this.next(); reject(error); });
38
+ },
39
+ };
40
+ if (this.active) {
41
+ this.waiting.push(item);
42
+ signal?.addEventListener("abort", item.cancel, { once: true });
43
+ }
44
+ else
45
+ item.run();
46
+ });
47
+ }
48
+ cancelWaiting() {
49
+ for (const item of [...this.waiting])
50
+ item.cancel();
51
+ }
52
+ next() {
53
+ const next = this.waiting.shift();
54
+ if (next)
55
+ next.run();
56
+ else
57
+ this.active = false;
58
+ }
59
+ }
@@ -0,0 +1,14 @@
1
+ import type http from "node:http";
2
+ export declare const DEFAULT_WEB_PASSWORD = "114514";
3
+ export declare function validateWebPassword(password: unknown): asserts password is string;
4
+ /** Only a salted password hash is persisted; settings never return it. */
5
+ export declare function hashWebPassword(password: unknown): string;
6
+ /** Signed cookies survive restarts, but a password change invalidates all of them. */
7
+ export declare class WebAuth {
8
+ readonly enabled: boolean;
9
+ constructor(enabled: boolean);
10
+ authenticated(req: http.IncomingMessage): boolean;
11
+ login(password: unknown): Promise<boolean>;
12
+ remember(res: http.ServerResponse): void;
13
+ forget(res: http.ServerResponse): void;
14
+ }
@@ -0,0 +1,16 @@
1
+ import { type Mode } from "../config.js";
2
+ export declare const WEB_STATE_FILE: string;
3
+ /** How many finished conversations one workspace remembers, newest last. */
4
+ export declare const SEEN_LIMIT = 300;
5
+ /** Whether a value from a request is an identity this file can hold. */
6
+ export declare function isSeenIdentity(value: unknown): value is string;
7
+ /** The conversations this workspace has already shown the reader, oldest first. */
8
+ export declare function loadSeen(mode?: Mode): string[];
9
+ /**
10
+ * Record (`seen: true`) or forget (`seen: false`) the given conversations and
11
+ * write the file back. Returns the workspace's new record.
12
+ *
13
+ * Identities that are not valid are ignored rather than rejected: the route
14
+ * already validated them, so this is only a guard on what reaches the disk.
15
+ */
16
+ export declare function updateSeen(mode: Mode, identities: string[], seen: boolean): string[];
@@ -0,0 +1,34 @@
1
+ /** What the folder window reported. Closing the window is a normal outcome. */
2
+ export type FolderPick = {
3
+ picked: string;
4
+ } | {
5
+ cancelled: true;
6
+ } | {
7
+ unavailable: string;
8
+ };
9
+ /** One finished dialog process. */
10
+ export interface DialogRun {
11
+ /** Exit code, or null when the process never ran to completion. */
12
+ code: number | null;
13
+ stdout: string;
14
+ stderr: string;
15
+ /** The executable itself does not exist here. */
16
+ missing: boolean;
17
+ }
18
+ /**
19
+ * Runs one dialog command. Injectable so the Windows, macOS and Linux paths can
20
+ * be tested on any machine, without a desktop session to draw a window in.
21
+ */
22
+ export type DialogRunner = (command: string, args: string[], env: NodeJS.ProcessEnv, timeoutMs: number) => Promise<DialogRun>;
23
+ export interface FolderPickRequest {
24
+ /** Folder the window opens in. Ignored when it is not an existing folder. */
25
+ start?: string;
26
+ /** Window title, already translated by the console. */
27
+ title?: string;
28
+ platform?: NodeJS.Platform;
29
+ timeoutMs?: number;
30
+ runner?: DialogRunner;
31
+ env?: NodeJS.ProcessEnv;
32
+ }
33
+ /** Open the operating system's folder window and report what the user chose. */
34
+ export declare function pickFolder(request?: FolderPickRequest): Promise<FolderPick>;