@kmugalkhod/lightcode 0.14.0 → 0.16.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.
- package/README.md +16 -5
- package/assets/web/index-wma2v20c.css +1 -0
- package/assets/web/index-wzb9vzdt.js +149 -0
- package/assets/web/index.html +1 -1
- package/cli.js +27 -5
- package/package.json +1 -1
- package/server.js +463 -63
- package/assets/web/index-2dshmdgm.css +0 -1
- package/assets/web/index-x9zna9dg.js +0 -325
package/server.js
CHANGED
|
@@ -428,6 +428,9 @@ import {
|
|
|
428
428
|
} from "fs";
|
|
429
429
|
import path4 from "path";
|
|
430
430
|
import { z } from "zod";
|
|
431
|
+
function isAuthenticatedWebRequest(request) {
|
|
432
|
+
return authenticatedWebRequests.has(request);
|
|
433
|
+
}
|
|
431
434
|
function defaultExpectedOrigin(env = Bun.env) {
|
|
432
435
|
const configuredPort = env.PORT?.trim() || "4983";
|
|
433
436
|
return new URL(`http://127.0.0.1:${configuredPort}`).origin;
|
|
@@ -603,6 +606,9 @@ function createWebSecurityMiddleware(options = {}) {
|
|
|
603
606
|
if (!hasValidBearerToken(request, authState.token)) {
|
|
604
607
|
return jsonSecurityError(c, 401, "web_auth_required", "A valid Lightcode browser token is required.");
|
|
605
608
|
}
|
|
609
|
+
if (browserRequest) {
|
|
610
|
+
authenticatedWebRequests.add(request);
|
|
611
|
+
}
|
|
606
612
|
} else if (browserRequest || authState.configured) {
|
|
607
613
|
return jsonSecurityError(c, 503, "web_auth_unavailable", "Browser access is unavailable. Start Lightcode with `lightcode web`.");
|
|
608
614
|
} else if (!allowUnauthenticatedCli) {
|
|
@@ -615,7 +621,7 @@ function createWebSecurityMiddleware(options = {}) {
|
|
|
615
621
|
await next();
|
|
616
622
|
};
|
|
617
623
|
}
|
|
618
|
-
var webAuthFileEnvironmentVariable = "LIGHTCODE_WEB_AUTH_FILE", webAuthHandoffSchema, maximumHandoffBytes = 4096, maximumHandoffAgeMs, maximumHandoffClockSkewMs, defaultAuthState;
|
|
624
|
+
var webAuthFileEnvironmentVariable = "LIGHTCODE_WEB_AUTH_FILE", webAuthHandoffSchema, maximumHandoffBytes = 4096, maximumHandoffAgeMs, maximumHandoffClockSkewMs, authenticatedWebRequests, defaultAuthState;
|
|
619
625
|
var init_web_auth = __esm(() => {
|
|
620
626
|
webAuthHandoffSchema = z.object({
|
|
621
627
|
version: z.literal(1),
|
|
@@ -624,6 +630,7 @@ var init_web_auth = __esm(() => {
|
|
|
624
630
|
}).strict();
|
|
625
631
|
maximumHandoffAgeMs = 5 * 60 * 1000;
|
|
626
632
|
maximumHandoffClockSkewMs = 30 * 1000;
|
|
633
|
+
authenticatedWebRequests = new WeakSet;
|
|
627
634
|
defaultAuthState = loadWebAuthStateFromEnvironment();
|
|
628
635
|
});
|
|
629
636
|
|
|
@@ -9522,7 +9529,7 @@ var init_runtime16 = __esm(() => {
|
|
|
9522
9529
|
|
|
9523
9530
|
// packages/ai/src/workspace-browser.ts
|
|
9524
9531
|
import { z as z38 } from "zod";
|
|
9525
|
-
var workspaceLocationIdSchema, workspaceLocationStateSchema, workspaceLocationSchema, workspaceLocationsResponseSchema, workspaceBrowserOpenRequestSchema, workspaceBrowserIdSchema, workspaceBrowserOpenResponseSchema, workspacePathSegmentSchema, workspacePathSegmentsSchema, workspaceBrowserCursorSchema, workspaceBrowserEntriesRequestSchema, workspaceBrowserEntryKindSchema, workspaceBrowserSymlinkStateSchema, workspaceBrowserEntrySchema, workspaceBrowserEntriesResponseSchema, workspaceBrowserSelectRequestSchema, workspaceGrantIdSchema, workspaceGrantSchema, workspaceBrowserSelectResponseSchema, workspaceSessionPathParamsSchema, workspaceSessionCreateRequestSchema, workspaceBrowserPathParamsSchema, workspaceApiErrorCodeSchema, workspaceApiErrorResponseSchema;
|
|
9532
|
+
var workspaceLocationIdSchema, workspaceLocationStateSchema, workspaceLocationSchema, workspaceLocationsResponseSchema, workspaceBrowserOpenRequestSchema, workspaceBrowserIdSchema, workspaceBrowserOpenResponseSchema, workspacePathSegmentSchema, workspacePathSegmentsSchema, workspaceBrowserCursorSchema, workspaceBrowserEntriesRequestSchema, workspaceBrowserEntryKindSchema, workspaceBrowserSymlinkStateSchema, workspaceBrowserEntrySchema, workspaceBrowserEntriesResponseSchema, workspaceBrowserSelectRequestSchema, workspaceGrantIdSchema, workspaceGrantSchema, workspaceNativePickerRequestSchema, workspaceNativePickerResponseSchema, workspaceBrowserSelectResponseSchema, workspaceSessionPathParamsSchema, workspaceSessionCreateRequestSchema, workspaceBrowserPathParamsSchema, workspaceApiErrorCodeSchema, workspaceApiErrorResponseSchema;
|
|
9526
9533
|
var init_workspace_browser = __esm(() => {
|
|
9527
9534
|
init_coding_agent_modes();
|
|
9528
9535
|
init_permissions();
|
|
@@ -9604,6 +9611,16 @@ var init_workspace_browser = __esm(() => {
|
|
|
9604
9611
|
pathLabel: z38.string().min(1).max(4096),
|
|
9605
9612
|
createdAt: z38.string().min(1)
|
|
9606
9613
|
}).strict();
|
|
9614
|
+
workspaceNativePickerRequestSchema = z38.object({}).strict();
|
|
9615
|
+
workspaceNativePickerResponseSchema = z38.discriminatedUnion("outcome", [
|
|
9616
|
+
z38.object({
|
|
9617
|
+
outcome: z38.literal("selected"),
|
|
9618
|
+
workspace: workspaceGrantSchema
|
|
9619
|
+
}).strict(),
|
|
9620
|
+
z38.object({
|
|
9621
|
+
outcome: z38.literal("cancelled")
|
|
9622
|
+
}).strict()
|
|
9623
|
+
]);
|
|
9607
9624
|
workspaceBrowserSelectResponseSchema = z38.object({
|
|
9608
9625
|
workspace: workspaceGrantSchema
|
|
9609
9626
|
}).strict();
|
|
@@ -9631,7 +9648,10 @@ var init_workspace_browser = __esm(() => {
|
|
|
9631
9648
|
"browser_capability_expired",
|
|
9632
9649
|
"workspace_grant_not_found",
|
|
9633
9650
|
"invalid_cursor",
|
|
9634
|
-
"workspace_unavailable"
|
|
9651
|
+
"workspace_unavailable",
|
|
9652
|
+
"native_picker_busy",
|
|
9653
|
+
"native_picker_unavailable",
|
|
9654
|
+
"native_picker_failed"
|
|
9635
9655
|
]);
|
|
9636
9656
|
workspaceApiErrorResponseSchema = z38.object({
|
|
9637
9657
|
error: z38.string().min(1),
|
|
@@ -16799,6 +16819,320 @@ var init_web_routes = __esm(() => {
|
|
|
16799
16819
|
}).get("/app/*", serveWebAsset);
|
|
16800
16820
|
});
|
|
16801
16821
|
|
|
16822
|
+
// apps/server/src/lib/native-directory-picker.ts
|
|
16823
|
+
import path24 from "path";
|
|
16824
|
+
import { accessSync, constants as fsConstants2 } from "fs";
|
|
16825
|
+
import { homedir as homedir4 } from "os";
|
|
16826
|
+
async function readBoundedStream(stream, maximumBytes) {
|
|
16827
|
+
const reader = stream.getReader();
|
|
16828
|
+
const chunks = [];
|
|
16829
|
+
let totalBytes = 0;
|
|
16830
|
+
try {
|
|
16831
|
+
while (true) {
|
|
16832
|
+
const { done, value } = await reader.read();
|
|
16833
|
+
if (done)
|
|
16834
|
+
break;
|
|
16835
|
+
totalBytes += value.byteLength;
|
|
16836
|
+
if (totalBytes > maximumBytes) {
|
|
16837
|
+
throw new PickerOutputTooLargeError;
|
|
16838
|
+
}
|
|
16839
|
+
chunks.push(value);
|
|
16840
|
+
}
|
|
16841
|
+
} finally {
|
|
16842
|
+
reader.releaseLock();
|
|
16843
|
+
}
|
|
16844
|
+
const output = new Uint8Array(totalBytes);
|
|
16845
|
+
let offset = 0;
|
|
16846
|
+
for (const chunk of chunks) {
|
|
16847
|
+
output.set(chunk, offset);
|
|
16848
|
+
offset += chunk.byteLength;
|
|
16849
|
+
}
|
|
16850
|
+
return new TextDecoder().decode(output);
|
|
16851
|
+
}
|
|
16852
|
+
function stripToolLineEnding(value) {
|
|
16853
|
+
return value.endsWith(`\r
|
|
16854
|
+
`) ? value.slice(0, -2) : value.endsWith(`
|
|
16855
|
+
`) ? value.slice(0, -1) : value;
|
|
16856
|
+
}
|
|
16857
|
+
function assertPickerPath(value, pathApi) {
|
|
16858
|
+
const windowsRoot = pathApi === path24.win32 ? path24.win32.parse(value).root : null;
|
|
16859
|
+
const isFullyQualifiedWindowsPath = windowsRoot === null || /^[A-Za-z]:[\\/]$/.test(windowsRoot) || /^\\\\(?![?.][\\/])[^\\/]+[\\/][^\\/]+[\\/]$/.test(windowsRoot);
|
|
16860
|
+
if (value.length === 0 || value.length > 4096 || value.includes("\x00") || !pathApi.isAbsolute(value) || !isFullyQualifiedWindowsPath) {
|
|
16861
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The system folder picker returned an invalid folder.");
|
|
16862
|
+
}
|
|
16863
|
+
return value;
|
|
16864
|
+
}
|
|
16865
|
+
function parseWindowsResult(result) {
|
|
16866
|
+
const output = stripToolLineEnding(result.stdout);
|
|
16867
|
+
if (result.exitCode !== 0) {
|
|
16868
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The Windows folder picker could not be opened.");
|
|
16869
|
+
}
|
|
16870
|
+
if (output === "cancelled")
|
|
16871
|
+
return { outcome: "cancelled" };
|
|
16872
|
+
if (!output.startsWith("selected:")) {
|
|
16873
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The Windows folder picker returned an invalid response.");
|
|
16874
|
+
}
|
|
16875
|
+
const encoded = output.slice("selected:".length);
|
|
16876
|
+
if (!encoded || !/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) {
|
|
16877
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The Windows folder picker returned an invalid response.");
|
|
16878
|
+
}
|
|
16879
|
+
try {
|
|
16880
|
+
const directory = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.from(encoded, "base64"));
|
|
16881
|
+
if (Buffer.from(directory, "utf8").toString("base64").replace(/=+$/, "") !== encoded.replace(/=+$/, "")) {
|
|
16882
|
+
throw new Error("invalid base64");
|
|
16883
|
+
}
|
|
16884
|
+
return {
|
|
16885
|
+
outcome: "selected",
|
|
16886
|
+
directory: assertPickerPath(directory, path24.win32)
|
|
16887
|
+
};
|
|
16888
|
+
} catch (error) {
|
|
16889
|
+
if (error instanceof NativeDirectoryPickerError)
|
|
16890
|
+
throw error;
|
|
16891
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The Windows folder picker returned an invalid response.");
|
|
16892
|
+
}
|
|
16893
|
+
}
|
|
16894
|
+
function parseMacosResult(result) {
|
|
16895
|
+
if (result.exitCode !== 0) {
|
|
16896
|
+
if (/\(-128\)|user canceled/i.test(result.stderr)) {
|
|
16897
|
+
return { outcome: "cancelled" };
|
|
16898
|
+
}
|
|
16899
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The macOS folder picker could not be opened.");
|
|
16900
|
+
}
|
|
16901
|
+
return {
|
|
16902
|
+
outcome: "selected",
|
|
16903
|
+
directory: assertPickerPath(stripToolLineEnding(result.stdout), path24.posix)
|
|
16904
|
+
};
|
|
16905
|
+
}
|
|
16906
|
+
function parseLinuxResult(result) {
|
|
16907
|
+
if (result.exitCode === 1)
|
|
16908
|
+
return { outcome: "cancelled" };
|
|
16909
|
+
if (result.exitCode !== 0) {
|
|
16910
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The Linux folder picker could not be opened.");
|
|
16911
|
+
}
|
|
16912
|
+
return {
|
|
16913
|
+
outcome: "selected",
|
|
16914
|
+
directory: assertPickerPath(stripToolLineEnding(result.stdout), path24.posix)
|
|
16915
|
+
};
|
|
16916
|
+
}
|
|
16917
|
+
function resolvePickerCommand({
|
|
16918
|
+
platform: platform4,
|
|
16919
|
+
environment,
|
|
16920
|
+
which
|
|
16921
|
+
}) {
|
|
16922
|
+
if (platform4 === "win32") {
|
|
16923
|
+
const configuredRoot = environment.SystemRoot?.trim();
|
|
16924
|
+
const systemRoot = configuredRoot && path24.win32.isAbsolute(configuredRoot) ? configuredRoot : "C:\\Windows";
|
|
16925
|
+
const candidate = path24.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
16926
|
+
const executable = which(candidate);
|
|
16927
|
+
if (!executable || !path24.win32.isAbsolute(executable))
|
|
16928
|
+
return null;
|
|
16929
|
+
return {
|
|
16930
|
+
kind: "windows",
|
|
16931
|
+
argv: [
|
|
16932
|
+
executable,
|
|
16933
|
+
"-NoLogo",
|
|
16934
|
+
"-NoProfile",
|
|
16935
|
+
"-NonInteractive",
|
|
16936
|
+
"-STA",
|
|
16937
|
+
"-EncodedCommand",
|
|
16938
|
+
Buffer.from(windowsPickerScript, "utf16le").toString("base64")
|
|
16939
|
+
]
|
|
16940
|
+
};
|
|
16941
|
+
}
|
|
16942
|
+
if (platform4 === "darwin") {
|
|
16943
|
+
const executable = which("/usr/bin/osascript");
|
|
16944
|
+
if (!executable || !path24.posix.isAbsolute(executable))
|
|
16945
|
+
return null;
|
|
16946
|
+
return {
|
|
16947
|
+
kind: "macos",
|
|
16948
|
+
argv: [executable, "-e", macosPickerScript]
|
|
16949
|
+
};
|
|
16950
|
+
}
|
|
16951
|
+
if (platform4 === "linux") {
|
|
16952
|
+
if (!environment.DISPLAY?.trim() && !environment.WAYLAND_DISPLAY?.trim()) {
|
|
16953
|
+
return null;
|
|
16954
|
+
}
|
|
16955
|
+
const desktop = environment.XDG_CURRENT_DESKTOP?.toLowerCase() ?? "";
|
|
16956
|
+
const candidates = desktop.includes("kde") ? ["kdialog", "zenity"] : ["zenity", "kdialog"];
|
|
16957
|
+
for (const command of candidates) {
|
|
16958
|
+
let executable = null;
|
|
16959
|
+
for (const directory of trustedLinuxExecutableDirectories) {
|
|
16960
|
+
const candidate = path24.posix.join(directory, command);
|
|
16961
|
+
const resolved = which(candidate);
|
|
16962
|
+
if (resolved === candidate) {
|
|
16963
|
+
executable = resolved;
|
|
16964
|
+
break;
|
|
16965
|
+
}
|
|
16966
|
+
}
|
|
16967
|
+
if (!executable)
|
|
16968
|
+
continue;
|
|
16969
|
+
if (command === "zenity") {
|
|
16970
|
+
return {
|
|
16971
|
+
kind: "zenity",
|
|
16972
|
+
argv: [
|
|
16973
|
+
executable,
|
|
16974
|
+
"--file-selection",
|
|
16975
|
+
"--directory",
|
|
16976
|
+
"--title=Choose a project folder"
|
|
16977
|
+
]
|
|
16978
|
+
};
|
|
16979
|
+
}
|
|
16980
|
+
return {
|
|
16981
|
+
kind: "kdialog",
|
|
16982
|
+
argv: [
|
|
16983
|
+
executable,
|
|
16984
|
+
"--title",
|
|
16985
|
+
"Choose a project folder",
|
|
16986
|
+
"--getexistingdirectory",
|
|
16987
|
+
path24.posix.isAbsolute(homedir4()) ? homedir4() : "/"
|
|
16988
|
+
]
|
|
16989
|
+
};
|
|
16990
|
+
}
|
|
16991
|
+
}
|
|
16992
|
+
return null;
|
|
16993
|
+
}
|
|
16994
|
+
function createNativeDirectoryPicker(options = {}) {
|
|
16995
|
+
const platform4 = options.platform ?? process.platform;
|
|
16996
|
+
const environment = options.environment ?? Bun.env;
|
|
16997
|
+
const which = options.which ?? ((candidate) => {
|
|
16998
|
+
try {
|
|
16999
|
+
accessSync(candidate, fsConstants2.X_OK);
|
|
17000
|
+
return candidate;
|
|
17001
|
+
} catch {
|
|
17002
|
+
return null;
|
|
17003
|
+
}
|
|
17004
|
+
});
|
|
17005
|
+
const runProcess = options.runProcess ?? runNativePickerProcess;
|
|
17006
|
+
let active = false;
|
|
17007
|
+
return {
|
|
17008
|
+
async pick(signal) {
|
|
17009
|
+
if (active) {
|
|
17010
|
+
throw new NativeDirectoryPickerError("native_picker_busy", "A folder picker is already open.");
|
|
17011
|
+
}
|
|
17012
|
+
if (signal?.aborted)
|
|
17013
|
+
return { outcome: "cancelled" };
|
|
17014
|
+
const command = resolvePickerCommand({ platform: platform4, environment, which });
|
|
17015
|
+
if (!command) {
|
|
17016
|
+
throw new NativeDirectoryPickerError("native_picker_unavailable", "No supported system folder picker is available.");
|
|
17017
|
+
}
|
|
17018
|
+
active = true;
|
|
17019
|
+
try {
|
|
17020
|
+
const result = await runProcess(command.argv, signal);
|
|
17021
|
+
if (signal?.aborted)
|
|
17022
|
+
return { outcome: "cancelled" };
|
|
17023
|
+
if (command.kind === "windows")
|
|
17024
|
+
return parseWindowsResult(result);
|
|
17025
|
+
if (command.kind === "macos")
|
|
17026
|
+
return parseMacosResult(result);
|
|
17027
|
+
return parseLinuxResult(result);
|
|
17028
|
+
} catch (error) {
|
|
17029
|
+
if (error instanceof NativeDirectoryPickerError)
|
|
17030
|
+
throw error;
|
|
17031
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The system folder picker could not be opened.");
|
|
17032
|
+
} finally {
|
|
17033
|
+
active = false;
|
|
17034
|
+
}
|
|
17035
|
+
}
|
|
17036
|
+
};
|
|
17037
|
+
}
|
|
17038
|
+
var maximumPickerOutputBytes, NativeDirectoryPickerError, trustedLinuxExecutableDirectories, PickerOutputTooLargeError, runNativePickerProcess = async (command, signal) => {
|
|
17039
|
+
if (signal?.aborted) {
|
|
17040
|
+
return { exitCode: 1, stdout: "", stderr: "" };
|
|
17041
|
+
}
|
|
17042
|
+
const child = Bun.spawn([...command], {
|
|
17043
|
+
stdin: "ignore",
|
|
17044
|
+
stdout: "pipe",
|
|
17045
|
+
stderr: "pipe",
|
|
17046
|
+
windowsHide: true
|
|
17047
|
+
});
|
|
17048
|
+
let aborted = false;
|
|
17049
|
+
const abort = () => {
|
|
17050
|
+
aborted = true;
|
|
17051
|
+
try {
|
|
17052
|
+
child.kill();
|
|
17053
|
+
} catch {}
|
|
17054
|
+
};
|
|
17055
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
17056
|
+
if (signal?.aborted)
|
|
17057
|
+
abort();
|
|
17058
|
+
try {
|
|
17059
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
17060
|
+
readBoundedStream(child.stdout, maximumPickerOutputBytes),
|
|
17061
|
+
readBoundedStream(child.stderr, maximumPickerOutputBytes),
|
|
17062
|
+
child.exited
|
|
17063
|
+
]);
|
|
17064
|
+
return {
|
|
17065
|
+
exitCode: aborted ? 1 : exitCode,
|
|
17066
|
+
stdout: aborted ? "" : stdout,
|
|
17067
|
+
stderr: aborted ? "" : stderr
|
|
17068
|
+
};
|
|
17069
|
+
} catch (error) {
|
|
17070
|
+
abort();
|
|
17071
|
+
if (error instanceof PickerOutputTooLargeError) {
|
|
17072
|
+
throw new NativeDirectoryPickerError("native_picker_failed", "The system folder picker returned an invalid response.");
|
|
17073
|
+
}
|
|
17074
|
+
throw error;
|
|
17075
|
+
} finally {
|
|
17076
|
+
signal?.removeEventListener("abort", abort);
|
|
17077
|
+
}
|
|
17078
|
+
}, windowsPickerScript, macosPickerScript;
|
|
17079
|
+
var init_native_directory_picker = __esm(() => {
|
|
17080
|
+
maximumPickerOutputBytes = 16 * 1024;
|
|
17081
|
+
NativeDirectoryPickerError = class NativeDirectoryPickerError extends Error {
|
|
17082
|
+
code;
|
|
17083
|
+
constructor(code, message) {
|
|
17084
|
+
super(message);
|
|
17085
|
+
this.code = code;
|
|
17086
|
+
this.name = "NativeDirectoryPickerError";
|
|
17087
|
+
}
|
|
17088
|
+
};
|
|
17089
|
+
trustedLinuxExecutableDirectories = [
|
|
17090
|
+
"/usr/bin",
|
|
17091
|
+
"/bin",
|
|
17092
|
+
"/run/current-system/sw/bin"
|
|
17093
|
+
];
|
|
17094
|
+
PickerOutputTooLargeError = class PickerOutputTooLargeError extends Error {
|
|
17095
|
+
};
|
|
17096
|
+
windowsPickerScript = String.raw`
|
|
17097
|
+
$ErrorActionPreference = 'Stop'
|
|
17098
|
+
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
|
17099
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
17100
|
+
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
|
17101
|
+
$dialog.Description = 'Choose a project folder'
|
|
17102
|
+
$dialog.ShowNewFolderButton = $true
|
|
17103
|
+
$dialog.AutoUpgradeEnabled = $true
|
|
17104
|
+
$owner = New-Object System.Windows.Forms.Form
|
|
17105
|
+
$owner.TopMost = $true
|
|
17106
|
+
$owner.ShowInTaskbar = $false
|
|
17107
|
+
$owner.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
|
|
17108
|
+
$owner.Location = New-Object System.Drawing.Point(-32000, -32000)
|
|
17109
|
+
$owner.Size = New-Object System.Drawing.Size(1, 1)
|
|
17110
|
+
$owner.Opacity = 0
|
|
17111
|
+
try {
|
|
17112
|
+
$owner.Show()
|
|
17113
|
+
$owner.Activate()
|
|
17114
|
+
$result = $dialog.ShowDialog($owner)
|
|
17115
|
+
if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
|
|
17116
|
+
$bytes = [Text.Encoding]::UTF8.GetBytes($dialog.SelectedPath)
|
|
17117
|
+
[Console]::Out.Write('selected:' + [Convert]::ToBase64String($bytes))
|
|
17118
|
+
} else {
|
|
17119
|
+
[Console]::Out.Write('cancelled')
|
|
17120
|
+
}
|
|
17121
|
+
} finally {
|
|
17122
|
+
$dialog.Dispose()
|
|
17123
|
+
$owner.Dispose()
|
|
17124
|
+
}
|
|
17125
|
+
`;
|
|
17126
|
+
macosPickerScript = String.raw`
|
|
17127
|
+
try
|
|
17128
|
+
set chosenFolder to choose folder with prompt "Choose a project folder"
|
|
17129
|
+
return POSIX path of chosenFolder
|
|
17130
|
+
on error number -128
|
|
17131
|
+
error number -128
|
|
17132
|
+
end try
|
|
17133
|
+
`;
|
|
17134
|
+
});
|
|
17135
|
+
|
|
16802
17136
|
// apps/server/src/routes/workspace-routes.ts
|
|
16803
17137
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
16804
17138
|
import {
|
|
@@ -16807,12 +17141,17 @@ import {
|
|
|
16807
17141
|
randomBytes,
|
|
16808
17142
|
timingSafeEqual as timingSafeEqual2
|
|
16809
17143
|
} from "crypto";
|
|
16810
|
-
import { constants as
|
|
16811
|
-
import { homedir as
|
|
16812
|
-
import
|
|
17144
|
+
import { constants as fsConstants3, promises as fs8 } from "fs";
|
|
17145
|
+
import { homedir as homedir5 } from "os";
|
|
17146
|
+
import path25 from "path";
|
|
16813
17147
|
import { Hono as Hono7 } from "hono";
|
|
16814
17148
|
function pathImplementation(platform4) {
|
|
16815
|
-
return platform4 === "win32" ?
|
|
17149
|
+
return platform4 === "win32" ? path25.win32 : path25.posix;
|
|
17150
|
+
}
|
|
17151
|
+
function isFilesystemRootPath(candidate, platform4 = process.platform) {
|
|
17152
|
+
const pathApi = pathImplementation(platform4);
|
|
17153
|
+
const normalized = pathApi.normalize(candidate);
|
|
17154
|
+
return normalized === pathApi.parse(normalized).root;
|
|
16816
17155
|
}
|
|
16817
17156
|
function normalizeKnownFolderCandidate(candidate, platform4) {
|
|
16818
17157
|
if (candidate.includes("\x00")) {
|
|
@@ -16823,7 +17162,7 @@ function normalizeKnownFolderCandidate(candidate, platform4) {
|
|
|
16823
17162
|
return null;
|
|
16824
17163
|
}
|
|
16825
17164
|
const normalized = pathApi.normalize(candidate);
|
|
16826
|
-
if (normalized
|
|
17165
|
+
if (isFilesystemRootPath(normalized, platform4)) {
|
|
16827
17166
|
return null;
|
|
16828
17167
|
}
|
|
16829
17168
|
return normalized;
|
|
@@ -16841,10 +17180,10 @@ async function probeDirectory(candidate) {
|
|
|
16841
17180
|
}
|
|
16842
17181
|
}
|
|
16843
17182
|
async function readSmallUserDirsConfiguration(filePath) {
|
|
16844
|
-
const noFollowFlag = typeof
|
|
17183
|
+
const noFollowFlag = typeof fsConstants3.O_NOFOLLOW === "number" ? fsConstants3.O_NOFOLLOW : 0;
|
|
16845
17184
|
let handle;
|
|
16846
17185
|
try {
|
|
16847
|
-
handle = await fs8.open(filePath,
|
|
17186
|
+
handle = await fs8.open(filePath, fsConstants3.O_RDONLY | noFollowFlag);
|
|
16848
17187
|
const metadata = await handle.stat();
|
|
16849
17188
|
if (!metadata.isFile() || metadata.size <= 0 || metadata.size > maximumUserDirsConfigurationBytes) {
|
|
16850
17189
|
return null;
|
|
@@ -16865,8 +17204,8 @@ async function resolveWindowsKnownFolder(environment, folder) {
|
|
|
16865
17204
|
if (!windowsRoot) {
|
|
16866
17205
|
return null;
|
|
16867
17206
|
}
|
|
16868
|
-
const systemDirectory =
|
|
16869
|
-
const executable =
|
|
17207
|
+
const systemDirectory = path25.win32.join(windowsRoot, "System32");
|
|
17208
|
+
const executable = path25.win32.join(systemDirectory, "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
16870
17209
|
const folderExpression = folder === "desktop" ? "[Environment]::GetFolderPath([Environment+SpecialFolder]::DesktopDirectory)" : folder === "documents" ? "[Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments)" : "[Environment]::ExpandEnvironmentVariables([string](Get-ItemProperty -LiteralPath 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders' -Name '{374DE290-123F-4565-9164-39C4925E467B}' -ErrorAction Stop).'{374DE290-123F-4565-9164-39C4925E467B}')";
|
|
16871
17210
|
const child = Bun.spawn([
|
|
16872
17211
|
executable,
|
|
@@ -16925,15 +17264,15 @@ function parseXdgUserDirectory(contents, homeDirectory, variableName) {
|
|
|
16925
17264
|
return null;
|
|
16926
17265
|
}
|
|
16927
17266
|
if (configured.startsWith("$HOME/")) {
|
|
16928
|
-
return
|
|
17267
|
+
return path25.posix.resolve(homeDirectory, configured.slice(6));
|
|
16929
17268
|
}
|
|
16930
17269
|
if (configured.startsWith("${HOME}/")) {
|
|
16931
|
-
return
|
|
17270
|
+
return path25.posix.resolve(homeDirectory, configured.slice(8));
|
|
16932
17271
|
}
|
|
16933
17272
|
if (configured.includes("$")) {
|
|
16934
17273
|
return null;
|
|
16935
17274
|
}
|
|
16936
|
-
return
|
|
17275
|
+
return path25.posix.isAbsolute(configured) ? path25.posix.normalize(configured) : null;
|
|
16937
17276
|
}
|
|
16938
17277
|
return null;
|
|
16939
17278
|
}
|
|
@@ -16963,7 +17302,7 @@ async function resolveDesktopDirectoryPath(options = {}) {
|
|
|
16963
17302
|
const platform4 = options.platform ?? process.platform;
|
|
16964
17303
|
const pathApi = pathImplementation(platform4);
|
|
16965
17304
|
const environment = options.environment ?? Bun.env;
|
|
16966
|
-
const homeDirectory = pathApi.normalize(options.homeDirectory ??
|
|
17305
|
+
const homeDirectory = pathApi.normalize(options.homeDirectory ?? homedir5());
|
|
16967
17306
|
const explicit = environment.LIGHTCODE_DESKTOP_PATH?.trim();
|
|
16968
17307
|
if (explicit) {
|
|
16969
17308
|
const normalized = normalizeKnownFolderCandidate(explicit, platform4);
|
|
@@ -16987,17 +17326,17 @@ async function resolveDesktopDirectoryPath(options = {}) {
|
|
|
16987
17326
|
return [];
|
|
16988
17327
|
}
|
|
16989
17328
|
return [
|
|
16990
|
-
index === 0 ? trimmed :
|
|
17329
|
+
index === 0 ? trimmed : path25.win32.join(trimmed, "Desktop")
|
|
16991
17330
|
];
|
|
16992
17331
|
});
|
|
16993
|
-
candidates.push(
|
|
16994
|
-
return await firstUsableKnownFolder(candidates, platform4, probe) ??
|
|
17332
|
+
candidates.push(path25.win32.join(userProfile, "Desktop"));
|
|
17333
|
+
return await firstUsableKnownFolder(candidates, platform4, probe) ?? path25.win32.join(homeDirectory, "Desktop");
|
|
16995
17334
|
}
|
|
16996
17335
|
if (platform4 === "linux") {
|
|
16997
17336
|
const configHome = environment.XDG_CONFIG_HOME?.trim();
|
|
16998
|
-
const safeConfigHome = configHome &&
|
|
17337
|
+
const safeConfigHome = configHome && path25.posix.isAbsolute(configHome) ? path25.posix.normalize(configHome) : path25.posix.join(homeDirectory, ".config");
|
|
16999
17338
|
const readConfiguration = options.readUserDirsConfiguration ?? readSmallUserDirsConfiguration;
|
|
17000
|
-
const contents = await readConfiguration(
|
|
17339
|
+
const contents = await readConfiguration(path25.posix.join(safeConfigHome, "user-dirs.dirs"));
|
|
17001
17340
|
const configured = contents ? parseXdgUserDirectory(contents, homeDirectory, "XDG_DESKTOP_DIR") : null;
|
|
17002
17341
|
if (configured) {
|
|
17003
17342
|
const selected = await firstUsableKnownFolder([configured], platform4, probe);
|
|
@@ -17012,7 +17351,7 @@ async function resolveUserDirectoryPath(folder, options) {
|
|
|
17012
17351
|
const platform4 = options.platform ?? process.platform;
|
|
17013
17352
|
const pathApi = pathImplementation(platform4);
|
|
17014
17353
|
const environment = options.environment ?? Bun.env;
|
|
17015
|
-
const homeDirectory = pathApi.normalize(options.homeDirectory ??
|
|
17354
|
+
const homeDirectory = pathApi.normalize(options.homeDirectory ?? homedir5());
|
|
17016
17355
|
const displayName = folder === "documents" ? "Documents" : "Downloads";
|
|
17017
17356
|
const overrideName = folder === "documents" ? "LIGHTCODE_DOCUMENTS_PATH" : "LIGHTCODE_DOWNLOADS_PATH";
|
|
17018
17357
|
const explicit = environment[overrideName]?.trim();
|
|
@@ -17034,17 +17373,17 @@ async function resolveUserDirectoryPath(folder, options) {
|
|
|
17034
17373
|
];
|
|
17035
17374
|
const candidates = [
|
|
17036
17375
|
knownFolder,
|
|
17037
|
-
...cloudRoots.map((root) => root?.trim() ?
|
|
17038
|
-
|
|
17039
|
-
|
|
17376
|
+
...cloudRoots.map((root) => root?.trim() ? path25.win32.join(root.trim(), displayName) : null),
|
|
17377
|
+
path25.win32.join(userProfile, displayName),
|
|
17378
|
+
path25.win32.join(homeDirectory, displayName)
|
|
17040
17379
|
];
|
|
17041
|
-
return await firstUsableKnownFolder(candidates, platform4, probe) ??
|
|
17380
|
+
return await firstUsableKnownFolder(candidates, platform4, probe) ?? path25.win32.join(homeDirectory, displayName);
|
|
17042
17381
|
}
|
|
17043
17382
|
if (platform4 === "linux") {
|
|
17044
17383
|
const configHome = environment.XDG_CONFIG_HOME?.trim();
|
|
17045
|
-
const safeConfigHome = configHome &&
|
|
17384
|
+
const safeConfigHome = configHome && path25.posix.isAbsolute(configHome) ? path25.posix.normalize(configHome) : path25.posix.join(homeDirectory, ".config");
|
|
17046
17385
|
const readConfiguration = options.readUserDirsConfiguration ?? readSmallUserDirsConfiguration;
|
|
17047
|
-
const contents = await readConfiguration(
|
|
17386
|
+
const contents = await readConfiguration(path25.posix.join(safeConfigHome, "user-dirs.dirs"));
|
|
17048
17387
|
const configured = contents ? parseXdgUserDirectory(contents, homeDirectory, folder === "documents" ? "XDG_DOCUMENTS_DIR" : "XDG_DOWNLOAD_DIR") : null;
|
|
17049
17388
|
const selected = await firstUsableKnownFolder([configured], platform4, probe);
|
|
17050
17389
|
if (selected) {
|
|
@@ -17063,7 +17402,7 @@ async function resolveProjectsDirectoryPath(options = {}) {
|
|
|
17063
17402
|
const platform4 = options.platform ?? process.platform;
|
|
17064
17403
|
const pathApi = pathImplementation(platform4);
|
|
17065
17404
|
const environment = options.environment ?? Bun.env;
|
|
17066
|
-
const homeDirectory = pathApi.normalize(options.homeDirectory ??
|
|
17405
|
+
const homeDirectory = pathApi.normalize(options.homeDirectory ?? homedir5());
|
|
17067
17406
|
const explicit = environment.LIGHTCODE_PROJECTS_PATH?.trim();
|
|
17068
17407
|
if (explicit) {
|
|
17069
17408
|
const normalized = normalizeKnownFolderCandidate(explicit, platform4);
|
|
@@ -17114,11 +17453,23 @@ function mapFilesystemError(error) {
|
|
|
17114
17453
|
}
|
|
17115
17454
|
return new WorkspaceRouteError("workspace_unavailable", "Unable to access the selected workspace.", 500, true);
|
|
17116
17455
|
}
|
|
17456
|
+
function mapNativePickerError(error) {
|
|
17457
|
+
if (!(error instanceof NativeDirectoryPickerError)) {
|
|
17458
|
+
return new WorkspaceRouteError("native_picker_failed", "The system folder picker could not be opened.", 500, true);
|
|
17459
|
+
}
|
|
17460
|
+
if (error.code === "native_picker_busy") {
|
|
17461
|
+
return new WorkspaceRouteError(error.code, "A folder picker is already open.", 409, true);
|
|
17462
|
+
}
|
|
17463
|
+
if (error.code === "native_picker_unavailable") {
|
|
17464
|
+
return new WorkspaceRouteError(error.code, "No supported system folder picker is available.", 503, false);
|
|
17465
|
+
}
|
|
17466
|
+
return new WorkspaceRouteError(error.code, "The system folder picker could not be opened.", 500, true);
|
|
17467
|
+
}
|
|
17117
17468
|
function workspaceErrorResponse(c, error) {
|
|
17118
17469
|
const mapped = error instanceof WorkspaceRouteError ? error : mapFilesystemError(error);
|
|
17119
17470
|
if (mapped.status === 500) {
|
|
17120
17471
|
logger13.error("workspace_request_failed", {
|
|
17121
|
-
error: getErrorMessage(
|
|
17472
|
+
error: getErrorMessage(mapped)
|
|
17122
17473
|
});
|
|
17123
17474
|
}
|
|
17124
17475
|
return c.json({
|
|
@@ -17144,13 +17495,13 @@ function identitiesMatch(left, right) {
|
|
|
17144
17495
|
return left.device === right.device && left.inode === right.inode;
|
|
17145
17496
|
}
|
|
17146
17497
|
function isContainedPath2(root, candidate) {
|
|
17147
|
-
const relative =
|
|
17148
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
17498
|
+
const relative = path25.relative(root, candidate);
|
|
17499
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path25.sep}`) && !path25.isAbsolute(relative);
|
|
17149
17500
|
}
|
|
17150
17501
|
async function resolveKnownDirectory(directory) {
|
|
17151
17502
|
try {
|
|
17152
|
-
const root = await fs8.realpath(
|
|
17153
|
-
if (
|
|
17503
|
+
const root = await fs8.realpath(path25.resolve(directory));
|
|
17504
|
+
if (isFilesystemRootPath(root)) {
|
|
17154
17505
|
throw new WorkspaceRouteError("workspace_unavailable", "A filesystem root cannot be opened as a workspace location.", 500, false);
|
|
17155
17506
|
}
|
|
17156
17507
|
const stats = await fs8.stat(root, { bigint: true });
|
|
@@ -17199,7 +17550,7 @@ async function symlinkFailure(root, candidate, locationName) {
|
|
|
17199
17550
|
async function resolveDirectoryFromSegments(root, segments, locationName) {
|
|
17200
17551
|
let current = root;
|
|
17201
17552
|
for (const segment of segments) {
|
|
17202
|
-
const candidate =
|
|
17553
|
+
const candidate = path25.join(current, segment);
|
|
17203
17554
|
if (!isContainedPath2(root, candidate)) {
|
|
17204
17555
|
throw new WorkspaceRouteError("workspace_symlink_escape", `The requested path is outside the allowed ${locationName} location.`, 403, false);
|
|
17205
17556
|
}
|
|
@@ -17302,7 +17653,7 @@ function decodeCursor(secret, cursor, binding) {
|
|
|
17302
17653
|
}
|
|
17303
17654
|
}
|
|
17304
17655
|
async function describeEntry(root, directory, name) {
|
|
17305
|
-
const candidate =
|
|
17656
|
+
const candidate = path25.join(directory, name);
|
|
17306
17657
|
try {
|
|
17307
17658
|
const stats = await fs8.lstat(candidate);
|
|
17308
17659
|
if (stats.isSymbolicLink()) {
|
|
@@ -17326,7 +17677,7 @@ async function describeEntry(root, directory, name) {
|
|
|
17326
17677
|
}
|
|
17327
17678
|
}
|
|
17328
17679
|
const kind = stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other";
|
|
17329
|
-
const requiredAccess = stats.isDirectory() ?
|
|
17680
|
+
const requiredAccess = stats.isDirectory() ? fsConstants3.R_OK | fsConstants3.X_OK : fsConstants3.R_OK;
|
|
17330
17681
|
const readable = await fs8.access(candidate, requiredAccess).then(() => true).catch(() => false);
|
|
17331
17682
|
return {
|
|
17332
17683
|
name,
|
|
@@ -17382,7 +17733,7 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17382
17733
|
return normalized;
|
|
17383
17734
|
} : () => resolveDesktopDirectoryPath());
|
|
17384
17735
|
const desktopPathLabel = options.desktopPathLabel ?? "Desktop";
|
|
17385
|
-
const defaultHomePath = async () =>
|
|
17736
|
+
const defaultHomePath = async () => homedir5();
|
|
17386
17737
|
const defaultDocumentsPath = () => resolveDocumentsDirectoryPath();
|
|
17387
17738
|
const defaultDownloadsPath = () => resolveDownloadsDirectoryPath();
|
|
17388
17739
|
const definitions = [
|
|
@@ -17424,6 +17775,8 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17424
17775
|
];
|
|
17425
17776
|
const definitionsById = new Map(definitions.map((definition) => [definition.id, definition]));
|
|
17426
17777
|
const createSession = options.createSession ?? createChatSession;
|
|
17778
|
+
const nativeDirectoryPicker = options.nativeDirectoryPicker ?? createNativeDirectoryPicker();
|
|
17779
|
+
const authorizeNativePickerRequest = options.authorizeNativePickerRequest ?? isAuthenticatedWebRequest;
|
|
17427
17780
|
const cursorSecret = randomBytes(32);
|
|
17428
17781
|
const browserCapabilities = new Map;
|
|
17429
17782
|
const workspaceGrants = new Map;
|
|
@@ -17442,7 +17795,70 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17442
17795
|
}
|
|
17443
17796
|
return grant;
|
|
17444
17797
|
};
|
|
17445
|
-
|
|
17798
|
+
const storeWorkspaceGrant = async ({
|
|
17799
|
+
root,
|
|
17800
|
+
identity,
|
|
17801
|
+
name,
|
|
17802
|
+
pathLabel
|
|
17803
|
+
}) => {
|
|
17804
|
+
await fs8.access(root, fsConstants3.R_OK | fsConstants3.X_OK);
|
|
17805
|
+
const createdAt = new Date(now()).toISOString();
|
|
17806
|
+
const id = crypto.randomUUID();
|
|
17807
|
+
const grant = {
|
|
17808
|
+
id,
|
|
17809
|
+
name,
|
|
17810
|
+
pathLabel,
|
|
17811
|
+
createdAt,
|
|
17812
|
+
root,
|
|
17813
|
+
identity
|
|
17814
|
+
};
|
|
17815
|
+
workspaceGrants.set(id, grant);
|
|
17816
|
+
trimOldestEntries(workspaceGrants, maximumWorkspaceGrants);
|
|
17817
|
+
try {
|
|
17818
|
+
await assertRootIdentity(grant);
|
|
17819
|
+
} catch (error) {
|
|
17820
|
+
workspaceGrants.delete(id);
|
|
17821
|
+
throw error;
|
|
17822
|
+
}
|
|
17823
|
+
return workspaceGrantSchema.parse({ id, name, pathLabel, createdAt });
|
|
17824
|
+
};
|
|
17825
|
+
return new Hono7().post("/picker/open", zValidator3("json", workspaceNativePickerRequestSchema, (result, c) => {
|
|
17826
|
+
if (!result.success) {
|
|
17827
|
+
return invalidRequestResponse(c);
|
|
17828
|
+
}
|
|
17829
|
+
}), async (c) => {
|
|
17830
|
+
if (!authorizeNativePickerRequest(c.req.raw)) {
|
|
17831
|
+
return c.json({
|
|
17832
|
+
error: "The system folder picker is available only to an authenticated Lightcode web session.",
|
|
17833
|
+
code: "web_browser_required"
|
|
17834
|
+
}, 403);
|
|
17835
|
+
}
|
|
17836
|
+
try {
|
|
17837
|
+
let picked;
|
|
17838
|
+
try {
|
|
17839
|
+
picked = await nativeDirectoryPicker.pick(c.req.raw.signal);
|
|
17840
|
+
} catch (error) {
|
|
17841
|
+
throw mapNativePickerError(error);
|
|
17842
|
+
}
|
|
17843
|
+
if (picked.outcome === "cancelled") {
|
|
17844
|
+
return c.json(workspaceNativePickerResponseSchema.parse({
|
|
17845
|
+
outcome: "cancelled"
|
|
17846
|
+
}), 200);
|
|
17847
|
+
}
|
|
17848
|
+
const directory = await resolveKnownDirectory(picked.directory);
|
|
17849
|
+
const workspace = await storeWorkspaceGrant({
|
|
17850
|
+
...directory,
|
|
17851
|
+
name: path25.basename(directory.root),
|
|
17852
|
+
pathLabel: directory.root
|
|
17853
|
+
});
|
|
17854
|
+
return c.json(workspaceNativePickerResponseSchema.parse({
|
|
17855
|
+
outcome: "selected",
|
|
17856
|
+
workspace
|
|
17857
|
+
}), 201);
|
|
17858
|
+
} catch (error) {
|
|
17859
|
+
return workspaceErrorResponse(c, error);
|
|
17860
|
+
}
|
|
17861
|
+
}).get("/locations", (c) => c.json(workspaceLocationsResponseSchema.parse({
|
|
17446
17862
|
locations: definitions.map((definition) => knownLocation(definition, "unprobed"))
|
|
17447
17863
|
}))).post("/browser/open", zValidator3("json", workspaceBrowserOpenRequestSchema, (result, c) => {
|
|
17448
17864
|
if (!result.success) {
|
|
@@ -17578,37 +17994,19 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17578
17994
|
operation: "select",
|
|
17579
17995
|
directory: directory.path
|
|
17580
17996
|
});
|
|
17581
|
-
await fs8.access(directory.path, fsConstants2.R_OK | fsConstants2.X_OK);
|
|
17582
|
-
const createdAt = new Date(now()).toISOString();
|
|
17583
|
-
const id = crypto.randomUUID();
|
|
17584
17997
|
const name = segments.at(-1) ?? capability2.rootName;
|
|
17585
17998
|
const pathLabel = [
|
|
17586
17999
|
capability2.location.pathLabel,
|
|
17587
18000
|
...segments
|
|
17588
18001
|
].join("/");
|
|
17589
|
-
const
|
|
17590
|
-
id,
|
|
17591
|
-
name,
|
|
17592
|
-
pathLabel,
|
|
17593
|
-
createdAt,
|
|
18002
|
+
const workspace = await storeWorkspaceGrant({
|
|
17594
18003
|
root: directory.path,
|
|
17595
|
-
identity: directory.identity
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
try {
|
|
17600
|
-
await assertRootIdentity(grant);
|
|
17601
|
-
} catch (error) {
|
|
17602
|
-
workspaceGrants.delete(id);
|
|
17603
|
-
throw error;
|
|
17604
|
-
}
|
|
18004
|
+
identity: directory.identity,
|
|
18005
|
+
name,
|
|
18006
|
+
pathLabel
|
|
18007
|
+
});
|
|
17605
18008
|
return c.json(workspaceBrowserSelectResponseSchema.parse({
|
|
17606
|
-
workspace
|
|
17607
|
-
id,
|
|
17608
|
-
name,
|
|
17609
|
-
pathLabel,
|
|
17610
|
-
createdAt
|
|
17611
|
-
})
|
|
18009
|
+
workspace
|
|
17612
18010
|
}), 201);
|
|
17613
18011
|
} catch (error) {
|
|
17614
18012
|
return workspaceErrorResponse(c, error);
|
|
@@ -17646,6 +18044,8 @@ var init_workspace_routes = __esm(() => {
|
|
|
17646
18044
|
init_src();
|
|
17647
18045
|
init_chat_store();
|
|
17648
18046
|
init_runtime_config();
|
|
18047
|
+
init_native_directory_picker();
|
|
18048
|
+
init_web_auth();
|
|
17649
18049
|
logger13 = createLogger("workspace-routes");
|
|
17650
18050
|
defaultBrowserCapabilityTtlMs = 10 * 60 * 1000;
|
|
17651
18051
|
maximumUserDirsConfigurationBytes = 16 * 1024;
|