visual-remote 0.1.2 → 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 +262 -75
- package/apps/cli/dist/vite.js +5007 -0
- package/apps/cli/vite.d.ts +11 -0
- package/package.json +22 -4
package/apps/cli/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import { realpathSync } from "node:fs";
|
|
4
5
|
import { resolve as resolve8 } from "node:path";
|
|
5
6
|
import { pathToFileURL } from "node:url";
|
|
6
7
|
import { Command, InvalidArgumentError } from "commander";
|
|
@@ -1438,8 +1439,8 @@ var CodexAdapter = class {
|
|
|
1438
1439
|
};
|
|
1439
1440
|
|
|
1440
1441
|
// ../../packages/bridge-core/src/config/loader.ts
|
|
1441
|
-
import { readFile, realpath } from "node:fs/promises";
|
|
1442
|
-
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";
|
|
1443
1444
|
import { parse as parseYaml } from "yaml";
|
|
1444
1445
|
import { ZodError } from "zod";
|
|
1445
1446
|
|
|
@@ -1626,11 +1627,42 @@ function isWithinRoot(root, candidate) {
|
|
|
1626
1627
|
const pathFromRoot = relative(root, candidate);
|
|
1627
1628
|
return pathFromRoot === "" || !pathFromRoot.startsWith(`..${sep}`) && pathFromRoot !== "..";
|
|
1628
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
|
+
}
|
|
1629
1655
|
async function loadVisualDevConfig(repositoryRoot, options = {}) {
|
|
1630
1656
|
const repoRoot = await realpath(repositoryRoot);
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
|
|
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);
|
|
1634
1666
|
const baseDocument = await readYamlMapping(configPath, options.requireConfig ?? false);
|
|
1635
1667
|
const localDocument = await readYamlMapping(localConfigPath, false);
|
|
1636
1668
|
const loadedFiles = [];
|
|
@@ -1657,7 +1689,7 @@ async function loadVisualDevConfig(repositoryRoot, options = {}) {
|
|
|
1657
1689
|
filePath: localDocument === void 0 ? configPath : localConfigPath
|
|
1658
1690
|
});
|
|
1659
1691
|
}
|
|
1660
|
-
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);
|
|
1661
1693
|
let workspaceRoot;
|
|
1662
1694
|
try {
|
|
1663
1695
|
workspaceRoot = await realpath(unresolvedWorkspace);
|
|
@@ -1676,6 +1708,7 @@ async function loadVisualDevConfig(repositoryRoot, options = {}) {
|
|
|
1676
1708
|
return {
|
|
1677
1709
|
config,
|
|
1678
1710
|
repoRoot,
|
|
1711
|
+
configRoot,
|
|
1679
1712
|
workspaceRoot,
|
|
1680
1713
|
configPath,
|
|
1681
1714
|
localConfigPath,
|
|
@@ -1963,7 +1996,7 @@ import {
|
|
|
1963
1996
|
rm
|
|
1964
1997
|
} from "node:fs/promises";
|
|
1965
1998
|
import { tmpdir } from "node:os";
|
|
1966
|
-
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";
|
|
1967
2000
|
var SNAPSHOT_ENV = {
|
|
1968
2001
|
GIT_AUTHOR_NAME: "Visual Bridge",
|
|
1969
2002
|
GIT_AUTHOR_EMAIL: "visual-bridge@localhost",
|
|
@@ -2316,7 +2349,7 @@ var GitTransactionManager = class _GitTransactionManager {
|
|
|
2316
2349
|
} catch (error) {
|
|
2317
2350
|
if (error.code !== "ENOENT") throw error;
|
|
2318
2351
|
}
|
|
2319
|
-
await this.#removeEmptyParents(
|
|
2352
|
+
await this.#removeEmptyParents(dirname2(absolute));
|
|
2320
2353
|
}
|
|
2321
2354
|
async #removeEmptyParents(start) {
|
|
2322
2355
|
let cursor = start;
|
|
@@ -2326,13 +2359,13 @@ var GitTransactionManager = class _GitTransactionManager {
|
|
|
2326
2359
|
} catch (error) {
|
|
2327
2360
|
const code = error.code;
|
|
2328
2361
|
if (code === "ENOENT") {
|
|
2329
|
-
cursor =
|
|
2362
|
+
cursor = dirname2(cursor);
|
|
2330
2363
|
continue;
|
|
2331
2364
|
}
|
|
2332
2365
|
if (code === "ENOTEMPTY" || code === "EEXIST") return;
|
|
2333
2366
|
throw error;
|
|
2334
2367
|
}
|
|
2335
|
-
cursor =
|
|
2368
|
+
cursor = dirname2(cursor);
|
|
2336
2369
|
}
|
|
2337
2370
|
}
|
|
2338
2371
|
async #assertSnapshotRef(ref) {
|
|
@@ -2352,7 +2385,7 @@ var GitTransactionManager = class _GitTransactionManager {
|
|
|
2352
2385
|
// ../../packages/bridge-core/src/storage/sqlite-task-store.ts
|
|
2353
2386
|
import { mkdirSync } from "node:fs";
|
|
2354
2387
|
import { createRequire } from "node:module";
|
|
2355
|
-
import { dirname as
|
|
2388
|
+
import { dirname as dirname3 } from "node:path";
|
|
2356
2389
|
var requireNodeBuiltin = createRequire(import.meta.url);
|
|
2357
2390
|
var optional = (value) => value ?? void 0;
|
|
2358
2391
|
function parseJson(value, fallback) {
|
|
@@ -2444,7 +2477,7 @@ function taskParams(task) {
|
|
|
2444
2477
|
var SqliteTaskStore = class {
|
|
2445
2478
|
#db;
|
|
2446
2479
|
constructor(filename) {
|
|
2447
|
-
if (filename !== ":memory:") mkdirSync(
|
|
2480
|
+
if (filename !== ":memory:") mkdirSync(dirname3(filename), { recursive: true, mode: 448 });
|
|
2448
2481
|
const { DatabaseSync } = requireNodeBuiltin("node:sqlite");
|
|
2449
2482
|
this.#db = new DatabaseSync(filename);
|
|
2450
2483
|
this.#db.exec(`
|
|
@@ -4588,7 +4621,9 @@ ${output2}` : ""}`
|
|
|
4588
4621
|
|
|
4589
4622
|
// ../../packages/bridge-core/src/bridge/default-control-service.ts
|
|
4590
4623
|
async function createDefaultControlService(context, environment = process.env) {
|
|
4591
|
-
const loaded = await loadVisualDevConfig(context.repoRoot
|
|
4624
|
+
const loaded = await loadVisualDevConfig(context.repoRoot, {
|
|
4625
|
+
...context.configRoot === void 0 ? {} : { configRoot: context.configRoot }
|
|
4626
|
+
});
|
|
4592
4627
|
if (loaded.config.agent.adapter !== "codex") {
|
|
4593
4628
|
throw new Error(
|
|
4594
4629
|
`Agent adapter ${loaded.config.agent.adapter} is not implemented in this MVP build`
|
|
@@ -4881,6 +4916,7 @@ async function startBridgeCore(options, dependencies) {
|
|
|
4881
4916
|
mode: options.mode,
|
|
4882
4917
|
projectId: loadedConfig.config.project.id,
|
|
4883
4918
|
repoRoot: loadedConfig.repoRoot,
|
|
4919
|
+
configRoot: loadedConfig.configRoot,
|
|
4884
4920
|
workspaceRoot: loadedConfig.workspaceRoot,
|
|
4885
4921
|
upstreamUrl: options.upstreamUrl
|
|
4886
4922
|
};
|
|
@@ -4950,8 +4986,10 @@ async function startBridgeCore(options, dependencies) {
|
|
|
4950
4986
|
}
|
|
4951
4987
|
}
|
|
4952
4988
|
async function startAttachBridge(options, dependencies = {}) {
|
|
4953
|
-
const
|
|
4954
|
-
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 });
|
|
4955
4993
|
return await startBridgeCore(
|
|
4956
4994
|
{
|
|
4957
4995
|
mode: "attach",
|
|
@@ -4965,8 +5003,13 @@ async function startAttachBridge(options, dependencies = {}) {
|
|
|
4965
5003
|
);
|
|
4966
5004
|
}
|
|
4967
5005
|
async function startManagedBridge(options = {}, dependencies = {}) {
|
|
4968
|
-
const
|
|
4969
|
-
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
|
+
});
|
|
4970
5013
|
const command = loadedConfig.config.upstream.command;
|
|
4971
5014
|
if (command === void 0) {
|
|
4972
5015
|
throw new Error("visual dev requires upstream.command in .visualdev/config.yaml");
|
|
@@ -5075,14 +5118,14 @@ function formatBridgeSummary(bridge) {
|
|
|
5075
5118
|
|
|
5076
5119
|
// src/doctor.ts
|
|
5077
5120
|
import { constants } from "node:fs";
|
|
5078
|
-
import { access as access2, stat as
|
|
5079
|
-
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";
|
|
5080
5123
|
import { execFile as execFile2 } from "node:child_process";
|
|
5081
5124
|
import { promisify as promisify2 } from "node:util";
|
|
5082
5125
|
var execFileAsync2 = promisify2(execFile2);
|
|
5083
5126
|
async function fileExists(path) {
|
|
5084
5127
|
try {
|
|
5085
|
-
await
|
|
5128
|
+
await stat3(path);
|
|
5086
5129
|
return true;
|
|
5087
5130
|
} catch (error) {
|
|
5088
5131
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
@@ -5130,14 +5173,19 @@ async function runDoctor(dependencies = {}) {
|
|
|
5130
5173
|
return checks;
|
|
5131
5174
|
}
|
|
5132
5175
|
try {
|
|
5133
|
-
const
|
|
5176
|
+
const configRoot = await discoverVisualDevConfigRoot(
|
|
5177
|
+
dependencies.cwd ?? process.cwd(),
|
|
5178
|
+
repoRoot
|
|
5179
|
+
);
|
|
5180
|
+
const loaded = await loadVisualDevConfig(repoRoot, { configRoot });
|
|
5134
5181
|
checks.push({
|
|
5135
5182
|
name: "config",
|
|
5136
5183
|
status: loaded.loadedFiles.length === 0 ? "warning" : "pass",
|
|
5137
5184
|
message: loaded.loadedFiles.length === 0 ? "No .visualdev/config.yaml; attach defaults are available." : loaded.loadedFiles.join(", ")
|
|
5138
5185
|
});
|
|
5139
5186
|
if (await fileExists(loaded.localConfigPath)) {
|
|
5140
|
-
const
|
|
5187
|
+
const localConfigRelativePath = relative6(repoRoot, loaded.localConfigPath);
|
|
5188
|
+
const ignored = await isIgnored(repoRoot, localConfigRelativePath);
|
|
5141
5189
|
checks.push({
|
|
5142
5190
|
name: "local-config-ignore",
|
|
5143
5191
|
status: ignored ? "pass" : "warning",
|
|
@@ -5175,15 +5223,23 @@ function formatDoctorChecks(checks) {
|
|
|
5175
5223
|
}
|
|
5176
5224
|
|
|
5177
5225
|
// src/init.ts
|
|
5178
|
-
import {
|
|
5179
|
-
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";
|
|
5180
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";';
|
|
5181
5237
|
function isMissingFile(error) {
|
|
5182
5238
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5183
5239
|
}
|
|
5184
5240
|
async function fileExists2(path) {
|
|
5185
5241
|
try {
|
|
5186
|
-
await
|
|
5242
|
+
await stat4(path);
|
|
5187
5243
|
return true;
|
|
5188
5244
|
} catch (error) {
|
|
5189
5245
|
if (isMissingFile(error)) return false;
|
|
@@ -5195,7 +5251,7 @@ function packageManagerFromField(value) {
|
|
|
5195
5251
|
const name = value.split("@", 1)[0];
|
|
5196
5252
|
return name === "bun" || name === "npm" || name === "pnpm" || name === "yarn" ? name : void 0;
|
|
5197
5253
|
}
|
|
5198
|
-
async function detectPackageManager(repoRoot, manifest) {
|
|
5254
|
+
async function detectPackageManager(projectRoot, repoRoot, manifest) {
|
|
5199
5255
|
const declared = packageManagerFromField(manifest.packageManager);
|
|
5200
5256
|
if (declared !== void 0) return declared;
|
|
5201
5257
|
const lockfiles = [
|
|
@@ -5205,8 +5261,10 @@ async function detectPackageManager(repoRoot, manifest) {
|
|
|
5205
5261
|
["bun", "bun.lockb"],
|
|
5206
5262
|
["npm", "package-lock.json"]
|
|
5207
5263
|
];
|
|
5208
|
-
for (const [
|
|
5209
|
-
|
|
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
|
+
}
|
|
5210
5268
|
}
|
|
5211
5269
|
return "npm";
|
|
5212
5270
|
}
|
|
@@ -5216,6 +5274,19 @@ function readDevScript(manifest) {
|
|
|
5216
5274
|
}
|
|
5217
5275
|
return manifest.scripts.dev.trim();
|
|
5218
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
|
+
}
|
|
5219
5290
|
function devCommand(manager, script) {
|
|
5220
5291
|
const command = manager === "pnpm" ? ["corepack", "pnpm", "run", "dev"] : manager === "yarn" ? ["corepack", "yarn", "run", "dev"] : manager === "bun" ? ["bun", "run", "dev"] : ["npm", "run", "dev"];
|
|
5221
5292
|
const next = /(^|[\s;&|])next(?:\s|$)/.test(script);
|
|
@@ -5228,14 +5299,14 @@ function devCommand(manager, script) {
|
|
|
5228
5299
|
"{upstreamPort}"
|
|
5229
5300
|
];
|
|
5230
5301
|
}
|
|
5231
|
-
async function readManifest(
|
|
5232
|
-
const manifestPath = join5(
|
|
5302
|
+
async function readManifest(projectRoot) {
|
|
5303
|
+
const manifestPath = join5(projectRoot, "package.json");
|
|
5233
5304
|
let source;
|
|
5234
5305
|
try {
|
|
5235
5306
|
source = await readFile5(manifestPath, "utf8");
|
|
5236
5307
|
} catch (error) {
|
|
5237
5308
|
if (isMissingFile(error)) {
|
|
5238
|
-
throw new Error("visual init requires package.json
|
|
5309
|
+
throw new Error("visual init requires package.json in the current directory");
|
|
5239
5310
|
}
|
|
5240
5311
|
throw error;
|
|
5241
5312
|
}
|
|
@@ -5250,55 +5321,163 @@ async function readManifest(repoRoot) {
|
|
|
5250
5321
|
}
|
|
5251
5322
|
return value;
|
|
5252
5323
|
}
|
|
5253
|
-
async function
|
|
5254
|
-
const
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
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;
|
|
5258
5328
|
}
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
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]
|
|
5276
5389
|
};
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
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
|
|
5282
5399
|
});
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
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
|
+
}
|
|
5286
5461
|
}
|
|
5287
|
-
throw error;
|
|
5288
5462
|
}
|
|
5289
|
-
return {
|
|
5463
|
+
return {
|
|
5464
|
+
created,
|
|
5465
|
+
configPath,
|
|
5466
|
+
devScript,
|
|
5467
|
+
packageManager,
|
|
5468
|
+
integrationPath,
|
|
5469
|
+
integrationChanged,
|
|
5470
|
+
packageInstalled
|
|
5471
|
+
};
|
|
5290
5472
|
}
|
|
5291
5473
|
function formatInitResult(result) {
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
`Already initialized: ${CONFIG_PATH}`,
|
|
5295
|
-
"Next: npx --yes visual-remote@latest dev"
|
|
5296
|
-
].join("\n");
|
|
5297
|
-
}
|
|
5474
|
+
const configLabel = result.created ? "Created" : "Existing";
|
|
5475
|
+
const integrationLabel = result.integrationChanged ? "Configured" : "Existing";
|
|
5298
5476
|
return [
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
"
|
|
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."
|
|
5302
5481
|
].join("\n");
|
|
5303
5482
|
}
|
|
5304
5483
|
|
|
@@ -5352,7 +5531,7 @@ function setExitCode(dependencies, code) {
|
|
|
5352
5531
|
}
|
|
5353
5532
|
}
|
|
5354
5533
|
function createCli(dependencies = {}) {
|
|
5355
|
-
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");
|
|
5356
5535
|
program.command("init").description("Create .visualdev/config.yaml for the current project").action(async () => {
|
|
5357
5536
|
const result = await initializeVisualDev(dependencies);
|
|
5358
5537
|
output(dependencies, formatInitResult(result));
|
|
@@ -5402,8 +5581,15 @@ function createCli(dependencies = {}) {
|
|
|
5402
5581
|
async function main(argv = process.argv, dependencies = {}) {
|
|
5403
5582
|
await createCli(dependencies).parseAsync(argv);
|
|
5404
5583
|
}
|
|
5584
|
+
function isDirectEntry(entryPath2) {
|
|
5585
|
+
try {
|
|
5586
|
+
return pathToFileURL(realpathSync(resolve8(entryPath2))).href === import.meta.url;
|
|
5587
|
+
} catch {
|
|
5588
|
+
return false;
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5405
5591
|
var entryPath = process.argv[1];
|
|
5406
|
-
if (entryPath !== void 0 &&
|
|
5592
|
+
if (entryPath !== void 0 && isDirectEntry(entryPath)) {
|
|
5407
5593
|
void main().catch((error) => {
|
|
5408
5594
|
process.stderr.write(
|
|
5409
5595
|
`${error instanceof Error ? error.message : "Unknown Visual Bridge error"}
|
|
@@ -5424,5 +5610,6 @@ export {
|
|
|
5424
5610
|
runBridgeUntilSignal,
|
|
5425
5611
|
runDoctor,
|
|
5426
5612
|
startAttachBridge,
|
|
5427
|
-
startManagedBridge
|
|
5613
|
+
startManagedBridge,
|
|
5614
|
+
transformViteConfig
|
|
5428
5615
|
};
|