@oharness/server 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 @@
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AAEH,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAw5CvB,CAAC;AAEF,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgrN5B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG;;;;;yCAKyB,CAAC;AAE1C,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;4CAOwB,kBAAkB,CAAC,OAAO,CAAC;SAC9D,KAAK;EACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmRK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBACvB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;EAC1C,aAAa;EACb,UAAU;EACV,gBAAgB;;;QAGV,CAAC"}
package/dist/pick.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Choosing a file or a directory, from a page that cannot see the filesystem.
3
+ *
4
+ * The browser genuinely cannot help here: `showDirectoryPicker` and
5
+ * `webkitdirectory` both withhold the absolute path by design, and an absolute
6
+ * path is exactly what the server needs to open a project. So the server does
7
+ * the choosing — it is the process that runs on this machine.
8
+ *
9
+ * Two ways, in order: the platform's own folder dialog, which is what someone
10
+ * means by "pick a folder"; and a listing API the page can browse when no
11
+ * dialog is available (a headless host, a remote session, a stripped-down
12
+ * Linux image).
13
+ */
14
+ export interface PickResult {
15
+ path?: string;
16
+ /** Set by the file dialog, which allows more than one. */
17
+ paths?: string[];
18
+ cancelled?: boolean;
19
+ /** Set when this platform has no dialog to offer; the page then browses. */
20
+ unavailable?: boolean;
21
+ /**
22
+ * The dialog failed rather than being dismissed.
23
+ *
24
+ * Worth telling apart: a host that cannot show a dialog at all — a headless
25
+ * session, a login without a window server, an automation permission that
26
+ * was never granted — used to report the same "cancelled" as a user pressing
27
+ * Escape, which left the click looking like it had done nothing.
28
+ */
29
+ error?: string;
30
+ }
31
+ export declare function pickDirectory(start?: string): Promise<PickResult>;
32
+ /**
33
+ * Files and folders, from anywhere on this machine, several at a time.
34
+ *
35
+ * The same argument as above and one more: the browser's own file input hands
36
+ * over bytes and withholds the path, which is right for a pasted screenshot
37
+ * and wrong for a file the agent is about to work on. Uploading a log it could
38
+ * simply open would copy it into the message and lose where it came from.
39
+ *
40
+ * macOS has a panel that takes files and folders at once and AppleScript's
41
+ * `choose file` does not, so this asks AppKit directly and keeps `choose file`
42
+ * as the fallback for a host where that fails. Windows and Linux ship one
43
+ * dialog for each, so a folder there comes from `pickDirectory` instead —
44
+ * which is why the menu asks for files and for folders on separate rows.
45
+ */
46
+ export declare function pickFiles(start?: string): Promise<PickResult>;
47
+ export interface DirEntry {
48
+ name: string;
49
+ path: string;
50
+ }
51
+ /**
52
+ * Subdirectories of one directory, for the in-app browser.
53
+ *
54
+ * Only directories, and only names — this is for navigating to a project, not
55
+ * for reading the disk. Hidden entries are skipped except the ones people
56
+ * actually keep code in.
57
+ */
58
+ export declare function listDirectories(target?: string): Promise<{
59
+ path: string;
60
+ parent?: string;
61
+ entries: DirEntry[];
62
+ }>;
package/dist/pick.js ADDED
@@ -0,0 +1,215 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readdir } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import path from "node:path";
5
+ /** What every one of these says when the answer was "no thanks". */
6
+ const CANCELLED = /-128|user cancell?ed|cancell?ed by user/i;
7
+ /** How long to wait for the user to answer the dialog before giving up. */
8
+ const DIALOG_TIMEOUT_MS = 5 * 60_000;
9
+ export async function pickDirectory(start) {
10
+ const from = start && start.trim() ? start : homedir();
11
+ if (process.platform === "darwin") {
12
+ // `POSIX path of` returns a trailing slash; the caller resolves it away.
13
+ const script = `set d to choose folder with prompt "Choose a project folder" ` +
14
+ `default location POSIX file ${JSON.stringify(from)}\n` +
15
+ `return POSIX path of d`;
16
+ return one(await run("osascript", ["-e", script]));
17
+ }
18
+ if (process.platform === "win32") {
19
+ const script = [
20
+ "Add-Type -AssemblyName System.Windows.Forms",
21
+ "$d = New-Object System.Windows.Forms.FolderBrowserDialog",
22
+ `$d.SelectedPath = ${JSON.stringify(from)}`,
23
+ "if ($d.ShowDialog() -eq 'OK') { $d.SelectedPath }",
24
+ ].join("; ");
25
+ return one(await run("powershell", ["-NoProfile", "-Command", script]));
26
+ }
27
+ // Linux has no one dialog; try the two that are usually installed.
28
+ for (const [command, args] of [
29
+ ["zenity", ["--file-selection", "--directory", `--filename=${from}/`]],
30
+ ["kdialog", ["--getexistingdirectory", from]],
31
+ ]) {
32
+ const result = await run(command, [...args]);
33
+ if (!result.unavailable)
34
+ return one(result);
35
+ }
36
+ return { unavailable: true };
37
+ }
38
+ /**
39
+ * Files and folders, from anywhere on this machine, several at a time.
40
+ *
41
+ * The same argument as above and one more: the browser's own file input hands
42
+ * over bytes and withholds the path, which is right for a pasted screenshot
43
+ * and wrong for a file the agent is about to work on. Uploading a log it could
44
+ * simply open would copy it into the message and lose where it came from.
45
+ *
46
+ * macOS has a panel that takes files and folders at once and AppleScript's
47
+ * `choose file` does not, so this asks AppKit directly and keeps `choose file`
48
+ * as the fallback for a host where that fails. Windows and Linux ship one
49
+ * dialog for each, so a folder there comes from `pickDirectory` instead —
50
+ * which is why the menu asks for files and for folders on separate rows.
51
+ */
52
+ export async function pickFiles(start) {
53
+ const from = start && start.trim() ? start : homedir();
54
+ if (process.platform === "darwin") {
55
+ const panel = await run("osascript", ["-l", "JavaScript", "-e", openPanel(from)]);
56
+ if (!panel.error && !panel.unavailable)
57
+ return lines(panel);
58
+ // `choose file` cannot take a folder, but a machine whose AppKit call just
59
+ // failed is better off with a dialog that opens than with an explanation.
60
+ const script = [
61
+ `set chosen to choose file with prompt "Attach files" ` +
62
+ `default location POSIX file ${JSON.stringify(from)} with multiple selections allowed`,
63
+ `set out to ""`,
64
+ `repeat with f in chosen`,
65
+ `set out to out & POSIX path of f & linefeed`,
66
+ `end repeat`,
67
+ `return out`,
68
+ ].join("\n");
69
+ return lines(await run("osascript", ["-e", script]));
70
+ }
71
+ if (process.platform === "win32") {
72
+ const script = [
73
+ "Add-Type -AssemblyName System.Windows.Forms",
74
+ "$d = New-Object System.Windows.Forms.OpenFileDialog",
75
+ `$d.InitialDirectory = ${JSON.stringify(from)}`,
76
+ "$d.Multiselect = $true",
77
+ "if ($d.ShowDialog() -eq 'OK') { $d.FileNames }",
78
+ ].join("; ");
79
+ return lines(await run("powershell", ["-NoProfile", "-Command", script]));
80
+ }
81
+ for (const [command, args] of [
82
+ ["zenity", ["--file-selection", "--multiple", "--separator=\n", `--filename=${from}/`]],
83
+ ["kdialog", ["--getopenfilename", from]],
84
+ ]) {
85
+ const result = await run(command, [...args]);
86
+ if (!result.unavailable)
87
+ return lines(result);
88
+ }
89
+ return { unavailable: true };
90
+ }
91
+ /**
92
+ * The macOS open panel, in JavaScript for Automation.
93
+ *
94
+ * Written against AppKit rather than in AppleScript because only the panel
95
+ * itself can be told to accept both a file and a folder in one pass. The
96
+ * process running it has no windows of its own, so it asks to be activated —
97
+ * without that the panel opens behind whatever is in front and looks like
98
+ * nothing happened.
99
+ */
100
+ function openPanel(from) {
101
+ return [
102
+ "ObjC.import('AppKit');",
103
+ "var app = $.NSApplication.sharedApplication;",
104
+ "app.setActivationPolicy($.NSApplicationActivationPolicyAccessory);",
105
+ "app.activateIgnoringOtherApps(true);",
106
+ "var panel = $.NSOpenPanel.openPanel;",
107
+ "panel.setCanChooseFiles(true);",
108
+ "panel.setCanChooseDirectories(true);",
109
+ "panel.setAllowsMultipleSelection(true);",
110
+ "panel.setPrompt('Attach');",
111
+ `panel.setDirectoryURL($.NSURL.fileURLWithPath(${JSON.stringify(from)}));`,
112
+ "var picked = panel.runModal === $.NSModalResponseOK ? panel.URLs : null;",
113
+ "var out = [];",
114
+ "if (picked) for (var i = 0; i < picked.count; i++) {",
115
+ " out.push(ObjC.unwrap(picked.objectAtIndex(i).path));",
116
+ "}",
117
+ // An empty answer is how run() hears "cancelled", which is what it was.
118
+ "out.join('\\n');",
119
+ ].join("\n");
120
+ }
121
+ /** One dialog answer that names one thing. */
122
+ function one(result) {
123
+ return result.path ? { path: path.resolve(result.path) } : result;
124
+ }
125
+ /** One dialog answer, which may name several files, split into paths. */
126
+ function lines(result) {
127
+ if (!result.path)
128
+ return result;
129
+ const paths = result.path
130
+ .split("\n")
131
+ .map((line) => line.trim())
132
+ .filter(Boolean)
133
+ .map((line) => path.resolve(line));
134
+ return paths.length ? { paths } : { cancelled: true };
135
+ }
136
+ function run(command, args) {
137
+ return new Promise((resolve) => {
138
+ let child;
139
+ try {
140
+ child = spawn(command, args);
141
+ }
142
+ catch {
143
+ return resolve({ unavailable: true });
144
+ }
145
+ let out = "";
146
+ let problem = "";
147
+ let settled = false;
148
+ const finish = (result) => {
149
+ if (settled)
150
+ return;
151
+ settled = true;
152
+ resolve(result);
153
+ };
154
+ // A dialog nobody answers would otherwise hold the request open forever.
155
+ const timer = setTimeout(() => {
156
+ child.kill();
157
+ finish({ cancelled: true });
158
+ }, DIALOG_TIMEOUT_MS);
159
+ timer.unref?.();
160
+ child.stdout?.on("data", (chunk) => (out += chunk));
161
+ child.stderr?.on("data", (chunk) => (problem += chunk));
162
+ // ENOENT means the tool is not installed, which is not an error worth
163
+ // reporting — it is the signal to try the next one.
164
+ child.on("error", () => {
165
+ clearTimeout(timer);
166
+ finish({ unavailable: true });
167
+ });
168
+ child.on("close", (code) => {
169
+ clearTimeout(timer);
170
+ const chosen = out.trim();
171
+ // A non-zero exit is how every one of these reports "cancelled" — but it
172
+ // is also how they report being unable to open at all, so what they said
173
+ // on the way out decides which of the two this was.
174
+ if (code !== 0 || !chosen) {
175
+ const said = problem.trim();
176
+ return finish(said && !CANCELLED.test(said) ? { error: said } : { cancelled: true });
177
+ }
178
+ // Raw: the file dialog answers with one path per line, and resolving the
179
+ // whole block as a single path would run them together.
180
+ finish({ path: chosen });
181
+ });
182
+ });
183
+ }
184
+ /**
185
+ * Subdirectories of one directory, for the in-app browser.
186
+ *
187
+ * Only directories, and only names — this is for navigating to a project, not
188
+ * for reading the disk. Hidden entries are skipped except the ones people
189
+ * actually keep code in.
190
+ */
191
+ export async function listDirectories(target) {
192
+ const dir = path.resolve(target && target.trim() ? expandHome(target) : homedir());
193
+ const parent = path.dirname(dir);
194
+ let found = [];
195
+ try {
196
+ const items = await readdir(dir, { withFileTypes: true });
197
+ found = items
198
+ .filter((item) => item.isDirectory() && !item.name.startsWith("."))
199
+ .map((item) => ({ name: item.name, path: path.join(dir, item.name) }))
200
+ .sort((a, b) => a.name.localeCompare(b.name));
201
+ }
202
+ catch {
203
+ // An unreadable directory lists as empty rather than failing the request:
204
+ // the user can still navigate back out of it.
205
+ }
206
+ return { path: dir, ...(parent === dir ? {} : { parent }), entries: found };
207
+ }
208
+ function expandHome(dir) {
209
+ if (dir === "~")
210
+ return homedir();
211
+ if (dir.startsWith("~/"))
212
+ return path.join(homedir(), dir.slice(2));
213
+ return dir;
214
+ }
215
+ //# sourceMappingURL=pick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pick.js","sourceRoot":"","sources":["../src/pick.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What the frontend remembers between runs.
3
+ *
4
+ * Only choices the user made by hand. The model picked in the composer is one
5
+ * of them: every new session reverting to the configured default undoes a
6
+ * decision that was deliberate, and re-making it on each session is the kind of
7
+ * chore a tool should absorb.
8
+ *
9
+ * User-scoped rather than per project, like the session store it sits beside —
10
+ * a model is a preference about how to work, not a property of one directory.
11
+ */
12
+ export interface Prefs {
13
+ /** Canonical id of the model last chosen in the picker. */
14
+ model?: string;
15
+ /**
16
+ * Directory of the project last opened by hand.
17
+ *
18
+ * Only switches count: the directory a run happens to start in is where the
19
+ * user already was, not somewhere they chose to go.
20
+ */
21
+ project?: string;
22
+ /**
23
+ * Whether the user has opted in to usage analytics.
24
+ *
25
+ * Absent rather than false by default, and read as "no" — consent that was
26
+ * never given and consent that was withdrawn are the same answer here, and
27
+ * writing `false` at startup would turn a question nobody was asked into a
28
+ * recorded decision.
29
+ */
30
+ analytics?: boolean;
31
+ /**
32
+ * Sessions and projects kept at the top of their list.
33
+ *
34
+ * Here rather than in the session record because a pin is a statement about
35
+ * this sidebar, not about the work: it does not belong in a transcript's
36
+ * metadata, and it should not travel to another machine and rearrange a list
37
+ * somebody there had arranged for themselves.
38
+ */
39
+ pinnedSessions?: string[];
40
+ pinnedProjects?: string[];
41
+ /**
42
+ * Order somebody arranged by hand, by session id within a project and by
43
+ * path across projects. Partial by design: a list holds only what was
44
+ * dragged, and everything else keeps falling in by how recently it was
45
+ * touched. A new session appearing at the top is the behaviour to preserve,
46
+ * not to override with a snapshot of an order taken once.
47
+ */
48
+ sessionOrder?: Record<string, string[]>;
49
+ projectOrder?: string[];
50
+ }
51
+ export declare function readPrefs(): Promise<Prefs>;
52
+ /**
53
+ * Store a preference, leaving the others alone.
54
+ *
55
+ * A patch rather than a replacement because the callers are single-minded: the
56
+ * model picker knows only about the model, and writing the whole file from
57
+ * there would drop the remembered project on the next switch.
58
+ */
59
+ export declare function writePrefs(patch: Prefs): Promise<void>;
package/dist/prefs.js ADDED
@@ -0,0 +1,66 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { userPath } from "@oharness/engine/paths.ts";
4
+ import { parseJson } from "@oharness/engine/util/json.ts";
5
+ /** The lists that are arrays of strings, checked the same way on read. */
6
+ const STRING_LISTS = ["pinnedSessions", "pinnedProjects", "projectOrder"];
7
+ const FILE = () => userPath("prefs.json");
8
+ export async function readPrefs() {
9
+ let raw;
10
+ try {
11
+ raw = await readFile(FILE(), "utf8");
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ const parsed = parseJson(raw);
17
+ if (!parsed.ok || typeof parsed.value !== "object" || parsed.value === null)
18
+ return {};
19
+ const prefs = {};
20
+ if (typeof parsed.value.model === "string" && parsed.value.model)
21
+ prefs.model = parsed.value.model;
22
+ if (typeof parsed.value.project === "string" && parsed.value.project) {
23
+ prefs.project = parsed.value.project;
24
+ }
25
+ if (typeof parsed.value.analytics === "boolean")
26
+ prefs.analytics = parsed.value.analytics;
27
+ for (const key of STRING_LISTS) {
28
+ const value = parsed.value[key];
29
+ if (Array.isArray(value)) {
30
+ prefs[key] = value.filter((entry) => typeof entry === "string");
31
+ }
32
+ }
33
+ const order = parsed.value.sessionOrder;
34
+ if (order && typeof order === "object" && !Array.isArray(order)) {
35
+ const cleaned = {};
36
+ for (const [cwd, ids] of Object.entries(order)) {
37
+ if (Array.isArray(ids)) {
38
+ cleaned[cwd] = ids.filter((id) => typeof id === "string");
39
+ }
40
+ }
41
+ prefs.sessionOrder = cleaned;
42
+ }
43
+ return prefs;
44
+ }
45
+ /**
46
+ * Store a preference, leaving the others alone.
47
+ *
48
+ * A patch rather than a replacement because the callers are single-minded: the
49
+ * model picker knows only about the model, and writing the whole file from
50
+ * there would drop the remembered project on the next switch.
51
+ */
52
+ export async function writePrefs(patch) {
53
+ const file = FILE();
54
+ await mkdir(path.dirname(file), { recursive: true });
55
+ const next = { ...(await readPrefs()), ...patch };
56
+ // Temp plus rename, so an interrupted write cannot leave a truncated file
57
+ // that the next run would silently read as "no preferences". The counter
58
+ // keeps two writes in one process from sharing a temp name, where the first
59
+ // rename moves the file away and the second fails with ENOENT.
60
+ tempSeq += 1;
61
+ const temp = `${file}.${process.pid}.${tempSeq}.tmp`;
62
+ await writeFile(temp, `${JSON.stringify(next, null, 2)}\n`, "utf8");
63
+ await rename(temp, file);
64
+ }
65
+ let tempSeq = 0;
66
+ //# sourceMappingURL=prefs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefs.js","sourceRoot":"","sources":["../src/prefs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The working directories the user has opened.
3
+ *
4
+ * A project is just a directory, but a `Harness` binds to one at construction
5
+ * — so switching projects means holding one harness per directory rather than
6
+ * mutating a field. They are kept alive after a switch: MCP servers and the
7
+ * model catalog are expensive to rebuild, and switching back is common.
8
+ *
9
+ * The list is persisted in the user directory, not the project: a list of
10
+ * projects does not belong to any one of them.
11
+ */
12
+ export interface Project {
13
+ path: string;
14
+ name: string;
15
+ /** Missing directories stay listed so the user can see what broke. */
16
+ missing?: boolean;
17
+ }
18
+ export declare function listProjects(): Promise<Project[]>;
19
+ export declare function addProject(dir: string): Promise<Project>;
20
+ export declare function removeProject(dir: string): Promise<boolean>;
21
+ /** Ensure a directory is listed, used to seed the list with the launch cwd. */
22
+ export declare function ensureProject(dir: string): Promise<void>;
23
+ /**
24
+ * The directory to open at startup.
25
+ *
26
+ * The last project chosen by hand, because a switch is a statement about what
27
+ * the user is working on and having to redo it on every launch is the chore a
28
+ * tool should absorb. The desktop shell has no meaningful working directory of
29
+ * its own, so without this it would always open wherever it was launched from.
30
+ *
31
+ * A remembered directory that has since been moved or deleted falls back
32
+ * rather than building a harness whose every file operation fails.
33
+ */
34
+ export declare function startupProject(fallback: string): Promise<string>;
@@ -0,0 +1,129 @@
1
+ import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { userPath } from "@oharness/engine/paths.ts";
4
+ import { parseJson } from "@oharness/engine/util/json.ts";
5
+ import { readPrefs } from "./prefs.js";
6
+ const FILE = () => userPath("projects.json");
7
+ export async function listProjects() {
8
+ let raw;
9
+ try {
10
+ raw = await readFile(FILE(), "utf8");
11
+ }
12
+ catch {
13
+ return [];
14
+ }
15
+ const parsed = parseJson(raw);
16
+ if (!parsed.ok || !Array.isArray(parsed.value.projects))
17
+ return [];
18
+ const entries = parsed.value.projects.filter((entry) => typeof entry === "string");
19
+ return Promise.all(entries.map(async (dir) => describe(dir)));
20
+ }
21
+ export async function addProject(dir) {
22
+ const resolved = await projectRoot(path.resolve(expandHome(dir)));
23
+ const info = await stat(resolved).catch(() => undefined);
24
+ // Refused rather than stored: a project that is not a directory produces a
25
+ // harness whose every file operation fails, which is a confusing way to
26
+ // learn the path was wrong.
27
+ if (!info)
28
+ throw new Error(`no such directory: ${resolved}`);
29
+ if (!info.isDirectory())
30
+ throw new Error(`not a directory: ${resolved}`);
31
+ const current = await listProjects();
32
+ if (!current.some((project) => project.path === resolved)) {
33
+ await save([...current.map((project) => project.path), resolved]);
34
+ }
35
+ return describe(resolved);
36
+ }
37
+ export async function removeProject(dir) {
38
+ const resolved = path.resolve(dir);
39
+ const current = await listProjects();
40
+ if (!current.some((project) => project.path === resolved))
41
+ return false;
42
+ await save(current.map((p) => p.path).filter((p) => p !== resolved));
43
+ return true;
44
+ }
45
+ /** Ensure a directory is listed, used to seed the list with the launch cwd. */
46
+ export async function ensureProject(dir) {
47
+ const resolved = path.resolve(dir);
48
+ const current = await listProjects();
49
+ if (current.some((project) => project.path === resolved))
50
+ return;
51
+ await save([...current.map((project) => project.path), resolved]);
52
+ }
53
+ /**
54
+ * The directory to open at startup.
55
+ *
56
+ * The last project chosen by hand, because a switch is a statement about what
57
+ * the user is working on and having to redo it on every launch is the chore a
58
+ * tool should absorb. The desktop shell has no meaningful working directory of
59
+ * its own, so without this it would always open wherever it was launched from.
60
+ *
61
+ * A remembered directory that has since been moved or deleted falls back
62
+ * rather than building a harness whose every file operation fails.
63
+ */
64
+ export async function startupProject(fallback) {
65
+ const remembered = (await readPrefs()).project;
66
+ if (!remembered)
67
+ return fallback;
68
+ const info = await stat(remembered).catch(() => undefined);
69
+ return info?.isDirectory() ? remembered : fallback;
70
+ }
71
+ /**
72
+ * The directory a project actually starts at.
73
+ *
74
+ * Opening repo/packages/web means opening repo: the agent needs the whole tree
75
+ * to be useful, and two entries for one repository would split the session
76
+ * list for what the user thinks of as one project. The repository marker is
77
+ * only used to find that boundary — nothing about version control is shown.
78
+ */
79
+ async function projectRoot(dir) {
80
+ let at = dir;
81
+ for (let depth = 0; depth < 40; depth++) {
82
+ if (await exists(path.join(at, ".git")))
83
+ return at;
84
+ const parent = path.dirname(at);
85
+ if (parent === at)
86
+ break;
87
+ at = parent;
88
+ }
89
+ return dir;
90
+ }
91
+ async function exists(target) {
92
+ return stat(target).then(() => true, () => false);
93
+ }
94
+ async function describe(dir) {
95
+ const info = await stat(dir).catch(() => undefined);
96
+ return {
97
+ path: dir,
98
+ name: path.basename(dir) || dir,
99
+ ...(info?.isDirectory() ? {} : { missing: true }),
100
+ };
101
+ }
102
+ function expandHome(dir) {
103
+ if (dir === "~")
104
+ return process.env["HOME"] ?? dir;
105
+ if (dir.startsWith("~/"))
106
+ return path.join(process.env["HOME"] ?? "", dir.slice(2));
107
+ return dir;
108
+ }
109
+ async function save(paths) {
110
+ const file = FILE();
111
+ await mkdir(path.dirname(file), { recursive: true });
112
+ const temp = tempName(file);
113
+ // Temp plus rename, so an interrupted write cannot leave a truncated list.
114
+ await writeFile(temp, `${JSON.stringify({ projects: [...new Set(paths)] }, null, 2)}\n`, "utf8");
115
+ await rename(temp, file);
116
+ }
117
+ /**
118
+ * A unique name for each in-flight write.
119
+ *
120
+ * `file.<pid>.tmp` collides when one process writes twice at once: the first
121
+ * rename moves the shared temp file away and the second fails with ENOENT.
122
+ * The counter makes concurrent writers independent.
123
+ */
124
+ let tempSeq = 0;
125
+ function tempName(file) {
126
+ tempSeq += 1;
127
+ return `${file}.${process.pid}.${tempSeq}.tmp`;
128
+ }
129
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"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"}