Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
cce2bcc
feat(workspace): support any javascript runtime
mutewinter Oct 8, 2025
55028e4
fix(studio): handle unpacked app path
mutewinter Oct 8, 2025
af5b184
fix(studio): ensure windows can run .cjs files with node prefix
mutewinter Oct 9, 2025
28f7559
fix(studio): proper git folder
mutewinter Oct 9, 2025
b477124
fix(workspace): proper PATH delimiter for windows
mutewinter Oct 9, 2025
8342d7e
feat(studio): tweaked cmd file for windows
mutewinter Oct 9, 2025
61a9d75
fix(studio): back to simpler cmd
mutewinter Oct 9, 2025
9fbf461
fix(studio): try to avoid visible cmd.exe
mutewinter Oct 9, 2025
97ced04
fix(workspace): attempt to hide cmd.exe
mutewinter Oct 9, 2025
ccb6dd1
fix(workspace): lower verbosity logs
mutewinter Oct 9, 2025
145c84f
feat(studio): new shim files powered by PS1
mutewinter Oct 9, 2025
36f0577
chore(workspace): remove fixme in favor of todo and windows hide
mutewinter Oct 9, 2025
dd41399
wip: try .cmd only with hide again
mutewinter Oct 9, 2025
31a25bf
wip: try cmd-shim
mutewinter Oct 9, 2025
ecc4d60
wip: extract bin locations and use links on windows
mutewinter Oct 10, 2025
d5b1e2a
wip: back to shim for node
mutewinter Oct 10, 2025
ea671d2
wip: fix regex for windows
mutewinter Oct 10, 2025
5497e07
wip: better shim extraction and correct absolute path with tests
mutewinter Oct 10, 2025
0a5b40a
wip: windows-specific task kill
mutewinter Oct 10, 2025
689aac5
wip: direct exec
mutewinter Oct 10, 2025
3561b12
wip: rollback ineffectual pid-based kill
mutewinter Oct 10, 2025
c880905
wip: process-specific which where
mutewinter Oct 10, 2025
4b2defc
wip: try taskkill on windows exclusively
mutewinter Oct 10, 2025
6855d21
Revert "wip: try taskkill on windows exclusively"
mutewinter Oct 10, 2025
55fa00e
wip: remove notion of .bin usage, since it isn't present in final bundle
mutewinter Oct 14, 2025
d0cb0e3
wip: setup bins now injects path automatically
mutewinter Oct 14, 2025
62256c8
wip: back to working node: true execution with shim file bin extracti…
mutewinter Oct 14, 2025
81e845d
wip: simplify dev server info
mutewinter Oct 14, 2025
c20f672
wip: netlify build info based framework and package manager detection
mutewinter Oct 14, 2025
e4724b5
fix(workspace): preserve query parameters and forward proto for proxy
mutewinter Oct 14, 2025
cc08e2a
wip: refactor to use execa without literal and tsc via execa locally
mutewinter Oct 14, 2025
7f452dd
wip: use direct bin for shell command
mutewinter Oct 14, 2025
d073df8
wip: remove runshellcommand
mutewinter Oct 14, 2025
d8e1d9e
wip: remove runPackageJsonScript
mutewinter Oct 14, 2025
bdff32f
wip: move concept of node env variables to workspace config
mutewinter Oct 14, 2025
bad5d37
wip: use default accuracy sort
mutewinter Oct 14, 2025
e5a4f3d
wip: assume pnpm for now and handle astro
mutewinter Oct 14, 2025
1eb9c4d
wip: remove logging
mutewinter Oct 14, 2025
6e1274b
wip: no build info
mutewinter Oct 14, 2025
73d8167
wip: log and telemetry for unknown framework
mutewinter Oct 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions apps/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@
"@orpc/client": "catalog:",
"@orpc/server": "catalog:",
"@orpc/tanstack-query": "catalog:",
"@pnpm/package-bins": "^1000.0.3",
"@pnpm/read-package-json": "^1000.0.4",
"@pnpm/types": "^1000.2.0",
"@quests/ai-gateway": "workspace:*",
"@quests/components": "workspace:*",
"@quests/shared": "workspace:*",
Expand All @@ -83,6 +80,7 @@
"@tanstack/react-router-devtools": "^1.131.28",
"@types/ws": "^8.18.1",
"@vscode/ripgrep": "^1.15.13",
"@zkochan/cmd-shim": "^7.0.0",
"arctic": "^3.6.0",
"better-auth": "catalog:",
"class-variance-authority": "^0.7.1",
Expand All @@ -96,14 +94,11 @@
"electron-log": "^5.4.1",
"electron-store": "^10.1.0",
"electron-updater": "^6.6.8",
"execa": "^9.6.0",
"framer-motion": "11.13.5",
"hono": "catalog:",
"jotai": "^2.14.0",
"lucide-react": "catalog:",
"ms": "^2.1.3",
"neverthrow": "^8.1.1",
"normalize-path": "^3.0.0",
"pnpm": "^10.13.1",
"posthog-js": "^1.258.6",
"posthog-node": "^5.6.0",
Expand Down Expand Up @@ -132,7 +127,6 @@
"@types/color-hash": "^2.0.0",
"@types/ms": "^2.1.0",
"@types/node": "22.16.0",
"@types/normalize-path": "^3.0.2",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/semver": "^7.7.0",
Expand Down
1 change: 1 addition & 0 deletions apps/studio/src/client/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ declare namespace NodeJS {
ELECTRON_USE_NEW_USER_FOLDER: string | undefined;
FORCE_DEV_AUTO_UPDATE: string | undefined;
NODE_ENV: string | undefined;
PATH: string | undefined;
SIGNTOOL_PATH: string | undefined;
WIN_CERT_PATH: string | undefined;
WIN_GCP_KMS_KEY_VERSION: string | undefined;
Expand Down
3 changes: 3 additions & 0 deletions apps/studio/src/electron-main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import path from "node:path";

import { createWorkspaceActor } from "./lib/create-workspace-actor";
import { registerTelemetry } from "./lib/register-telemetry";
import { setupBinDirectory } from "./lib/setup-bin-directory";
import { watchThemePreferenceAndApply } from "./lib/theme-utils";
import { initializeRPC } from "./rpc/initialize";

Expand Down Expand Up @@ -114,6 +115,8 @@ void app.whenReady().then(async () => {
updateTitleBarOverlay();
});

await setupBinDirectory();

appUpdater = new StudioAppUpdater();
appUpdater.pollForUpdates();

Expand Down
85 changes: 7 additions & 78 deletions apps/studio/src/electron-main/lib/create-workspace-actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,19 @@ import {
workspacePublisher,
} from "@quests/workspace/electron";
import { app, shell } from "electron";
import { execa, parseCommandString } from "execa";
import ms from "ms";
import { err, ok } from "neverthrow";
import path from "node:path";
import { createActor } from "xstate";

import { getProvidersStore } from "../stores/providers";
import { captureServerEvent } from "./capture-server-event";
import { captureServerException } from "./capture-server-exception";
import { getFramework } from "./frameworks";
import { getAllPackageBinaryPaths } from "./link-bins";
import { getPnpmPath } from "./pnpm";
import { getPNPMBinPath } from "./setup-bin-directory";

const scopedLogger = logger.scope("workspace-actor");

export function createWorkspaceActor() {
const rootDir = path.join(app.getPath("userData"), WORKSPACE_FOLDER);
const execaEnv = {
// Required for normal node processes to work
// See https://www.electronjs.org/docs/latest/api/environment-variables
ELECTRON_RUN_AS_NODE: "1",
};
const actor = createActor(workspaceMachine, {
input: {
aiGatewayApp,
Expand All @@ -54,79 +45,17 @@ export function createWorkspaceActor() {

return providers;
},
nodeExecEnv: {
// Required to allow Electron to operate as a node process
// See https://www.electronjs.org/docs/latest/api/environment-variables
ELECTRON_RUN_AS_NODE: "1",
},
pnpmBinPath: getPNPMBinPath(),
previewCacheTimeMs: ms("24 hours"),
registryDir: app.isPackaged
? path.join(process.resourcesPath, REGISTRY_DIR_NAME)
: path.resolve(import.meta.dirname, REGISTRY_DEV_DIR_PATH),
rootDir,
runPackageJsonScript: async ({ cwd, script, scriptOptions, signal }) => {
const { framework, frameworkModulePath } = await getFramework({
rootDir: cwd,
script,
});
if (!framework) {
return err(new Error(`Unsupported framework: ${script}`));
}

try {
return ok(
execa({
cancelSignal: signal,
cwd,
env: {
...execaEnv,
...scriptOptions.env,
},
node: true,
})`${frameworkModulePath} ${framework.args("dev", scriptOptions.port)}`,
);
} catch (error) {
return err(error instanceof Error ? error : new Error(String(error)));
}
},
runShellCommand: async (command, { cwd, signal }) => {
const [commandName, ...rest] = parseCommandString(command);
const pnpmPath = getPnpmPath();

if (commandName === "pnpm") {
return ok(
execa({
cancelSignal: signal,
cwd,
env: execaEnv,
node: true,
})`${pnpmPath} ${rest}`,
);
}

if (commandName === "tsc") {
const binaryPaths = await getAllPackageBinaryPaths(cwd);
const binPaths = binaryPaths.get("typescript");

if (!binPaths) {
return err(new Error(`tsc not found in ${cwd}`));
}

const modulePath = binPaths.find(
(binPath) => path.basename(binPath) === "tsc",
);

if (!modulePath) {
return err(new Error(`tsc not found in ${cwd}`));
}

return ok(
execa({
cancelSignal: signal,
cwd,
env: execaEnv,
node: true,
})`${modulePath} ${rest}`,
);
}

return err(new Error(`Not implemented: ${command}`));
},
shimClientDir: app.isPackaged
? path.resolve(process.resourcesPath, "shim-client")
: // Uncomment to test built shim
Expand Down
63 changes: 0 additions & 63 deletions apps/studio/src/electron-main/lib/frameworks.ts

This file was deleted.

102 changes: 0 additions & 102 deletions apps/studio/src/electron-main/lib/link-bins.ts

This file was deleted.

14 changes: 7 additions & 7 deletions apps/studio/src/electron-main/lib/pnpm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { forkExecCommand } from "@/electron-main/lib/exec-command";
import { createRequire } from "node:module";
import path from "node:path";

export function getPnpmPath() {
export async function pnpmVersion() {
const pnpmPath = getPnpmPath();
const result = await forkExecCommand(pnpmPath, ["-v"]);
return result.stdout;
}

function getPnpmPath() {
const require = createRequire(import.meta.url);
const packageJsonPath = require.resolve("pnpm");
const unpackedPath = packageJsonPath.replace("app.asar", "app.asar.unpacked");
const pnpmPath = path.dirname(unpackedPath);
return path.join(pnpmPath, "bin", "pnpm.cjs");
}

export async function pnpmVersion() {
const pnpmPath = getPnpmPath();
const result = await forkExecCommand(pnpmPath, ["-v"]);
return result.stdout;
}
Loading