@spencer-kit/coder-studio 0.3.3 → 0.3.4

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.
@@ -863,11 +863,11 @@ function resolveSpawnArgv(argv, deps = {}) {
863
863
  return [];
864
864
  }
865
865
  const restArgs = argv.slice(1);
866
- const readFileSync8 = deps.readFileSync ?? ((file) => fs2.readFileSync(file, "utf8"));
867
- const existsSync9 = deps.existsSync ?? fs2.existsSync;
866
+ const readFileSync9 = deps.readFileSync ?? ((file) => fs2.readFileSync(file, "utf8"));
867
+ const existsSync10 = deps.existsSync ?? fs2.existsSync;
868
868
  const pathEnv = deps.pathEnv ?? process.env.Path ?? process.env.PATH ?? "";
869
869
  const pathExt = deps.pathExt ?? process.env.PATHEXT ?? DEFAULT_PATHEXT;
870
- const resolved = resolveExecutablePath(command, pathEnv, pathExt, existsSync9);
870
+ const resolved = resolveExecutablePath(command, pathEnv, pathExt, existsSync10);
871
871
  if (!resolved) {
872
872
  return [...argv];
873
873
  }
@@ -878,7 +878,7 @@ function resolveSpawnArgv(argv, deps = {}) {
878
878
  if (ext === ".cmd" || ext === ".bat") {
879
879
  let content;
880
880
  try {
881
- content = readFileSync8(resolved);
881
+ content = readFileSync9(resolved);
882
882
  } catch {
883
883
  return ["cmd.exe", "/d", "/s", "/c", resolved, ...restArgs];
884
884
  }
@@ -916,17 +916,17 @@ function expandShimVars(value, dp0Dir) {
916
916
  function parsePathExt(pathExt) {
917
917
  return pathExt.split(";").map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0);
918
918
  }
919
- function resolveExecutablePath(command, pathEnv, pathExt, existsSync9) {
919
+ function resolveExecutablePath(command, pathEnv, pathExt, existsSync10) {
920
920
  const hasExt = path2.win32.extname(command).length > 0;
921
921
  const extensions = parsePathExt(pathExt);
922
922
  if (path2.win32.isAbsolute(command)) {
923
- if (existsSync9(command)) {
923
+ if (existsSync10(command)) {
924
924
  return command;
925
925
  }
926
926
  if (!hasExt) {
927
927
  for (const ext of extensions) {
928
928
  const candidate = command + ext;
929
- if (existsSync9(candidate)) {
929
+ if (existsSync10(candidate)) {
930
930
  return candidate;
931
931
  }
932
932
  }
@@ -937,14 +937,14 @@ function resolveExecutablePath(command, pathEnv, pathExt, existsSync9) {
937
937
  for (const dir of dirs) {
938
938
  if (hasExt) {
939
939
  const candidate = path2.win32.join(dir, command);
940
- if (existsSync9(candidate)) {
940
+ if (existsSync10(candidate)) {
941
941
  return candidate;
942
942
  }
943
943
  continue;
944
944
  }
945
945
  for (const ext of extensions) {
946
946
  const candidate = path2.win32.join(dir, command + ext);
947
- if (existsSync9(candidate)) {
947
+ if (existsSync10(candidate)) {
948
948
  return candidate;
949
949
  }
950
950
  }
@@ -2107,6 +2107,157 @@ var init_command_check = __esm({
2107
2107
  }
2108
2108
  });
2109
2109
 
2110
+ // packages/server/src/provider-runtime/e2e-provider-mock.ts
2111
+ import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
2112
+ import { dirname as dirname4, join as join3 } from "node:path";
2113
+ function createE2EProviderMockOverrides(env = process.env) {
2114
+ const statePath = env.CODER_STUDIO_E2E_PROVIDER_STATE_PATH;
2115
+ if (!statePath) {
2116
+ return null;
2117
+ }
2118
+ const binDir = env.CODER_STUDIO_E2E_PROVIDER_BIN_DIR;
2119
+ const debugLogPath = env.CODER_STUDIO_E2E_PROVIDER_DEBUG_LOG_PATH;
2120
+ appendDebugLog(debugLogPath, `init statePath=${statePath} binDir=${binDir ?? ""}`);
2121
+ const commandExists = async (command) => {
2122
+ const state = readMockState(statePath);
2123
+ const override = state.commands?.[command];
2124
+ appendDebugLog(
2125
+ debugLogPath,
2126
+ `commandExists ${command} override=${String(override)} state=${JSON.stringify(state.commands ?? {})}`
2127
+ );
2128
+ if (typeof override === "boolean") {
2129
+ return override;
2130
+ }
2131
+ return checkCommandAvailable(command);
2132
+ };
2133
+ const runCommand2 = async (file, args, options) => {
2134
+ const providerId = getInstallProviderId(file, args);
2135
+ appendDebugLog(
2136
+ debugLogPath,
2137
+ `runCommand ${file} ${args.join(" ")} provider=${providerId ?? "none"}`
2138
+ );
2139
+ if (!providerId) {
2140
+ return runCommandAsString(file, args, options);
2141
+ }
2142
+ const state = readMockState(statePath);
2143
+ const behavior = state.installBehavior?.[providerId];
2144
+ appendDebugLog(
2145
+ debugLogPath,
2146
+ `behavior ${providerId} ${JSON.stringify(behavior)} state=${JSON.stringify(state)}`
2147
+ );
2148
+ if (!behavior) {
2149
+ return runCommandAsString(file, args, options);
2150
+ }
2151
+ if (behavior.result === "success") {
2152
+ writeMockState(statePath, (draft) => {
2153
+ draft.commands ??= {};
2154
+ draft.commands[providerId] = true;
2155
+ });
2156
+ if (binDir) {
2157
+ ensureProviderCommand(binDir, providerId);
2158
+ }
2159
+ appendDebugLog(debugLogPath, `install success ${providerId}`);
2160
+ return {
2161
+ stdout: `installed ${providerId}`,
2162
+ stderr: ""
2163
+ };
2164
+ }
2165
+ const message = behavior.message ?? (behavior.result === "permission_denied" ? "permission denied" : "command not found");
2166
+ throw Object.assign(new Error(message), {
2167
+ exitCode: 1,
2168
+ stdout: "",
2169
+ stderr: message
2170
+ });
2171
+ };
2172
+ return {
2173
+ commandExists,
2174
+ runCommand: runCommand2
2175
+ };
2176
+ }
2177
+ function getInstallProviderId(file, args) {
2178
+ if (file !== "npm" || args.length !== 3) {
2179
+ return null;
2180
+ }
2181
+ if (args[0] !== "install" || args[1] !== "-g") {
2182
+ return null;
2183
+ }
2184
+ const packageName = args[2];
2185
+ if (packageName === PROVIDER_INSTALL_PACKAGES.claude) {
2186
+ return "claude";
2187
+ }
2188
+ if (packageName === PROVIDER_INSTALL_PACKAGES.codex) {
2189
+ return "codex";
2190
+ }
2191
+ return null;
2192
+ }
2193
+ function readMockState(statePath) {
2194
+ if (!existsSync6(statePath)) {
2195
+ return {};
2196
+ }
2197
+ const raw = readFileSync5(statePath, "utf8");
2198
+ if (!raw.trim()) {
2199
+ return {};
2200
+ }
2201
+ try {
2202
+ return JSON.parse(raw);
2203
+ } catch (error) {
2204
+ throw new Error(
2205
+ `Invalid provider mock state at ${statePath}: ${error instanceof Error ? error.message : String(error)}`
2206
+ );
2207
+ }
2208
+ }
2209
+ function writeMockState(statePath, updater) {
2210
+ const nextState = readMockState(statePath);
2211
+ updater(nextState);
2212
+ mkdirSync3(dirname4(statePath), { recursive: true });
2213
+ writeFileSync3(statePath, JSON.stringify(nextState, null, 2));
2214
+ return nextState;
2215
+ }
2216
+ function ensureProviderCommand(binDir, providerId) {
2217
+ mkdirSync3(binDir, { recursive: true });
2218
+ const scriptPath = join3(binDir, providerId);
2219
+ writeFileSync3(scriptPath, PROVIDER_COMMAND_SCRIPTS[providerId], "utf8");
2220
+ chmodSync(scriptPath, 493);
2221
+ }
2222
+ function appendDebugLog(path10, line) {
2223
+ if (!path10) {
2224
+ return;
2225
+ }
2226
+ mkdirSync3(dirname4(path10), { recursive: true });
2227
+ writeFileSync3(path10, `${line}
2228
+ `, { flag: "a" });
2229
+ }
2230
+ var PROVIDER_INSTALL_PACKAGES, PROVIDER_COMMAND_SCRIPTS;
2231
+ var init_e2e_provider_mock = __esm({
2232
+ "packages/server/src/provider-runtime/e2e-provider-mock.ts"() {
2233
+ "use strict";
2234
+ init_command_check();
2235
+ init_command_runner();
2236
+ PROVIDER_INSTALL_PACKAGES = {
2237
+ claude: "@anthropic-ai/claude-code",
2238
+ codex: "@openai/codex"
2239
+ };
2240
+ PROVIDER_COMMAND_SCRIPTS = {
2241
+ claude: `#!/usr/bin/env bash
2242
+ set -euo pipefail
2243
+ trap 'exit 0' TERM INT
2244
+ printf 'Mock Claude ready\\n'
2245
+ while true; do
2246
+ sleep 1
2247
+ done
2248
+ `,
2249
+ codex: `#!/usr/bin/env bash
2250
+ set -euo pipefail
2251
+ trap 'exit 0' TERM INT
2252
+ printf 'Session ID: abcdef-123456\\n> '
2253
+ while true; do
2254
+ sleep 1
2255
+ done
2256
+ `
2257
+ };
2258
+ }
2259
+ });
2260
+
2110
2261
  // packages/server/src/provider-runtime/install-manager.ts
2111
2262
  import { randomUUID as randomUUID2 } from "node:crypto";
2112
2263
  function getErrorDetails(error) {
@@ -3589,8 +3740,8 @@ var init_database = __esm({
3589
3740
 
3590
3741
  // packages/server/src/storage/db.ts
3591
3742
  import { DatabaseSync } from "node:sqlite";
3592
- import { readFileSync as readFileSync5 } from "fs";
3593
- import { join as join3 } from "path";
3743
+ import { readFileSync as readFileSync6 } from "fs";
3744
+ import { join as join4 } from "path";
3594
3745
  function normalizeSql(sql) {
3595
3746
  return (sql ?? "").replace(/\s+/g, " ").trim();
3596
3747
  }
@@ -3737,8 +3888,8 @@ var init_db = __esm({
3737
3888
  "packages/server/src/storage/db.ts"() {
3738
3889
  "use strict";
3739
3890
  init_database();
3740
- SCHEMA_PATH = join3(import.meta.dirname, "migrations", "001_init.sql");
3741
- SCHEMA_SQL = readFileSync5(SCHEMA_PATH, "utf-8");
3891
+ SCHEMA_PATH = join4(import.meta.dirname, "migrations", "001_init.sql");
3892
+ SCHEMA_SQL = readFileSync6(SCHEMA_PATH, "utf-8");
3742
3893
  LEGACY_TABLES = ["hook_registrations", "_migrations"];
3743
3894
  LEGACY_SESSION_COLUMNS = ["resume_id", "transcript_path"];
3744
3895
  EXPECTED_SCHEMA_ENTRIES = buildExpectedSchemaEntries();
@@ -4670,6 +4821,7 @@ function parseFetchUpdatedRefs(stderr) {
4670
4821
  }
4671
4822
  async function runGitCheckout(cwd, ref, options) {
4672
4823
  const args = ["checkout"];
4824
+ const formatCheckoutError = (error, fallbackMessage) => error instanceof GitError ? error.stderr.trim() || error.message || fallbackMessage : fallbackMessage;
4673
4825
  let isRemoteRef = false;
4674
4826
  try {
4675
4827
  const { stdout: remoteList } = await runGit(cwd, ["remote"]);
@@ -4681,15 +4833,22 @@ async function runGitCheckout(cwd, ref, options) {
4681
4833
  if (isRemoteRef && !options?.createBranch) {
4682
4834
  const remoteSeparatorIndex = ref.indexOf("/");
4683
4835
  const branchName = remoteSeparatorIndex >= 0 ? ref.slice(remoteSeparatorIndex + 1) : ref;
4684
- args.push("-b", branchName, ref);
4836
+ try {
4837
+ await runGit(cwd, ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`]);
4838
+ const { stdout, stderr } = await runGit(cwd, ["checkout", branchName]);
4839
+ const message = stdout || stderr || `Checkout to ${branchName} completed`;
4840
+ return { success: true, message, branch: branchName };
4841
+ } catch {
4842
+ args.push("-b", branchName, ref);
4843
+ }
4685
4844
  try {
4686
4845
  const { stdout, stderr } = await runGit(cwd, args);
4687
4846
  const message = stdout || stderr || `Checkout to ${ref} completed`;
4688
4847
  return { success: true, message, branch: branchName };
4689
- } catch {
4848
+ } catch (error) {
4690
4849
  return {
4691
4850
  success: false,
4692
- message: `Failed to checkout remote branch '${ref}'`
4851
+ message: formatCheckoutError(error, `Failed to checkout remote branch '${ref}'`)
4693
4852
  };
4694
4853
  }
4695
4854
  } else {
@@ -4703,10 +4862,10 @@ async function runGitCheckout(cwd, ref, options) {
4703
4862
  const branch = branchMatch?.[1] ?? ref;
4704
4863
  const message = stdout || stderr || `Checkout to ${ref} completed`;
4705
4864
  return { success: true, message, branch };
4706
- } catch {
4865
+ } catch (error) {
4707
4866
  return {
4708
4867
  success: false,
4709
- message: `Failed to checkout '${ref}'`
4868
+ message: formatCheckoutError(error, `Failed to checkout '${ref}'`)
4710
4869
  };
4711
4870
  }
4712
4871
  }
@@ -4721,23 +4880,42 @@ async function runGitCreateBranch(cwd, branchName, options) {
4721
4880
  }
4722
4881
  async function runGitListBranches(cwd) {
4723
4882
  const { stdout: localOutput } = await runGit(cwd, ["branch", "--list"]);
4883
+ const { stdout: localVerboseOutput } = await runGit(cwd, ["branch", "--list", "-vv"]);
4724
4884
  const { stdout: remoteOutput } = await runGit(cwd, ["branch", "-r"]);
4725
4885
  const branches = [];
4726
4886
  let current = "";
4887
+ const linkedWorktreePathsByBranch = /* @__PURE__ */ new Map();
4888
+ const localVerboseLines = localVerboseOutput.split("\n").filter((line) => line.trim());
4889
+ for (const line of localVerboseLines) {
4890
+ const normalizedLine = line.replace(/^[*+ ]\s+/, "");
4891
+ const branchMatch = normalizedLine.match(/^([^\s]+)\s+/);
4892
+ const worktreeMatch = line.match(/\((.+?)\)\s/);
4893
+ if (!branchMatch?.[1] || !worktreeMatch?.[1]) {
4894
+ continue;
4895
+ }
4896
+ const worktreePath = worktreeMatch[1];
4897
+ if (worktreePath.startsWith("/") || worktreePath.startsWith("~")) {
4898
+ linkedWorktreePathsByBranch.set(branchMatch[1], worktreePath);
4899
+ }
4900
+ }
4727
4901
  const localLines = localOutput.split("\n").filter((line) => line.trim());
4728
4902
  for (const line of localLines) {
4729
4903
  const isCurrent = line.startsWith("*");
4730
- const name = line.replace(/^\*?\s+/, "").trim();
4904
+ const name = line.replace(/^[*+ ]\s+/, "").trim();
4731
4905
  if (name.startsWith("(HEAD detached")) {
4732
4906
  if (isCurrent) {
4733
4907
  current = "";
4734
4908
  }
4735
4909
  continue;
4736
4910
  }
4911
+ if (linkedWorktreePathsByBranch.has(name) && !isCurrent) {
4912
+ continue;
4913
+ }
4737
4914
  branches.push({
4738
4915
  name,
4739
4916
  isRemote: false,
4740
- isCurrent
4917
+ isCurrent,
4918
+ linkedWorktreePath: linkedWorktreePathsByBranch.get(name)
4741
4919
  });
4742
4920
  if (isCurrent) {
4743
4921
  current = name;
@@ -5186,7 +5364,7 @@ var init_context_builder = __esm({
5186
5364
  });
5187
5365
 
5188
5366
  // packages/server/src/terminal/pty-host.ts
5189
- import { chmodSync, existsSync as existsSync6, statSync } from "node:fs";
5367
+ import { chmodSync as chmodSync2, existsSync as existsSync7, statSync } from "node:fs";
5190
5368
  import { createRequire } from "node:module";
5191
5369
  import path6 from "node:path";
5192
5370
  function ensureNodePtySpawnHelperExecutable(deps = {}) {
@@ -5196,9 +5374,9 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
5196
5374
  }
5197
5375
  const arch = deps.arch ?? process.arch;
5198
5376
  const resolve4 = deps.resolve ?? ((id) => require2.resolve(id));
5199
- const fileExists = deps.existsSync ?? existsSync6;
5377
+ const fileExists = deps.existsSync ?? existsSync7;
5200
5378
  const stat7 = deps.statSync ?? statSync;
5201
- const chmod = deps.chmodSync ?? chmodSync;
5379
+ const chmod = deps.chmodSync ?? chmodSync2;
5202
5380
  let packageJsonPath;
5203
5381
  try {
5204
5382
  packageJsonPath = resolve4(NODE_PTY_PKG);
@@ -7268,9 +7446,9 @@ var init_validator = __esm({
7268
7446
  });
7269
7447
 
7270
7448
  // packages/server/src/fs/gitignore.ts
7271
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
7449
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
7272
7450
  import ignore from "ignore";
7273
- import { join as join4, relative as relative3 } from "path";
7451
+ import { join as join5, relative as relative3 } from "path";
7274
7452
  function normalizePath(path10) {
7275
7453
  return path10.replace(/\\/g, "/");
7276
7454
  }
@@ -7290,26 +7468,26 @@ function isIgnoredByGitignore(ig, path10) {
7290
7468
  return ig.ignores(path10) || ig.ignores(`${path10}/`);
7291
7469
  }
7292
7470
  function createGitignoreFilter(rootPath, dirPath) {
7293
- const gitignorePath = join4(rootPath, ".gitignore");
7294
- if (!existsSync7(gitignorePath)) {
7471
+ const gitignorePath = join5(rootPath, ".gitignore");
7472
+ if (!existsSync8(gitignorePath)) {
7295
7473
  return (name) => !isDefaultTreeIgnored(name);
7296
7474
  }
7297
- const gitignoreContent = readFileSync6(gitignorePath, "utf-8");
7475
+ const gitignoreContent = readFileSync7(gitignorePath, "utf-8");
7298
7476
  const ig = ignore().add(gitignoreContent);
7299
7477
  return (name) => {
7300
7478
  if (isAlwaysTreeIgnored(name)) {
7301
7479
  return false;
7302
7480
  }
7303
- const relativePath = relativeToRoot(rootPath, join4(dirPath, name));
7481
+ const relativePath = relativeToRoot(rootPath, join5(dirPath, name));
7304
7482
  return !isIgnoredByGitignore(ig, relativePath);
7305
7483
  };
7306
7484
  }
7307
7485
  function createWatcherIgnoreFilter(rootPath) {
7308
- const gitignorePath = join4(rootPath, ".gitignore");
7309
- if (!existsSync7(gitignorePath)) {
7486
+ const gitignorePath = join5(rootPath, ".gitignore");
7487
+ if (!existsSync8(gitignorePath)) {
7310
7488
  return (path10) => DEFAULT_WATCHER_IGNORED_PATTERNS.some((p) => p.test(normalizePath(path10)));
7311
7489
  }
7312
- const gitignoreContent = readFileSync6(gitignorePath, "utf-8");
7490
+ const gitignoreContent = readFileSync7(gitignorePath, "utf-8");
7313
7491
  const ig = ignore().add(gitignoreContent);
7314
7492
  return (path10) => {
7315
7493
  const normalizedPath = normalizePath(path10);
@@ -7436,6 +7614,11 @@ var init_manager4 = __esm({
7436
7614
  new WorkspaceWatcher(workspaceId, rootPath, this.deps.broadcaster)
7437
7615
  );
7438
7616
  }
7617
+ hydrateWatchers() {
7618
+ for (const workspace of this.list()) {
7619
+ this.startWatcher(workspace.id, workspace.path);
7620
+ }
7621
+ }
7439
7622
  updateUiState(workspaceId, uiState) {
7440
7623
  const workspace = this.get(workspaceId);
7441
7624
  if (!workspace) {
@@ -8911,7 +9094,7 @@ var init_hub = __esm({
8911
9094
  // packages/server/src/commands/workspace.ts
8912
9095
  import { readdir as readdir2 } from "node:fs/promises";
8913
9096
  import { homedir as homedir3 } from "node:os";
8914
- import { join as join5 } from "node:path";
9097
+ import { join as join6 } from "node:path";
8915
9098
  import { z as z6 } from "zod";
8916
9099
  var init_workspace = __esm({
8917
9100
  "packages/server/src/commands/workspace.ts"() {
@@ -8930,11 +9113,11 @@ var init_workspace = __esm({
8930
9113
  const entries = await readdir2(basePath, { withFileTypes: true });
8931
9114
  const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
8932
9115
  name: entry.name,
8933
- path: join5(basePath, entry.name)
9116
+ path: join6(basePath, entry.name)
8934
9117
  })).sort((a, b) => a.name.localeCompare(b.name));
8935
9118
  return {
8936
9119
  currentPath: basePath,
8937
- parentPath: basePath !== "/" ? join5(basePath, "..") : null,
9120
+ parentPath: basePath !== "/" ? join6(basePath, "..") : null,
8938
9121
  directories
8939
9122
  };
8940
9123
  }
@@ -9207,9 +9390,9 @@ var init_session = __esm({
9207
9390
 
9208
9391
  // packages/server/src/fs/tree.ts
9209
9392
  import { readdir as readdir3, stat as stat6 } from "fs/promises";
9210
- import { join as join6, relative as relative4 } from "path";
9393
+ import { join as join7, relative as relative4 } from "path";
9211
9394
  async function readTree(rootPath, subdir) {
9212
- const targetPath = subdir ? join6(rootPath, subdir) : rootPath;
9395
+ const targetPath = subdir ? join7(rootPath, subdir) : rootPath;
9213
9396
  const filter = createGitignoreFilter(rootPath, targetPath);
9214
9397
  const entries = await readdir3(targetPath, { withFileTypes: true });
9215
9398
  const nodes = [];
@@ -9217,7 +9400,7 @@ async function readTree(rootPath, subdir) {
9217
9400
  if (!filter(entry.name)) {
9218
9401
  continue;
9219
9402
  }
9220
- const fullPath = join6(targetPath, entry.name);
9403
+ const fullPath = join7(targetPath, entry.name);
9221
9404
  const relPath = relative4(rootPath, fullPath);
9222
9405
  if (entry.isDirectory()) {
9223
9406
  nodes.push({
@@ -9261,7 +9444,7 @@ async function searchFiles(rootPath, query, limit = 10) {
9261
9444
  const filteredEntries = entries.filter((entry) => filter(entry.name));
9262
9445
  filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
9263
9446
  for (const entry of filteredEntries) {
9264
- const fullPath = join6(dirPath, entry.name);
9447
+ const fullPath = join7(dirPath, entry.name);
9265
9448
  const relPath = relative4(rootPath, fullPath);
9266
9449
  if (entry.isDirectory()) {
9267
9450
  await walk(fullPath);
@@ -9873,37 +10056,37 @@ var init_git2 = __esm({
9873
10056
  });
9874
10057
 
9875
10058
  // packages/server/src/config/config-io.ts
9876
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
10059
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync, writeFileSync as writeFileSync4 } from "node:fs";
9877
10060
  import { homedir as homedir4 } from "node:os";
9878
- import { basename as basename3, dirname as dirname4, join as join7 } from "node:path";
10061
+ import { basename as basename3, dirname as dirname5, join as join8 } from "node:path";
9879
10062
  function resolveConfigPath(configType) {
9880
10063
  if (configType === "codex") {
9881
10064
  const testHome = process.env.CODER_STUDIO_CODEX_HOME;
9882
10065
  if (testHome && testHome.trim()) {
9883
- return join7(testHome, "config.toml");
10066
+ return join8(testHome, "config.toml");
9884
10067
  }
9885
10068
  const codexHome = process.env.CODEX_HOME;
9886
10069
  if (codexHome && codexHome.trim()) {
9887
- return join7(codexHome, "config.toml");
10070
+ return join8(codexHome, "config.toml");
9888
10071
  }
9889
- return join7(homedir4(), ".codex", "config.toml");
10072
+ return join8(homedir4(), ".codex", "config.toml");
9890
10073
  }
9891
10074
  if (configType === "claude") {
9892
10075
  const testHome = process.env.CODER_STUDIO_CLAUDE_HOME;
9893
10076
  if (testHome && testHome.trim()) {
9894
- return join7(testHome, "settings.json");
10077
+ return join8(testHome, "settings.json");
9895
10078
  }
9896
- return join7(homedir4(), ".claude", "settings.json");
10079
+ return join8(homedir4(), ".claude", "settings.json");
9897
10080
  }
9898
10081
  throw new Error(`Unknown config type: ${configType}`);
9899
10082
  }
9900
10083
  function readConfigFile(configType) {
9901
10084
  const configPath = resolveConfigPath(configType);
9902
- if (!existsSync8(configPath)) {
10085
+ if (!existsSync9(configPath)) {
9903
10086
  return { configPath, content: "", exists: false };
9904
10087
  }
9905
10088
  try {
9906
- const content = readFileSync7(configPath, "utf-8");
10089
+ const content = readFileSync8(configPath, "utf-8");
9907
10090
  return { configPath, content, exists: true };
9908
10091
  } catch {
9909
10092
  return { configPath, content: "", exists: false };
@@ -9912,16 +10095,16 @@ function readConfigFile(configType) {
9912
10095
  function writeConfigFile(configType, content) {
9913
10096
  try {
9914
10097
  const configPath = resolveConfigPath(configType);
9915
- const parentDir = dirname4(configPath);
9916
- if (!existsSync8(parentDir)) {
9917
- mkdirSync3(parentDir, { recursive: true });
10098
+ const parentDir = dirname5(configPath);
10099
+ if (!existsSync9(parentDir)) {
10100
+ mkdirSync4(parentDir, { recursive: true });
9918
10101
  }
9919
10102
  let backupPath = null;
9920
- if (existsSync8(configPath)) {
10103
+ if (existsSync9(configPath)) {
9921
10104
  backupPath = createBackup(configPath);
9922
10105
  }
9923
10106
  const tempPath = `${configPath}.tmp`;
9924
- writeFileSync3(tempPath, content, "utf-8");
10107
+ writeFileSync4(tempPath, content, "utf-8");
9925
10108
  renameSync(tempPath, configPath);
9926
10109
  return { success: true, backupPath };
9927
10110
  } catch (error) {
@@ -9933,13 +10116,13 @@ function writeConfigFile(configType, content) {
9933
10116
  }
9934
10117
  }
9935
10118
  function createBackup(filePath) {
9936
- const original = readFileSync7(filePath, "utf-8");
10119
+ const original = readFileSync8(filePath, "utf-8");
9937
10120
  const ext = filePath.split(".").pop() ?? "";
9938
10121
  const base = basename3(filePath, `.${ext}`);
9939
- const dir = dirname4(filePath);
10122
+ const dir = dirname5(filePath);
9940
10123
  const ts = formatTimestamp(/* @__PURE__ */ new Date());
9941
- const backupPath = join7(dir, `${base}.bak.${ts}.${ext}`);
9942
- writeFileSync3(backupPath, original, "utf-8");
10124
+ const backupPath = join8(dir, `${base}.bak.${ts}.${ext}`);
10125
+ writeFileSync4(backupPath, original, "utf-8");
9943
10126
  return backupPath;
9944
10127
  }
9945
10128
  function formatTimestamp(d) {
@@ -10643,6 +10826,7 @@ async function createServer(configOverrides) {
10643
10826
  (err) => console.warn("[uploads] cascade cleanup failed", { wsId: workspaceId, err })
10644
10827
  )
10645
10828
  });
10829
+ workspaceMgr.hydrateWatchers();
10646
10830
  const authSessionRepo = new AuthSessionRepo(db);
10647
10831
  const authLoginBlockRepo = new AuthLoginBlockRepo(db);
10648
10832
  const app = await buildFastifyApp({
@@ -10682,10 +10866,13 @@ async function createServer(configOverrides) {
10682
10866
  });
10683
10867
  await sessionMgr.hydrate();
10684
10868
  await supervisorMgr.hydrate();
10685
- const providerRuntimeDeps = {};
10869
+ const providerMockOverrides = createE2EProviderMockOverrides();
10870
+ const providerRuntimeDeps = providerMockOverrides ? {
10871
+ commandExists: providerMockOverrides.commandExists
10872
+ } : {};
10686
10873
  const providerInstallMgr = new ProviderInstallManager(providerRegistry, {
10687
10874
  ...providerRuntimeDeps,
10688
- runCommand: runCommandAsString
10875
+ runCommand: providerMockOverrides?.runCommand ?? runCommandAsString
10689
10876
  });
10690
10877
  commandContext = {
10691
10878
  workspaceMgr,
@@ -10855,6 +11042,7 @@ var init_server = __esm({
10855
11042
  init_config();
10856
11043
  init_auto_fetch();
10857
11044
  init_command_runner();
11045
+ init_e2e_provider_mock();
10858
11046
  init_install_manager();
10859
11047
  init_manager();
10860
11048
  init_db();