visual-remote 0.1.3 → 0.2.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 +47 -53
- package/apps/cli/dist/index.js +253 -74
- package/apps/cli/dist/vite.js +5007 -0
- package/apps/cli/vite.d.ts +11 -0
- package/package.json +19 -2
package/apps/cli/dist/index.js
CHANGED
|
@@ -1439,8 +1439,8 @@ var CodexAdapter = class {
|
|
|
1439
1439
|
};
|
|
1440
1440
|
|
|
1441
1441
|
// ../../packages/bridge-core/src/config/loader.ts
|
|
1442
|
-
import { readFile, realpath } from "node:fs/promises";
|
|
1443
|
-
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
1442
|
+
import { readFile, realpath, stat as stat2 } from "node:fs/promises";
|
|
1443
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
1444
1444
|
import { parse as parseYaml } from "yaml";
|
|
1445
1445
|
import { ZodError } from "zod";
|
|
1446
1446
|
|
|
@@ -1627,11 +1627,42 @@ function isWithinRoot(root, candidate) {
|
|
|
1627
1627
|
const pathFromRoot = relative(root, candidate);
|
|
1628
1628
|
return pathFromRoot === "" || !pathFromRoot.startsWith(`..${sep}`) && pathFromRoot !== "..";
|
|
1629
1629
|
}
|
|
1630
|
+
async function configExists(root) {
|
|
1631
|
+
try {
|
|
1632
|
+
await stat2(join(root, CONFIG_PATH));
|
|
1633
|
+
return true;
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
1636
|
+
return false;
|
|
1637
|
+
}
|
|
1638
|
+
throw error;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
async function discoverVisualDevConfigRoot(startDirectory, repositoryRoot) {
|
|
1642
|
+
const repoRoot = await realpath(repositoryRoot);
|
|
1643
|
+
let cursor = await realpath(startDirectory);
|
|
1644
|
+
if (!isWithinRoot(repoRoot, cursor)) {
|
|
1645
|
+
throw new VisualDevConfigError(`Project directory is outside Git worktree: ${cursor}`);
|
|
1646
|
+
}
|
|
1647
|
+
while (true) {
|
|
1648
|
+
if (await configExists(cursor)) return cursor;
|
|
1649
|
+
if (cursor === repoRoot) return repoRoot;
|
|
1650
|
+
const parent = dirname(cursor);
|
|
1651
|
+
if (parent === cursor || !isWithinRoot(repoRoot, parent)) return repoRoot;
|
|
1652
|
+
cursor = parent;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1630
1655
|
async function loadVisualDevConfig(repositoryRoot, options = {}) {
|
|
1631
1656
|
const repoRoot = await realpath(repositoryRoot);
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
|
|
1657
|
+
const configRoot = await realpath(options.configRoot ?? repoRoot);
|
|
1658
|
+
if (!isWithinRoot(repoRoot, configRoot)) {
|
|
1659
|
+
throw new VisualDevConfigError(
|
|
1660
|
+
`Config directory is outside Git worktree: ${configRoot}`
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
const configPath = join(configRoot, CONFIG_PATH);
|
|
1664
|
+
const localConfigPath = join(configRoot, LOCAL_CONFIG_PATH);
|
|
1665
|
+
const projectId = basename(configRoot);
|
|
1635
1666
|
const baseDocument = await readYamlMapping(configPath, options.requireConfig ?? false);
|
|
1636
1667
|
const localDocument = await readYamlMapping(localConfigPath, false);
|
|
1637
1668
|
const loadedFiles = [];
|
|
@@ -1658,7 +1689,7 @@ async function loadVisualDevConfig(repositoryRoot, options = {}) {
|
|
|
1658
1689
|
filePath: localDocument === void 0 ? configPath : localConfigPath
|
|
1659
1690
|
});
|
|
1660
1691
|
}
|
|
1661
|
-
const unresolvedWorkspace = isAbsolute(config.project.workspace) ? config.project.workspace : resolve(
|
|
1692
|
+
const unresolvedWorkspace = isAbsolute(config.project.workspace) ? config.project.workspace : resolve(configRoot, config.project.workspace);
|
|
1662
1693
|
let workspaceRoot;
|
|
1663
1694
|
try {
|
|
1664
1695
|
workspaceRoot = await realpath(unresolvedWorkspace);
|
|
@@ -1677,6 +1708,7 @@ async function loadVisualDevConfig(repositoryRoot, options = {}) {
|
|
|
1677
1708
|
return {
|
|
1678
1709
|
config,
|
|
1679
1710
|
repoRoot,
|
|
1711
|
+
configRoot,
|
|
1680
1712
|
workspaceRoot,
|
|
1681
1713
|
configPath,
|
|
1682
1714
|
localConfigPath,
|
|
@@ -1964,7 +1996,7 @@ import {
|
|
|
1964
1996
|
rm
|
|
1965
1997
|
} from "node:fs/promises";
|
|
1966
1998
|
import { tmpdir } from "node:os";
|
|
1967
|
-
import { dirname, relative as relative3, resolve as resolve4, sep as sep3 } from "node:path";
|
|
1999
|
+
import { dirname as dirname2, relative as relative3, resolve as resolve4, sep as sep3 } from "node:path";
|
|
1968
2000
|
var SNAPSHOT_ENV = {
|
|
1969
2001
|
GIT_AUTHOR_NAME: "Visual Bridge",
|
|
1970
2002
|
GIT_AUTHOR_EMAIL: "visual-bridge@localhost",
|
|
@@ -2317,7 +2349,7 @@ var GitTransactionManager = class _GitTransactionManager {
|
|
|
2317
2349
|
} catch (error) {
|
|
2318
2350
|
if (error.code !== "ENOENT") throw error;
|
|
2319
2351
|
}
|
|
2320
|
-
await this.#removeEmptyParents(
|
|
2352
|
+
await this.#removeEmptyParents(dirname2(absolute));
|
|
2321
2353
|
}
|
|
2322
2354
|
async #removeEmptyParents(start) {
|
|
2323
2355
|
let cursor = start;
|
|
@@ -2327,13 +2359,13 @@ var GitTransactionManager = class _GitTransactionManager {
|
|
|
2327
2359
|
} catch (error) {
|
|
2328
2360
|
const code = error.code;
|
|
2329
2361
|
if (code === "ENOENT") {
|
|
2330
|
-
cursor =
|
|
2362
|
+
cursor = dirname2(cursor);
|
|
2331
2363
|
continue;
|
|
2332
2364
|
}
|
|
2333
2365
|
if (code === "ENOTEMPTY" || code === "EEXIST") return;
|
|
2334
2366
|
throw error;
|
|
2335
2367
|
}
|
|
2336
|
-
cursor =
|
|
2368
|
+
cursor = dirname2(cursor);
|
|
2337
2369
|
}
|
|
2338
2370
|
}
|
|
2339
2371
|
async #assertSnapshotRef(ref) {
|
|
@@ -2353,7 +2385,7 @@ var GitTransactionManager = class _GitTransactionManager {
|
|
|
2353
2385
|
// ../../packages/bridge-core/src/storage/sqlite-task-store.ts
|
|
2354
2386
|
import { mkdirSync } from "node:fs";
|
|
2355
2387
|
import { createRequire } from "node:module";
|
|
2356
|
-
import { dirname as
|
|
2388
|
+
import { dirname as dirname3 } from "node:path";
|
|
2357
2389
|
var requireNodeBuiltin = createRequire(import.meta.url);
|
|
2358
2390
|
var optional = (value) => value ?? void 0;
|
|
2359
2391
|
function parseJson(value, fallback) {
|
|
@@ -2445,7 +2477,7 @@ function taskParams(task) {
|
|
|
2445
2477
|
var SqliteTaskStore = class {
|
|
2446
2478
|
#db;
|
|
2447
2479
|
constructor(filename) {
|
|
2448
|
-
if (filename !== ":memory:") mkdirSync(
|
|
2480
|
+
if (filename !== ":memory:") mkdirSync(dirname3(filename), { recursive: true, mode: 448 });
|
|
2449
2481
|
const { DatabaseSync } = requireNodeBuiltin("node:sqlite");
|
|
2450
2482
|
this.#db = new DatabaseSync(filename);
|
|
2451
2483
|
this.#db.exec(`
|
|
@@ -4589,7 +4621,9 @@ ${output2}` : ""}`
|
|
|
4589
4621
|
|
|
4590
4622
|
// ../../packages/bridge-core/src/bridge/default-control-service.ts
|
|
4591
4623
|
async function createDefaultControlService(context, environment = process.env) {
|
|
4592
|
-
const loaded = await loadVisualDevConfig(context.repoRoot
|
|
4624
|
+
const loaded = await loadVisualDevConfig(context.repoRoot, {
|
|
4625
|
+
...context.configRoot === void 0 ? {} : { configRoot: context.configRoot }
|
|
4626
|
+
});
|
|
4593
4627
|
if (loaded.config.agent.adapter !== "codex") {
|
|
4594
4628
|
throw new Error(
|
|
4595
4629
|
`Agent adapter ${loaded.config.agent.adapter} is not implemented in this MVP build`
|
|
@@ -4882,6 +4916,7 @@ async function startBridgeCore(options, dependencies) {
|
|
|
4882
4916
|
mode: options.mode,
|
|
4883
4917
|
projectId: loadedConfig.config.project.id,
|
|
4884
4918
|
repoRoot: loadedConfig.repoRoot,
|
|
4919
|
+
configRoot: loadedConfig.configRoot,
|
|
4885
4920
|
workspaceRoot: loadedConfig.workspaceRoot,
|
|
4886
4921
|
upstreamUrl: options.upstreamUrl
|
|
4887
4922
|
};
|
|
@@ -4951,8 +4986,10 @@ async function startBridgeCore(options, dependencies) {
|
|
|
4951
4986
|
}
|
|
4952
4987
|
}
|
|
4953
4988
|
async function startAttachBridge(options, dependencies = {}) {
|
|
4954
|
-
const
|
|
4955
|
-
const
|
|
4989
|
+
const cwd = dependencies.cwd ?? process.cwd();
|
|
4990
|
+
const repoRoot = await discoverGitWorktreeRoot(cwd);
|
|
4991
|
+
const configRoot = await discoverVisualDevConfigRoot(cwd, repoRoot);
|
|
4992
|
+
const loadedConfig = await loadVisualDevConfig(repoRoot, { configRoot });
|
|
4956
4993
|
return await startBridgeCore(
|
|
4957
4994
|
{
|
|
4958
4995
|
mode: "attach",
|
|
@@ -4966,8 +5003,13 @@ async function startAttachBridge(options, dependencies = {}) {
|
|
|
4966
5003
|
);
|
|
4967
5004
|
}
|
|
4968
5005
|
async function startManagedBridge(options = {}, dependencies = {}) {
|
|
4969
|
-
const
|
|
4970
|
-
const
|
|
5006
|
+
const cwd = dependencies.cwd ?? process.cwd();
|
|
5007
|
+
const repoRoot = await discoverGitWorktreeRoot(cwd);
|
|
5008
|
+
const configRoot = await discoverVisualDevConfigRoot(cwd, repoRoot);
|
|
5009
|
+
const loadedConfig = await loadVisualDevConfig(repoRoot, {
|
|
5010
|
+
requireConfig: true,
|
|
5011
|
+
configRoot
|
|
5012
|
+
});
|
|
4971
5013
|
const command = loadedConfig.config.upstream.command;
|
|
4972
5014
|
if (command === void 0) {
|
|
4973
5015
|
throw new Error("visual dev requires upstream.command in .visualdev/config.yaml");
|
|
@@ -5076,14 +5118,14 @@ function formatBridgeSummary(bridge) {
|
|
|
5076
5118
|
|
|
5077
5119
|
// src/doctor.ts
|
|
5078
5120
|
import { constants } from "node:fs";
|
|
5079
|
-
import { access as access2, stat as
|
|
5080
|
-
import { delimiter, isAbsolute as isAbsolute5, join as join4, resolve as resolve7 } from "node:path";
|
|
5121
|
+
import { access as access2, stat as stat3 } from "node:fs/promises";
|
|
5122
|
+
import { delimiter, isAbsolute as isAbsolute5, join as join4, relative as relative6, resolve as resolve7 } from "node:path";
|
|
5081
5123
|
import { execFile as execFile2 } from "node:child_process";
|
|
5082
5124
|
import { promisify as promisify2 } from "node:util";
|
|
5083
5125
|
var execFileAsync2 = promisify2(execFile2);
|
|
5084
5126
|
async function fileExists(path) {
|
|
5085
5127
|
try {
|
|
5086
|
-
await
|
|
5128
|
+
await stat3(path);
|
|
5087
5129
|
return true;
|
|
5088
5130
|
} catch (error) {
|
|
5089
5131
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
@@ -5131,14 +5173,19 @@ async function runDoctor(dependencies = {}) {
|
|
|
5131
5173
|
return checks;
|
|
5132
5174
|
}
|
|
5133
5175
|
try {
|
|
5134
|
-
const
|
|
5176
|
+
const configRoot = await discoverVisualDevConfigRoot(
|
|
5177
|
+
dependencies.cwd ?? process.cwd(),
|
|
5178
|
+
repoRoot
|
|
5179
|
+
);
|
|
5180
|
+
const loaded = await loadVisualDevConfig(repoRoot, { configRoot });
|
|
5135
5181
|
checks.push({
|
|
5136
5182
|
name: "config",
|
|
5137
5183
|
status: loaded.loadedFiles.length === 0 ? "warning" : "pass",
|
|
5138
5184
|
message: loaded.loadedFiles.length === 0 ? "No .visualdev/config.yaml; attach defaults are available." : loaded.loadedFiles.join(", ")
|
|
5139
5185
|
});
|
|
5140
5186
|
if (await fileExists(loaded.localConfigPath)) {
|
|
5141
|
-
const
|
|
5187
|
+
const localConfigRelativePath = relative6(repoRoot, loaded.localConfigPath);
|
|
5188
|
+
const ignored = await isIgnored(repoRoot, localConfigRelativePath);
|
|
5142
5189
|
checks.push({
|
|
5143
5190
|
name: "local-config-ignore",
|
|
5144
5191
|
status: ignored ? "pass" : "warning",
|
|
@@ -5176,15 +5223,23 @@ function formatDoctorChecks(checks) {
|
|
|
5176
5223
|
}
|
|
5177
5224
|
|
|
5178
5225
|
// src/init.ts
|
|
5179
|
-
import {
|
|
5180
|
-
import {
|
|
5226
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
5227
|
+
import { readFile as readFile5, mkdir as mkdir3, realpath as realpath10, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
5228
|
+
import { basename as basename2, dirname as dirname4, join as join5, relative as relative7 } from "node:path";
|
|
5181
5229
|
import { stringify as stringifyYaml } from "yaml";
|
|
5230
|
+
var VITE_CONFIG_FILES = [
|
|
5231
|
+
"vite.config.ts",
|
|
5232
|
+
"vite.config.mts",
|
|
5233
|
+
"vite.config.js",
|
|
5234
|
+
"vite.config.mjs"
|
|
5235
|
+
];
|
|
5236
|
+
var VITE_IMPORT = 'import { visualRemote } from "visual-remote/vite";';
|
|
5182
5237
|
function isMissingFile(error) {
|
|
5183
5238
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5184
5239
|
}
|
|
5185
5240
|
async function fileExists2(path) {
|
|
5186
5241
|
try {
|
|
5187
|
-
await
|
|
5242
|
+
await stat4(path);
|
|
5188
5243
|
return true;
|
|
5189
5244
|
} catch (error) {
|
|
5190
5245
|
if (isMissingFile(error)) return false;
|
|
@@ -5196,7 +5251,7 @@ function packageManagerFromField(value) {
|
|
|
5196
5251
|
const name = value.split("@", 1)[0];
|
|
5197
5252
|
return name === "bun" || name === "npm" || name === "pnpm" || name === "yarn" ? name : void 0;
|
|
5198
5253
|
}
|
|
5199
|
-
async function detectPackageManager(repoRoot, manifest) {
|
|
5254
|
+
async function detectPackageManager(projectRoot, repoRoot, manifest) {
|
|
5200
5255
|
const declared = packageManagerFromField(manifest.packageManager);
|
|
5201
5256
|
if (declared !== void 0) return declared;
|
|
5202
5257
|
const lockfiles = [
|
|
@@ -5206,8 +5261,10 @@ async function detectPackageManager(repoRoot, manifest) {
|
|
|
5206
5261
|
["bun", "bun.lockb"],
|
|
5207
5262
|
["npm", "package-lock.json"]
|
|
5208
5263
|
];
|
|
5209
|
-
for (const [
|
|
5210
|
-
|
|
5264
|
+
for (const root of projectRoot === repoRoot ? [projectRoot] : [projectRoot, repoRoot]) {
|
|
5265
|
+
for (const [manager, filename] of lockfiles) {
|
|
5266
|
+
if (await fileExists2(join5(root, filename))) return manager;
|
|
5267
|
+
}
|
|
5211
5268
|
}
|
|
5212
5269
|
return "npm";
|
|
5213
5270
|
}
|
|
@@ -5217,6 +5274,19 @@ function readDevScript(manifest) {
|
|
|
5217
5274
|
}
|
|
5218
5275
|
return manifest.scripts.dev.trim();
|
|
5219
5276
|
}
|
|
5277
|
+
function packageRecord(value) {
|
|
5278
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
5279
|
+
}
|
|
5280
|
+
function isViteProject(manifest, devScript) {
|
|
5281
|
+
const packages = {
|
|
5282
|
+
...packageRecord(manifest.dependencies),
|
|
5283
|
+
...packageRecord(manifest.devDependencies)
|
|
5284
|
+
};
|
|
5285
|
+
return "vite" in packages || /(^|[\s;&|])vite(?:\s|$)/.test(devScript);
|
|
5286
|
+
}
|
|
5287
|
+
function hasVisualRemote(manifest) {
|
|
5288
|
+
return "visual-remote" in packageRecord(manifest.dependencies) || "visual-remote" in packageRecord(manifest.devDependencies);
|
|
5289
|
+
}
|
|
5220
5290
|
function devCommand(manager, script) {
|
|
5221
5291
|
const command = manager === "pnpm" ? ["corepack", "pnpm", "run", "dev"] : manager === "yarn" ? ["corepack", "yarn", "run", "dev"] : manager === "bun" ? ["bun", "run", "dev"] : ["npm", "run", "dev"];
|
|
5222
5292
|
const next = /(^|[\s;&|])next(?:\s|$)/.test(script);
|
|
@@ -5229,14 +5299,14 @@ function devCommand(manager, script) {
|
|
|
5229
5299
|
"{upstreamPort}"
|
|
5230
5300
|
];
|
|
5231
5301
|
}
|
|
5232
|
-
async function readManifest(
|
|
5233
|
-
const manifestPath = join5(
|
|
5302
|
+
async function readManifest(projectRoot) {
|
|
5303
|
+
const manifestPath = join5(projectRoot, "package.json");
|
|
5234
5304
|
let source;
|
|
5235
5305
|
try {
|
|
5236
5306
|
source = await readFile5(manifestPath, "utf8");
|
|
5237
5307
|
} catch (error) {
|
|
5238
5308
|
if (isMissingFile(error)) {
|
|
5239
|
-
throw new Error("visual init requires package.json
|
|
5309
|
+
throw new Error("visual init requires package.json in the current directory");
|
|
5240
5310
|
}
|
|
5241
5311
|
throw error;
|
|
5242
5312
|
}
|
|
@@ -5251,55 +5321,163 @@ async function readManifest(repoRoot) {
|
|
|
5251
5321
|
}
|
|
5252
5322
|
return value;
|
|
5253
5323
|
}
|
|
5254
|
-
async function
|
|
5255
|
-
const
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
return { created: false, configPath };
|
|
5324
|
+
async function findViteConfig(projectRoot) {
|
|
5325
|
+
for (const filename of VITE_CONFIG_FILES) {
|
|
5326
|
+
const candidate = join5(projectRoot, filename);
|
|
5327
|
+
if (await fileExists2(candidate)) return candidate;
|
|
5259
5328
|
}
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5329
|
+
throw new Error(
|
|
5330
|
+
"visual init found Vite but could not find vite.config.ts, .mts, .js, or .mjs"
|
|
5331
|
+
);
|
|
5332
|
+
}
|
|
5333
|
+
function insertViteImport(source) {
|
|
5334
|
+
const lines = source.split("\n");
|
|
5335
|
+
let index = 0;
|
|
5336
|
+
while (lines[index]?.startsWith("///")) index += 1;
|
|
5337
|
+
lines.splice(index, 0, VITE_IMPORT);
|
|
5338
|
+
return lines.join("\n");
|
|
5339
|
+
}
|
|
5340
|
+
function transformViteConfig(source) {
|
|
5341
|
+
if (source.includes("visual-remote/vite") && /\bvisualRemote\s*\(/.test(source)) {
|
|
5342
|
+
return source;
|
|
5343
|
+
}
|
|
5344
|
+
let transformed = source.includes("visual-remote/vite") ? source : insertViteImport(source);
|
|
5345
|
+
const pluginsPattern = /(\bplugins\s*:\s*\[)/;
|
|
5346
|
+
if (pluginsPattern.test(transformed)) {
|
|
5347
|
+
return transformed.replace(
|
|
5348
|
+
pluginsPattern,
|
|
5349
|
+
(match, _prefix, offset, fullSource) => `${match}visualRemote(),${fullSource[offset + match.length] === "\n" ? "" : " "}`
|
|
5350
|
+
);
|
|
5351
|
+
}
|
|
5352
|
+
const objectConfigPattern = /(defineConfig\s*\(\s*\{)/;
|
|
5353
|
+
if (objectConfigPattern.test(transformed)) {
|
|
5354
|
+
return transformed.replace(
|
|
5355
|
+
objectConfigPattern,
|
|
5356
|
+
"$1\n plugins: [visualRemote()],"
|
|
5357
|
+
);
|
|
5358
|
+
}
|
|
5359
|
+
throw new Error(
|
|
5360
|
+
"visual init could not add visualRemote() to the Vite plugins array"
|
|
5361
|
+
);
|
|
5362
|
+
}
|
|
5363
|
+
async function configureVite(configPath) {
|
|
5364
|
+
const source = await readFile5(configPath, "utf8");
|
|
5365
|
+
const transformed = transformViteConfig(source);
|
|
5366
|
+
if (transformed === source) return false;
|
|
5367
|
+
await writeFile3(configPath, transformed, "utf8");
|
|
5368
|
+
return true;
|
|
5369
|
+
}
|
|
5370
|
+
function installCommand(request) {
|
|
5371
|
+
if (request.packageManager === "pnpm") {
|
|
5372
|
+
return {
|
|
5373
|
+
command: "corepack",
|
|
5374
|
+
args: ["pnpm", "add", "--save-dev", request.packageSpec]
|
|
5375
|
+
};
|
|
5376
|
+
}
|
|
5377
|
+
if (request.packageManager === "yarn") {
|
|
5378
|
+
return {
|
|
5379
|
+
command: "corepack",
|
|
5380
|
+
args: ["yarn", "add", "--dev", request.packageSpec]
|
|
5381
|
+
};
|
|
5382
|
+
}
|
|
5383
|
+
if (request.packageManager === "bun") {
|
|
5384
|
+
return { command: "bun", args: ["add", "--dev", request.packageSpec] };
|
|
5385
|
+
}
|
|
5386
|
+
return {
|
|
5387
|
+
command: "npm",
|
|
5388
|
+
args: ["install", "--save-dev", request.packageSpec]
|
|
5277
5389
|
};
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5390
|
+
}
|
|
5391
|
+
async function installPackage(request) {
|
|
5392
|
+
const { command, args } = installCommand(request);
|
|
5393
|
+
await new Promise((resolvePromise, reject) => {
|
|
5394
|
+
const child = spawn6(command, args, {
|
|
5395
|
+
cwd: request.cwd,
|
|
5396
|
+
env: process.env,
|
|
5397
|
+
stdio: "inherit",
|
|
5398
|
+
windowsHide: true
|
|
5283
5399
|
});
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5400
|
+
child.once("error", reject);
|
|
5401
|
+
child.once("exit", (code, signal) => {
|
|
5402
|
+
if (code === 0) {
|
|
5403
|
+
resolvePromise();
|
|
5404
|
+
return;
|
|
5405
|
+
}
|
|
5406
|
+
reject(
|
|
5407
|
+
new Error(
|
|
5408
|
+
`${command} ${args.join(" ")} failed${signal === null ? ` with exit code ${code ?? "unknown"}` : ` with ${signal}`}`
|
|
5409
|
+
)
|
|
5410
|
+
);
|
|
5411
|
+
});
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5414
|
+
async function initializeVisualDev(dependencies = {}) {
|
|
5415
|
+
const projectRoot = await realpath10(dependencies.cwd ?? process.cwd());
|
|
5416
|
+
const repoRoot = await discoverGitWorktreeRoot(projectRoot);
|
|
5417
|
+
const manifest = await readManifest(projectRoot);
|
|
5418
|
+
const devScript = readDevScript(manifest);
|
|
5419
|
+
if (!isViteProject(manifest, devScript)) {
|
|
5420
|
+
throw new Error("visual init currently supports Vite projects");
|
|
5421
|
+
}
|
|
5422
|
+
const packageManager = await detectPackageManager(projectRoot, repoRoot, manifest);
|
|
5423
|
+
const integrationPath = await findViteConfig(projectRoot);
|
|
5424
|
+
const packageInstalled = !hasVisualRemote(manifest);
|
|
5425
|
+
if (packageInstalled) {
|
|
5426
|
+
await (dependencies.installPackage ?? installPackage)({
|
|
5427
|
+
cwd: projectRoot,
|
|
5428
|
+
packageManager,
|
|
5429
|
+
packageSpec: "visual-remote@latest"
|
|
5430
|
+
});
|
|
5431
|
+
}
|
|
5432
|
+
const integrationChanged = await configureVite(integrationPath);
|
|
5433
|
+
const configPath = join5(projectRoot, CONFIG_PATH);
|
|
5434
|
+
const created = !await fileExists2(configPath);
|
|
5435
|
+
if (created) {
|
|
5436
|
+
const document = {
|
|
5437
|
+
version: 1,
|
|
5438
|
+
project: {
|
|
5439
|
+
id: basename2(projectRoot),
|
|
5440
|
+
workspace: "."
|
|
5441
|
+
},
|
|
5442
|
+
gateway: {
|
|
5443
|
+
host: "0.0.0.0",
|
|
5444
|
+
port: "auto"
|
|
5445
|
+
},
|
|
5446
|
+
upstream: {
|
|
5447
|
+
port: "auto",
|
|
5448
|
+
command: devCommand(packageManager, devScript)
|
|
5449
|
+
}
|
|
5450
|
+
};
|
|
5451
|
+
await mkdir3(dirname4(configPath), { recursive: true });
|
|
5452
|
+
try {
|
|
5453
|
+
await writeFile3(configPath, stringifyYaml(document), {
|
|
5454
|
+
encoding: "utf8",
|
|
5455
|
+
flag: "wx"
|
|
5456
|
+
});
|
|
5457
|
+
} catch (error) {
|
|
5458
|
+
if (!isMissingFile(error) && (typeof error !== "object" || error === null || !("code" in error) || error.code !== "EEXIST")) {
|
|
5459
|
+
throw error;
|
|
5460
|
+
}
|
|
5287
5461
|
}
|
|
5288
|
-
throw error;
|
|
5289
5462
|
}
|
|
5290
|
-
return {
|
|
5463
|
+
return {
|
|
5464
|
+
created,
|
|
5465
|
+
configPath,
|
|
5466
|
+
devScript,
|
|
5467
|
+
packageManager,
|
|
5468
|
+
integrationPath,
|
|
5469
|
+
integrationChanged,
|
|
5470
|
+
packageInstalled
|
|
5471
|
+
};
|
|
5291
5472
|
}
|
|
5292
5473
|
function formatInitResult(result) {
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
`Already initialized: ${CONFIG_PATH}`,
|
|
5296
|
-
"Next: npx --yes visual-remote@latest dev"
|
|
5297
|
-
].join("\n");
|
|
5298
|
-
}
|
|
5474
|
+
const configLabel = result.created ? "Created" : "Existing";
|
|
5475
|
+
const integrationLabel = result.integrationChanged ? "Configured" : "Existing";
|
|
5299
5476
|
return [
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
"
|
|
5477
|
+
`${configLabel}: ${relative7(process.cwd(), result.configPath) || CONFIG_PATH}`,
|
|
5478
|
+
`${integrationLabel}: ${relative7(process.cwd(), result.integrationPath)}`,
|
|
5479
|
+
result.packageInstalled ? "Installed: visual-remote@latest" : "Installed: visual-remote",
|
|
5480
|
+
"Next: Start the app normally and open its original URL."
|
|
5303
5481
|
].join("\n");
|
|
5304
5482
|
}
|
|
5305
5483
|
|
|
@@ -5353,7 +5531,7 @@ function setExitCode(dependencies, code) {
|
|
|
5353
5531
|
}
|
|
5354
5532
|
}
|
|
5355
5533
|
function createCli(dependencies = {}) {
|
|
5356
|
-
const program = new Command().name("visual").description("Visual Remote Dev Bridge").version("0.
|
|
5534
|
+
const program = new Command().name("visual").description("Visual Remote Dev Bridge").version("0.2.0");
|
|
5357
5535
|
program.command("init").description("Create .visualdev/config.yaml for the current project").action(async () => {
|
|
5358
5536
|
const result = await initializeVisualDev(dependencies);
|
|
5359
5537
|
output(dependencies, formatInitResult(result));
|
|
@@ -5432,5 +5610,6 @@ export {
|
|
|
5432
5610
|
runBridgeUntilSignal,
|
|
5433
5611
|
runDoctor,
|
|
5434
5612
|
startAttachBridge,
|
|
5435
|
-
startManagedBridge
|
|
5613
|
+
startManagedBridge,
|
|
5614
|
+
transformViteConfig
|
|
5436
5615
|
};
|