dev3000 0.0.185 → 0.0.187
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 +102 -13
- package/bin/dev3000 +1 -2
- package/dist/cdp-monitor.d.ts +7 -12
- package/dist/cdp-monitor.d.ts.map +1 -1
- package/dist/cdp-monitor.js +86 -170
- package/dist/cdp-monitor.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1369
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/portless.d.ts +10 -0
- package/dist/portless.d.ts.map +1 -1
- package/dist/portless.js +44 -1
- package/dist/portless.js.map +1 -1
- package/dist/runtime/bridge.d.ts +26 -0
- package/dist/runtime/bridge.d.ts.map +1 -0
- package/dist/runtime/bridge.js +340 -0
- package/dist/runtime/bridge.js.map +1 -0
- package/dist/runtime/chrome-isolation.d.ts +16 -0
- package/dist/runtime/chrome-isolation.d.ts.map +1 -0
- package/dist/runtime/chrome-isolation.js +97 -0
- package/dist/runtime/chrome-isolation.js.map +1 -0
- package/dist/runtime/cli.js +116 -26
- package/dist/runtime/cli.js.map +1 -1
- package/dist/runtime/event-views.d.ts +21 -0
- package/dist/runtime/event-views.d.ts.map +1 -0
- package/dist/runtime/event-views.js +53 -0
- package/dist/runtime/event-views.js.map +1 -0
- package/dist/runtime/harness.d.ts +16 -0
- package/dist/runtime/harness.d.ts.map +1 -0
- package/dist/runtime/harness.js +68 -0
- package/dist/runtime/harness.js.map +1 -0
- package/dist/runtime/local-proxy.d.ts +12 -0
- package/dist/runtime/local-proxy.d.ts.map +1 -0
- package/dist/runtime/local-proxy.js +114 -0
- package/dist/runtime/local-proxy.js.map +1 -0
- package/dist/runtime/runtime.d.ts +14 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +82 -21
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/sandbox-agent.d.ts +6 -0
- package/dist/runtime/sandbox-agent.d.ts.map +1 -0
- package/dist/runtime/sandbox-agent.js +35 -0
- package/dist/runtime/sandbox-agent.js.map +1 -0
- package/dist/runtime/sandbox-command.d.ts +9 -0
- package/dist/runtime/sandbox-command.d.ts.map +1 -0
- package/dist/runtime/sandbox-command.js +101 -0
- package/dist/runtime/sandbox-command.js.map +1 -0
- package/dist/runtime/sandbox-proxy.d.ts +11 -0
- package/dist/runtime/sandbox-proxy.d.ts.map +1 -0
- package/dist/runtime/sandbox-proxy.js +123 -0
- package/dist/runtime/sandbox-proxy.js.map +1 -0
- package/dist/runtime/sandbox-sessions.d.ts +31 -0
- package/dist/runtime/sandbox-sessions.d.ts.map +1 -0
- package/dist/runtime/sandbox-sessions.js +102 -0
- package/dist/runtime/sandbox-sessions.js.map +1 -0
- package/dist/runtime/sandbox.d.ts +21 -0
- package/dist/runtime/sandbox.d.ts.map +1 -0
- package/dist/runtime/sandbox.js +481 -0
- package/dist/runtime/sandbox.js.map +1 -0
- package/dist/utils/agent-browser.d.ts +2 -120
- package/dist/utils/agent-browser.d.ts.map +1 -1
- package/dist/utils/agent-browser.js +13 -376
- package/dist/utils/agent-browser.js.map +1 -1
- package/dist/utils/project-name.d.ts +11 -1
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +14 -1
- package/dist/utils/project-name.js.map +1 -1
- package/package.json +13 -22
- package/skills/d3k/SKILL.md +23 -1
- package/dist/commands/crawl.d.ts +0 -12
- package/dist/commands/crawl.d.ts.map +0 -1
- package/dist/commands/crawl.js +0 -101
- package/dist/commands/crawl.js.map +0 -1
- package/dist/commands/errors.d.ts +0 -14
- package/dist/commands/errors.d.ts.map +0 -1
- package/dist/commands/errors.js +0 -246
- package/dist/commands/errors.js.map +0 -1
- package/dist/commands/fix.d.ts +0 -13
- package/dist/commands/fix.d.ts.map +0 -1
- package/dist/commands/fix.js +0 -219
- package/dist/commands/fix.js.map +0 -1
- package/dist/commands/logs.d.ts +0 -13
- package/dist/commands/logs.d.ts.map +0 -1
- package/dist/commands/logs.js +0 -146
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/resume.d.ts +0 -11
- package/dist/commands/resume.d.ts.map +0 -1
- package/dist/commands/resume.js +0 -75
- package/dist/commands/resume.js.map +0 -1
- package/dist/commands/status.d.ts +0 -21
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -44
- package/dist/commands/status.js.map +0 -1
- package/dist/components/AgentSelector.d.ts +0 -12
- package/dist/components/AgentSelector.d.ts.map +0 -1
- package/dist/components/AgentSelector.js +0 -99
- package/dist/components/AgentSelector.js.map +0 -1
- package/dist/components/PackageSelector.d.ts +0 -13
- package/dist/components/PackageSelector.d.ts.map +0 -1
- package/dist/components/PackageSelector.js +0 -79
- package/dist/components/PackageSelector.js.map +0 -1
- package/dist/constants/log-colors.d.ts +0 -43
- package/dist/constants/log-colors.d.ts.map +0 -1
- package/dist/constants/log-colors.js +0 -59
- package/dist/constants/log-colors.js.map +0 -1
- package/dist/dev-environment.d.ts +0 -187
- package/dist/dev-environment.d.ts.map +0 -1
- package/dist/dev-environment.js +0 -2248
- package/dist/dev-environment.js.map +0 -1
- package/dist/screencast-manager.d.ts +0 -78
- package/dist/screencast-manager.d.ts.map +0 -1
- package/dist/screencast-manager.js +0 -529
- package/dist/screencast-manager.js.map +0 -1
- package/dist/services/parsers/error-detectors/base.d.ts +0 -24
- package/dist/services/parsers/error-detectors/base.d.ts.map +0 -1
- package/dist/services/parsers/error-detectors/base.js +0 -44
- package/dist/services/parsers/error-detectors/base.js.map +0 -1
- package/dist/services/parsers/error-detectors/index.d.ts +0 -6
- package/dist/services/parsers/error-detectors/index.d.ts.map +0 -1
- package/dist/services/parsers/error-detectors/index.js +0 -6
- package/dist/services/parsers/error-detectors/index.js.map +0 -1
- package/dist/services/parsers/error-detectors/nextjs.d.ts +0 -9
- package/dist/services/parsers/error-detectors/nextjs.d.ts.map +0 -1
- package/dist/services/parsers/error-detectors/nextjs.js +0 -52
- package/dist/services/parsers/error-detectors/nextjs.js.map +0 -1
- package/dist/services/parsers/index.d.ts +0 -9
- package/dist/services/parsers/index.d.ts.map +0 -1
- package/dist/services/parsers/index.js +0 -10
- package/dist/services/parsers/index.js.map +0 -1
- package/dist/services/parsers/log-parsers/base.d.ts +0 -26
- package/dist/services/parsers/log-parsers/base.d.ts.map +0 -1
- package/dist/services/parsers/log-parsers/base.js +0 -6
- package/dist/services/parsers/log-parsers/base.js.map +0 -1
- package/dist/services/parsers/log-parsers/index.d.ts +0 -6
- package/dist/services/parsers/log-parsers/index.d.ts.map +0 -1
- package/dist/services/parsers/log-parsers/index.js +0 -5
- package/dist/services/parsers/log-parsers/index.js.map +0 -1
- package/dist/services/parsers/log-parsers/standard.d.ts +0 -9
- package/dist/services/parsers/log-parsers/standard.d.ts.map +0 -1
- package/dist/services/parsers/log-parsers/standard.js +0 -17
- package/dist/services/parsers/log-parsers/standard.js.map +0 -1
- package/dist/services/parsers/output-processor.d.ts +0 -39
- package/dist/services/parsers/output-processor.d.ts.map +0 -1
- package/dist/services/parsers/output-processor.js +0 -54
- package/dist/services/parsers/output-processor.js.map +0 -1
- package/dist/skills/d3k/internal-skill.md +0 -32
- package/dist/skills/index.d.ts +0 -54
- package/dist/skills/index.d.ts.map +0 -1
- package/dist/skills/index.js +0 -214
- package/dist/skills/index.js.map +0 -1
- package/dist/src/tui-interface-impl.tsx +0 -644
- package/dist/tui-interface-impl.d.ts +0 -30
- package/dist/tui-interface-impl.d.ts.map +0 -1
- package/dist/tui-interface-impl.js +0 -403
- package/dist/tui-interface-impl.js.map +0 -1
- package/dist/tui-interface-opentui.d.ts +0 -33
- package/dist/tui-interface-opentui.d.ts.map +0 -1
- package/dist/tui-interface-opentui.js +0 -1128
- package/dist/tui-interface-opentui.js.map +0 -1
- package/dist/tui-interface.d.ts +0 -38
- package/dist/tui-interface.d.ts.map +0 -1
- package/dist/tui-interface.js +0 -84
- package/dist/tui-interface.js.map +0 -1
- package/dist/utils/agent-detection.d.ts +0 -17
- package/dist/utils/agent-detection.d.ts.map +0 -1
- package/dist/utils/agent-detection.js +0 -81
- package/dist/utils/agent-detection.js.map +0 -1
- package/dist/utils/agent-selection.d.ts +0 -25
- package/dist/utils/agent-selection.d.ts.map +0 -1
- package/dist/utils/agent-selection.js +0 -73
- package/dist/utils/agent-selection.js.map +0 -1
- package/dist/utils/browser-command-argv.d.ts +0 -19
- package/dist/utils/browser-command-argv.d.ts.map +0 -1
- package/dist/utils/browser-command-argv.js +0 -149
- package/dist/utils/browser-command-argv.js.map +0 -1
- package/dist/utils/build-version.d.ts +0 -2
- package/dist/utils/build-version.d.ts.map +0 -1
- package/dist/utils/build-version.js +0 -8
- package/dist/utils/build-version.js.map +0 -1
- package/dist/utils/cli-options.d.ts +0 -5
- package/dist/utils/cli-options.d.ts.map +0 -1
- package/dist/utils/cli-options.js +0 -36
- package/dist/utils/cli-options.js.map +0 -1
- package/dist/utils/d3k-dir.d.ts +0 -3
- package/dist/utils/d3k-dir.d.ts.map +0 -1
- package/dist/utils/d3k-dir.js +0 -12
- package/dist/utils/d3k-dir.js.map +0 -1
- package/dist/utils/d3k-skill-installer.d.ts +0 -4
- package/dist/utils/d3k-skill-installer.d.ts.map +0 -1
- package/dist/utils/d3k-skill-installer.js +0 -30
- package/dist/utils/d3k-skill-installer.js.map +0 -1
- package/dist/utils/log-filename.d.ts +0 -30
- package/dist/utils/log-filename.d.ts.map +0 -1
- package/dist/utils/log-filename.js +0 -60
- package/dist/utils/log-filename.js.map +0 -1
- package/dist/utils/project-detector.d.ts +0 -25
- package/dist/utils/project-detector.d.ts.map +0 -1
- package/dist/utils/project-detector.js +0 -153
- package/dist/utils/project-detector.js.map +0 -1
- package/dist/utils/project-metadata.d.ts +0 -4
- package/dist/utils/project-metadata.d.ts.map +0 -1
- package/dist/utils/project-metadata.js +0 -48
- package/dist/utils/project-metadata.js.map +0 -1
- package/dist/utils/repo-validate.d.ts +0 -2
- package/dist/utils/repo-validate.d.ts.map +0 -1
- package/dist/utils/repo-validate.js +0 -10
- package/dist/utils/repo-validate.js.map +0 -1
- package/dist/utils/session.d.ts +0 -19
- package/dist/utils/session.d.ts.map +0 -1
- package/dist/utils/session.js +0 -65
- package/dist/utils/session.js.map +0 -1
- package/dist/utils/skill-installer.d.ts +0 -65
- package/dist/utils/skill-installer.d.ts.map +0 -1
- package/dist/utils/skill-installer.js +0 -265
- package/dist/utils/skill-installer.js.map +0 -1
- package/dist/utils/timestamp.d.ts +0 -20
- package/dist/utils/timestamp.d.ts.map +0 -1
- package/dist/utils/timestamp.js +0 -57
- package/dist/utils/timestamp.js.map +0 -1
- package/dist/utils/tmux-helpers.d.ts +0 -36
- package/dist/utils/tmux-helpers.d.ts.map +0 -1
- package/dist/utils/tmux-helpers.js +0 -118
- package/dist/utils/tmux-helpers.js.map +0 -1
- package/dist/utils/user-config.d.ts +0 -12
- package/dist/utils/user-config.d.ts.map +0 -1
- package/dist/utils/user-config.js +0 -46
- package/dist/utils/user-config.js.map +0 -1
- package/dist/utils/version-check.d.ts +0 -52
- package/dist/utils/version-check.d.ts.map +0 -1
- package/dist/utils/version-check.js +0 -304
- package/dist/utils/version-check.js.map +0 -1
- package/src/tui-interface-impl.tsx +0 -644
package/dist/cli.js
CHANGED
|
@@ -1,1370 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function getRunnablePath(searchPath) {
|
|
3
|
-
if (!existsSync(searchPath)) {
|
|
4
|
-
return null;
|
|
5
|
-
}
|
|
6
|
-
if (process.platform === "win32") {
|
|
7
|
-
return searchPath;
|
|
8
|
-
}
|
|
9
|
-
try {
|
|
10
|
-
accessSync(searchPath, constants.X_OK);
|
|
11
|
-
return searchPath;
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
try {
|
|
15
|
-
chmodSync(searchPath, 0o755);
|
|
16
|
-
accessSync(searchPath, constants.X_OK);
|
|
17
|
-
return searchPath;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function ensureCommandPath(env) {
|
|
25
|
-
if (!env.PATH || env.PATH === "") {
|
|
26
|
-
env.PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function findAgentBrowser() {
|
|
30
|
-
const os = process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "win32" : "linux";
|
|
31
|
-
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
32
|
-
const nativeName = `agent-browser-${os}-${arch}`;
|
|
33
|
-
const platformPkg = `${os}-${arch}`;
|
|
34
|
-
const cwd = process.cwd();
|
|
35
|
-
const home = homedir();
|
|
36
|
-
const searchPaths = [
|
|
37
|
-
join(home, ".bun", "install", "global", "node_modules", "@d3k", platformPkg, "node_modules", ".bin", nativeName),
|
|
38
|
-
join(home, ".bun", "install", "global", "node_modules", "agent-browser", "bin", nativeName),
|
|
39
|
-
join(home, ".bun", "install", "global", "node_modules", "dev3000", "node_modules", ".bin", nativeName),
|
|
40
|
-
join(cwd, "node_modules", ".bin", nativeName),
|
|
41
|
-
join(cwd, "node_modules", "agent-browser", "bin", nativeName),
|
|
42
|
-
join(home, ".bun", "install", "global", "node_modules", "@d3k", platformPkg, "node_modules", ".bin", "agent-browser"),
|
|
43
|
-
join(home, ".bun", "install", "global", "node_modules", "dev3000", "node_modules", ".bin", "agent-browser"),
|
|
44
|
-
join(home, ".bun", "install", "global", "node_modules", ".bin", "agent-browser"),
|
|
45
|
-
join(home, ".bun", "install", "global", "node_modules", "agent-browser", "bin", "agent-browser.js"),
|
|
46
|
-
join(cwd, "node_modules", ".bin", "agent-browser"),
|
|
47
|
-
join(cwd, "node_modules", "agent-browser", "bin", "agent-browser.js")
|
|
48
|
-
];
|
|
49
|
-
const globalNodeModules = [
|
|
50
|
-
join("/usr", "local", "lib", "node_modules"),
|
|
51
|
-
join("/opt", "homebrew", "lib", "node_modules")
|
|
52
|
-
];
|
|
53
|
-
for (const root of globalNodeModules) {
|
|
54
|
-
searchPaths.push(join(root, "dev3000", "node_modules", ".bin", nativeName));
|
|
55
|
-
searchPaths.push(join(root, "dev3000", "node_modules", ".bin", "agent-browser"));
|
|
56
|
-
searchPaths.push(join(root, "agent-browser", "bin", nativeName));
|
|
57
|
-
searchPaths.push(join(root, "agent-browser", "bin", "agent-browser.js"));
|
|
58
|
-
}
|
|
59
|
-
for (const p of searchPaths) {
|
|
60
|
-
const runnablePath = getRunnablePath(p);
|
|
61
|
-
if (runnablePath)
|
|
62
|
-
return runnablePath;
|
|
63
|
-
}
|
|
64
|
-
return "agent-browser";
|
|
65
|
-
}
|
|
66
|
-
function getSessionCdpPort() {
|
|
67
|
-
const session = findCurrentSession();
|
|
68
|
-
return getSessionCdpPortForSession(session);
|
|
69
|
-
}
|
|
70
|
-
function getSessionCdpPortForSession(session) {
|
|
71
|
-
const cdpUrl = session?.cdpUrl;
|
|
72
|
-
if (!cdpUrl) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
const parsed = new URL(cdpUrl);
|
|
77
|
-
if (/^\d+$/.test(parsed.port)) {
|
|
78
|
-
return parsed.port;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
const match = cdpUrl.match(/:(\d+)/);
|
|
83
|
-
if (match) {
|
|
84
|
-
return match[1];
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
function printUnsafeAgentBrowserOpenError(reason, hasActiveSession) {
|
|
90
|
-
const sessionHint = hasActiveSession
|
|
91
|
-
? "The active d3k session has no usable d3k-managed browser. This usually means d3k was started with --servers-only, browser startup failed, or the session is stale."
|
|
92
|
-
: "No active d3k session with a managed browser was found.";
|
|
93
|
-
console.error([
|
|
94
|
-
"Refusing to run `d3k agent-browser open` because it would create or use an agent-browser-managed browser instead of d3k's active browser.",
|
|
95
|
-
"",
|
|
96
|
-
reason,
|
|
97
|
-
sessionHint,
|
|
98
|
-
"",
|
|
99
|
-
"For OAuth/auth debugging, start d3k normally so it launches the app and browser together:",
|
|
100
|
-
' d3k --command "<dev command>" --port <port> --startup-timeout <seconds> --app-url "<url>"',
|
|
101
|
-
"",
|
|
102
|
-
"Then navigate the d3k-managed browser with:",
|
|
103
|
-
' d3k agent-browser --require-d3k-browser open "<url>"',
|
|
104
|
-
"",
|
|
105
|
-
"For an explicit non-auth standalone/fresh-profile browser, pass:",
|
|
106
|
-
' d3k agent-browser --allow-new-browser open "<url>"'
|
|
107
|
-
].join("\n"));
|
|
108
|
-
}
|
|
109
|
-
function runLocalBrowserTool(args) {
|
|
110
|
-
const env = { ...process.env };
|
|
111
|
-
ensureCommandPath(env);
|
|
112
|
-
const invocation = parseAgentBrowserInvocationArgs(args);
|
|
113
|
-
const toolArgs = invocation.args;
|
|
114
|
-
const subcommand = invocation.subcommand;
|
|
115
|
-
const hasExplicitCdp = hasAgentBrowserOption(toolArgs, "--cdp");
|
|
116
|
-
const hasProfile = hasAgentBrowserOption(toolArgs, "--profile") || hasAgentBrowserOption(toolArgs, "--profile-dir");
|
|
117
|
-
if (subcommand === "errors") {
|
|
118
|
-
console.log("\x1b[33m💡 Tip: Using `d3k errors` instead (shows browser + server errors)\x1b[0m\n");
|
|
119
|
-
const d3kBin = process.argv[1];
|
|
120
|
-
const result = spawnSync(d3kBin, ["errors"], { stdio: "inherit", shell: false });
|
|
121
|
-
process.exit(result.status ?? 0);
|
|
122
|
-
}
|
|
123
|
-
if (subcommand === "console") {
|
|
124
|
-
console.log("\x1b[33m💡 Tip: Using `d3k logs` instead (shows browser + server logs)\x1b[0m\n");
|
|
125
|
-
const d3kBin = process.argv[1];
|
|
126
|
-
const result = spawnSync(d3kBin, ["logs", "--type", "browser"], { stdio: "inherit", shell: false });
|
|
127
|
-
process.exit(result.status ?? 0);
|
|
128
|
-
}
|
|
129
|
-
const binaryPath = findAgentBrowser();
|
|
130
|
-
const openNeedsD3kBrowser = isAgentBrowserOpenCommand(subcommand) && !invocation.allowNewBrowser && !hasExplicitCdp;
|
|
131
|
-
if (invocation.allowNewBrowser && invocation.requireD3kBrowser) {
|
|
132
|
-
console.error("`--allow-new-browser` and `--require-d3k-browser` cannot be used together.");
|
|
133
|
-
process.exit(1);
|
|
134
|
-
}
|
|
135
|
-
if (invocation.requireD3kBrowser && hasExplicitCdp) {
|
|
136
|
-
console.error("`--require-d3k-browser` uses the active d3k session. Remove `--cdp` or use `--allow-new-browser`.");
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
const activeSession = findCurrentSession();
|
|
140
|
-
const sessionCdpPort = getSessionCdpPortForSession(activeSession);
|
|
141
|
-
const needsD3kBrowser = invocation.requireD3kBrowser || openNeedsD3kBrowser;
|
|
142
|
-
if (needsD3kBrowser && hasProfile) {
|
|
143
|
-
printUnsafeAgentBrowserOpenError("`--profile` and `--profile-dir` make agent-browser use its own browser profile instead of d3k's active browser.", activeSession !== null);
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
if (needsD3kBrowser && !sessionCdpPort) {
|
|
147
|
-
printUnsafeAgentBrowserOpenError("Opening a URL without a d3k-managed browser would launch a new automation browser, which can break Google OAuth/Supabase auth with `This browser or app may not be secure`.", activeSession !== null);
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
if (subcommand !== "connect" && subcommand !== "close" && !hasExplicitCdp && !hasProfile) {
|
|
151
|
-
if (sessionCdpPort) {
|
|
152
|
-
const connectResult = spawnSync(binaryPath, ["connect", sessionCdpPort], { stdio: "pipe", env, shell: false });
|
|
153
|
-
if (needsD3kBrowser && connectResult.status !== 0) {
|
|
154
|
-
printUnsafeAgentBrowserOpenError("d3k found a browser session, but agent-browser could not connect to it.", activeSession !== null);
|
|
155
|
-
process.exit(connectResult.status ?? 1);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const result = spawnSync(binaryPath, toolArgs, {
|
|
160
|
-
stdio: "pipe",
|
|
161
|
-
env,
|
|
162
|
-
shell: false
|
|
163
|
-
});
|
|
164
|
-
if (result.stdout?.length > 0) {
|
|
165
|
-
process.stdout.write(result.stdout);
|
|
166
|
-
}
|
|
167
|
-
if (result.stderr?.length > 0) {
|
|
168
|
-
process.stderr.write(result.stderr);
|
|
169
|
-
}
|
|
170
|
-
if (result.error) {
|
|
171
|
-
console.error(`\nError spawning agent-browser: ${result.error.message}`);
|
|
172
|
-
console.error(`Binary path: ${binaryPath}`);
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
process.exit(result.status ?? 1);
|
|
176
|
-
}
|
|
177
|
-
import { getBrowserCommandInvocation, hasAgentBrowserOption, isAgentBrowserOpenCommand, parseAgentBrowserInvocationArgs } from "./utils/browser-command-argv.js";
|
|
178
|
-
const browserCommandInvocation = getBrowserCommandInvocation(process.argv.slice(2));
|
|
179
|
-
if (browserCommandInvocation && (process.argv[1]?.includes("d3k") || process.argv[1]?.includes("dev3000"))) {
|
|
180
|
-
runLocalBrowserTool(browserCommandInvocation.args);
|
|
181
|
-
}
|
|
182
|
-
import chalk from "chalk";
|
|
183
|
-
import { execSync, spawnSync } from "child_process";
|
|
184
|
-
import { Command } from "commander";
|
|
185
|
-
import { accessSync, appendFileSync, chmodSync, constants, existsSync, readFileSync } from "fs";
|
|
186
|
-
import { homedir } from "os";
|
|
187
|
-
import { detect } from "package-manager-detector";
|
|
188
|
-
import { dirname, join, resolve } from "path";
|
|
189
|
-
import { fileURLToPath } from "url";
|
|
190
|
-
import { createPersistentLogFile, findAvailablePort, startDevEnvironment } from "./dev-environment.js";
|
|
191
|
-
import { getPortlessProxyStatus, installPortlessService } from "./portless.js";
|
|
192
|
-
import { getSkill, getSkillsInfo, listAvailableSkills } from "./skills/index.js";
|
|
193
|
-
import { detectAIAgent } from "./utils/agent-detection.js";
|
|
194
|
-
import { getAvailableAgents, getSkillsAgentId } from "./utils/agent-selection.js";
|
|
195
|
-
import { validateDateTimeOption, validatePortOption, validatePositiveIntegerOption, validateScriptOption } from "./utils/cli-options.js";
|
|
196
|
-
import { ensureD3kHomeDir } from "./utils/d3k-dir.js";
|
|
197
|
-
import { ensureD3kSkill } from "./utils/d3k-skill-installer.js";
|
|
198
|
-
import { readProjectAgentName } from "./utils/project-metadata.js";
|
|
199
|
-
import { getProjectDir, getProjectDisplayName } from "./utils/project-name.js";
|
|
200
|
-
import { findCurrentSession } from "./utils/session.js";
|
|
201
|
-
import { checkForSkillUpdates, getApplicablePackages, getSkillsPathForLocation, installSkillPackage, isPackageInstalled, updateSkills } from "./utils/skill-installer.js";
|
|
202
|
-
import { DEFAULT_TMUX_CONFIG, generateSessionName, generateTmuxCommands, getTmuxInstallInstructions, isTmuxInstalled } from "./utils/tmux-helpers.js";
|
|
203
|
-
import { loadUserConfig, saveUserConfig } from "./utils/user-config.js";
|
|
204
|
-
import { checkForUpdates, getUpgradeCommand, performUpgrade } from "./utils/version-check.js";
|
|
205
|
-
// Global error handlers to log crashes
|
|
206
|
-
const crashLogPath = join(ensureD3kHomeDir(), "crash.log");
|
|
207
|
-
function logCrash(type, error) {
|
|
208
|
-
try {
|
|
209
|
-
ensureD3kHomeDir();
|
|
210
|
-
const timestamp = new Date().toISOString();
|
|
211
|
-
const errorStr = error instanceof Error ? `${error.message}\n${error.stack}` : String(error);
|
|
212
|
-
const logEntry = `[${timestamp}] ${type}: ${errorStr}\n\n`;
|
|
213
|
-
appendFileSync(crashLogPath, logEntry);
|
|
214
|
-
console.error(chalk.red(`\n💥 d3k crashed: ${error instanceof Error ? error.message : error}`));
|
|
215
|
-
console.error(chalk.gray(` Details logged to: ${crashLogPath}`));
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
// If we can't log, at least print to stderr
|
|
219
|
-
console.error(`d3k crashed: ${error}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
function triggerEmergencyShutdown(reason, error) {
|
|
223
|
-
const handler = globalThis.__d3kEmergencyShutdown;
|
|
224
|
-
if (typeof handler === "function") {
|
|
225
|
-
try {
|
|
226
|
-
handler(reason, error);
|
|
227
|
-
return true;
|
|
228
|
-
}
|
|
229
|
-
catch {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
function setTerminalTitle(rootDir) {
|
|
236
|
-
const safeRootDir = rootDir
|
|
237
|
-
.split("\u001b")
|
|
238
|
-
.join("")
|
|
239
|
-
.split("\u0007")
|
|
240
|
-
.join("")
|
|
241
|
-
.replace(/[\r\n]+/g, " ")
|
|
242
|
-
.trim();
|
|
243
|
-
const title = `d3k ${safeRootDir}`;
|
|
244
|
-
// Some terminals prefer process.title over OSC escape sequences.
|
|
245
|
-
process.title = title;
|
|
246
|
-
if (!process.stdout.isTTY)
|
|
247
|
-
return;
|
|
248
|
-
process.stdout.write(`\u001b]0;${title}\u0007\u001b]2;${title}\u0007`);
|
|
249
|
-
}
|
|
250
|
-
process.on("uncaughtException", (error) => {
|
|
251
|
-
logCrash("Uncaught Exception", error);
|
|
252
|
-
if (!triggerEmergencyShutdown("uncaughtException", error)) {
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
process.on("unhandledRejection", (reason) => {
|
|
257
|
-
logCrash("Unhandled Promise Rejection", reason);
|
|
258
|
-
if (!triggerEmergencyShutdown("unhandledRejection", reason)) {
|
|
259
|
-
process.exit(1);
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
function shellQuote(value) {
|
|
263
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Build the d3k command string with forwarded options.
|
|
267
|
-
*/
|
|
268
|
-
function buildD3kCommandWithOptions(options) {
|
|
269
|
-
const d3kBase = process.argv[1].endsWith("d3k") ? "d3k" : "dev3000";
|
|
270
|
-
const args = [shellQuote(d3kBase)];
|
|
271
|
-
// Forward options that were explicitly set
|
|
272
|
-
if (options.port)
|
|
273
|
-
args.push("--port", shellQuote(options.port));
|
|
274
|
-
if (options.script)
|
|
275
|
-
args.push("--script", shellQuote(options.script));
|
|
276
|
-
if (options.command)
|
|
277
|
-
args.push("--command", shellQuote(options.command));
|
|
278
|
-
if (options.startupTimeout)
|
|
279
|
-
args.push("--startup-timeout", shellQuote(options.startupTimeout));
|
|
280
|
-
if (options.browserNavigationTimeout) {
|
|
281
|
-
args.push("--browser-navigation-timeout", shellQuote(options.browserNavigationTimeout));
|
|
282
|
-
}
|
|
283
|
-
if (options.profileDir)
|
|
284
|
-
args.push("--profile-dir", shellQuote(options.profileDir));
|
|
285
|
-
if (options.browserTool)
|
|
286
|
-
args.push("--browser-tool", shellQuote(options.browserTool));
|
|
287
|
-
if (options.browser)
|
|
288
|
-
args.push("--browser", shellQuote(options.browser));
|
|
289
|
-
if (options.appUrl)
|
|
290
|
-
args.push("--app-url", shellQuote(options.appUrl));
|
|
291
|
-
if (options.externalCdpBase)
|
|
292
|
-
args.push("--external-cdp-base", shellQuote(options.externalCdpBase));
|
|
293
|
-
if (options.serversOnly)
|
|
294
|
-
args.push("--servers-only");
|
|
295
|
-
if (options.headless)
|
|
296
|
-
args.push("--headless");
|
|
297
|
-
if (options.dateTime)
|
|
298
|
-
args.push("--date-time", shellQuote(options.dateTime));
|
|
299
|
-
if (options.pluginReactScan)
|
|
300
|
-
args.push("--plugin-react-scan");
|
|
301
|
-
if (options.agentName)
|
|
302
|
-
args.push("--agent-name", shellQuote(options.agentName));
|
|
303
|
-
if (options.portless === false)
|
|
304
|
-
args.push("--no-portless");
|
|
305
|
-
return args.join(" ");
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Launch d3k with an agent using tmux for proper terminal multiplexing.
|
|
309
|
-
* This creates a split-screen with the agent on the left and d3k logs on the right.
|
|
310
|
-
*/
|
|
311
|
-
async function launchWithTmux(agentCommand, forwardedOptions = {}) {
|
|
312
|
-
const { execSync } = await import("child_process");
|
|
313
|
-
const { appendFileSync, writeFileSync } = await import("fs");
|
|
314
|
-
// Log file for debugging crashes
|
|
315
|
-
const d3kDir = ensureD3kHomeDir();
|
|
316
|
-
const crashLogPath = join(d3kDir, "crash.log");
|
|
317
|
-
const logCrash = (message, error) => {
|
|
318
|
-
const timestamp = new Date().toISOString();
|
|
319
|
-
const errorStr = error instanceof Error ? `${error.message}\n${error.stack}` : String(error || "");
|
|
320
|
-
const logEntry = `[${timestamp}] ${message}${errorStr ? `\n${errorStr}` : ""}\n`;
|
|
321
|
-
try {
|
|
322
|
-
appendFileSync(crashLogPath, logEntry);
|
|
323
|
-
}
|
|
324
|
-
catch {
|
|
325
|
-
// Ignore write errors
|
|
326
|
-
}
|
|
327
|
-
console.error(chalk.red(message));
|
|
328
|
-
if (error)
|
|
329
|
-
console.error(error);
|
|
330
|
-
};
|
|
331
|
-
// Check if tmux is installed
|
|
332
|
-
if (!(await isTmuxInstalled())) {
|
|
333
|
-
console.error(chalk.red("\n❌ tmux is not installed."));
|
|
334
|
-
console.error(chalk.yellow("\nThe --with-agent flag requires tmux for split-screen mode."));
|
|
335
|
-
console.error(chalk.cyan("\nTo install tmux:"));
|
|
336
|
-
for (const instruction of getTmuxInstallInstructions()) {
|
|
337
|
-
console.error(chalk.gray(` ${instruction}`));
|
|
338
|
-
}
|
|
339
|
-
console.error(chalk.yellow("\nAlternatively, run d3k and your agent in separate terminal tabs.\n"));
|
|
340
|
-
process.exit(1);
|
|
341
|
-
}
|
|
342
|
-
// Generate a unique session name
|
|
343
|
-
const sessionName = generateSessionName();
|
|
344
|
-
const agentPaneLogPath = join(d3kDir, "tmux-last-agent.log");
|
|
345
|
-
const d3kPaneLogPath = join(d3kDir, "tmux-last-d3k.log");
|
|
346
|
-
// Build d3k command with forwarded options
|
|
347
|
-
const d3kCommand = buildD3kCommandWithOptions(forwardedOptions);
|
|
348
|
-
// Generate tmux commands using the helper
|
|
349
|
-
const commands = generateTmuxCommands({
|
|
350
|
-
sessionName,
|
|
351
|
-
d3kCommand,
|
|
352
|
-
agentCommand,
|
|
353
|
-
paneWidthPercent: DEFAULT_TMUX_CONFIG.paneWidthPercent,
|
|
354
|
-
d3kLogPath: d3kPaneLogPath,
|
|
355
|
-
agentLogPath: agentPaneLogPath
|
|
356
|
-
});
|
|
357
|
-
// Create a shell script that sets up tmux and attaches
|
|
358
|
-
// This ensures clean terminal state by exec'ing into tmux
|
|
359
|
-
const scriptPath = join(d3kDir, "launch-tmux.sh");
|
|
360
|
-
// Get the first command (new-session) and remaining commands
|
|
361
|
-
const [newSessionCmd, ...remainingCommands] = commands;
|
|
362
|
-
// Modify new-session to include terminal dimensions (detected at runtime)
|
|
363
|
-
// This ensures the 80/20 split ratio is maintained when the window is resized on attach
|
|
364
|
-
const newSessionWithSize = newSessionCmd.replace("tmux new-session -d", "tmux new-session -d -x $COLS -y $LINES");
|
|
365
|
-
const scriptContent = `#!/bin/bash
|
|
366
|
-
set -euo pipefail
|
|
367
|
-
|
|
368
|
-
# Reset terminal state
|
|
369
|
-
stty sane 2>/dev/null || true
|
|
370
|
-
reset 2>/dev/null || true
|
|
371
|
-
|
|
372
|
-
# Get terminal dimensions for proper pane sizing
|
|
373
|
-
COLS=$(tput cols)
|
|
374
|
-
LINES=$(tput lines)
|
|
375
|
-
|
|
376
|
-
# Setup tmux session with actual terminal size
|
|
377
|
-
${newSessionWithSize} && \\
|
|
378
|
-
${remainingCommands.join(" && \\\n")}
|
|
379
|
-
|
|
380
|
-
# Verify split setup succeeded before attaching.
|
|
381
|
-
PANE_COUNT=$(tmux list-panes -t "${sessionName}" 2>/dev/null | wc -l | tr -d ' ')
|
|
382
|
-
if [ "\${PANE_COUNT}" -lt 2 ]; then
|
|
383
|
-
echo "❌ Failed to initialize split panes (expected 2, got \${PANE_COUNT})."
|
|
384
|
-
tmux kill-session -t "${sessionName}" 2>/dev/null || true
|
|
385
|
-
exit 1
|
|
386
|
-
fi
|
|
387
|
-
|
|
388
|
-
# Replace this process with tmux attach
|
|
389
|
-
exec tmux attach-session -t "${sessionName}"
|
|
390
|
-
`;
|
|
391
|
-
try {
|
|
392
|
-
const tmuxStartTime = Date.now();
|
|
393
|
-
// Write the launch script
|
|
394
|
-
writeFileSync(scriptPath, scriptContent, { mode: 0o755 });
|
|
395
|
-
// Spawn bash with the script, inheriting stdio
|
|
396
|
-
// The script will exec into tmux, replacing bash
|
|
397
|
-
const { spawnSync } = await import("child_process");
|
|
398
|
-
const result = spawnSync("bash", [scriptPath], {
|
|
399
|
-
stdio: "inherit",
|
|
400
|
-
shell: false
|
|
401
|
-
});
|
|
402
|
-
const tmuxDurationMs = Date.now() - tmuxStartTime;
|
|
403
|
-
const exitCode = result.status ?? (result.error ? 1 : 0);
|
|
404
|
-
// Clean up session on exit
|
|
405
|
-
try {
|
|
406
|
-
execSync(`tmux kill-session -t "${sessionName}" 2>/dev/null`, { stdio: "ignore" });
|
|
407
|
-
}
|
|
408
|
-
catch {
|
|
409
|
-
// Session might already be killed
|
|
410
|
-
}
|
|
411
|
-
// If tmux exits quickly, surface diagnostics to avoid a "silent crash" experience.
|
|
412
|
-
if (exitCode !== 0 || tmuxDurationMs < 15_000) {
|
|
413
|
-
console.error("");
|
|
414
|
-
if (exitCode !== 0) {
|
|
415
|
-
console.error(chalk.red(`❌ tmux session exited with code ${exitCode}`));
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
console.error(chalk.yellow("⚠️ tmux session exited quickly"));
|
|
419
|
-
}
|
|
420
|
-
console.error(chalk.gray(`Agent pane log: ${agentPaneLogPath}`));
|
|
421
|
-
console.error(chalk.gray(`d3k pane log: ${d3kPaneLogPath}`));
|
|
422
|
-
console.error(chalk.gray(`Crash log: ${crashLogPath}`));
|
|
423
|
-
console.error("");
|
|
424
|
-
}
|
|
425
|
-
// Clear screen only on clean, long-running sessions.
|
|
426
|
-
if (exitCode === 0 && tmuxDurationMs >= 15_000) {
|
|
427
|
-
process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
|
|
428
|
-
console.log(chalk.gray("Thanks for using d3k!"));
|
|
429
|
-
}
|
|
430
|
-
process.exit(exitCode);
|
|
431
|
-
}
|
|
432
|
-
catch (error) {
|
|
433
|
-
logCrash("Failed to start tmux session", error);
|
|
434
|
-
process.exit(1);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* Show interactive agent selection prompt using Ink.
|
|
439
|
-
* Returns the selected agent config, or null if user chose "No agent".
|
|
440
|
-
*/
|
|
441
|
-
async function promptAgentSelection(defaultAgentName) {
|
|
442
|
-
const { render } = await import("ink");
|
|
443
|
-
const React = await import("react");
|
|
444
|
-
const { AgentSelector } = await import("./components/AgentSelector.js");
|
|
445
|
-
const agents = getAvailableAgents();
|
|
446
|
-
// Store the result to return after Ink exits
|
|
447
|
-
let selectedResult = null;
|
|
448
|
-
try {
|
|
449
|
-
const { unmount, waitUntilExit, clear } = render(React.createElement(AgentSelector, {
|
|
450
|
-
agents,
|
|
451
|
-
defaultAgentName,
|
|
452
|
-
onComplete: (result) => {
|
|
453
|
-
selectedResult = result.agent;
|
|
454
|
-
// Always save the selection for next time
|
|
455
|
-
try {
|
|
456
|
-
if (result.agent) {
|
|
457
|
-
saveUserConfig({ defaultAgent: result.agent });
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
// User chose "No agent" - clear the saved default
|
|
461
|
-
saveUserConfig({ defaultAgent: undefined });
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
catch (_error) {
|
|
465
|
-
console.warn(chalk.yellow("Warning: Could not save agent preference"));
|
|
466
|
-
}
|
|
467
|
-
// Clear the Ink output and unmount
|
|
468
|
-
clear();
|
|
469
|
-
unmount();
|
|
470
|
-
}
|
|
471
|
-
}));
|
|
472
|
-
// Wait for Ink to fully exit
|
|
473
|
-
await waitUntilExit();
|
|
474
|
-
// Clear terminal and scrollback to remove any Ink artifacts
|
|
475
|
-
process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
|
|
476
|
-
}
|
|
477
|
-
catch (error) {
|
|
478
|
-
console.error(chalk.red("Error in agent selection:"), error);
|
|
479
|
-
return null;
|
|
480
|
-
}
|
|
481
|
-
return selectedResult;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Show interactive skill selection prompt using Ink.
|
|
485
|
-
* Returns the selected skills and install location, or empty array if user skipped.
|
|
486
|
-
*/
|
|
487
|
-
async function promptPackageSelection(packages, agentId) {
|
|
488
|
-
const { render } = await import("ink");
|
|
489
|
-
const React = await import("react");
|
|
490
|
-
const { PackageSelector } = await import("./components/PackageSelector.js");
|
|
491
|
-
let selectedPackages = [];
|
|
492
|
-
let installLocation = "project";
|
|
493
|
-
try {
|
|
494
|
-
const { unmount, waitUntilExit, clear } = render(React.createElement(PackageSelector, {
|
|
495
|
-
packages,
|
|
496
|
-
agentId,
|
|
497
|
-
onComplete: (selected, location) => {
|
|
498
|
-
selectedPackages = selected;
|
|
499
|
-
installLocation = location;
|
|
500
|
-
clear();
|
|
501
|
-
unmount();
|
|
502
|
-
},
|
|
503
|
-
onSkip: () => {
|
|
504
|
-
clear();
|
|
505
|
-
unmount();
|
|
506
|
-
}
|
|
507
|
-
}));
|
|
508
|
-
await waitUntilExit();
|
|
509
|
-
// Clear terminal and scrollback to remove any Ink artifacts
|
|
510
|
-
process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
|
|
511
|
-
}
|
|
512
|
-
catch (error) {
|
|
513
|
-
console.error(chalk.red("Error in package selection:"), error);
|
|
514
|
-
return { packages: [], location: "project" };
|
|
515
|
-
}
|
|
516
|
-
return { packages: selectedPackages, location: installLocation };
|
|
517
|
-
}
|
|
518
|
-
function detectPythonCommand(debug = false) {
|
|
519
|
-
// Check if we're in a virtual environment
|
|
520
|
-
if (process.env.VIRTUAL_ENV) {
|
|
521
|
-
if (debug) {
|
|
522
|
-
console.log(`[DEBUG] Virtual environment detected: ${process.env.VIRTUAL_ENV}`);
|
|
523
|
-
console.log(`[DEBUG] Using activated python command`);
|
|
524
|
-
}
|
|
525
|
-
return "python";
|
|
526
|
-
}
|
|
527
|
-
// Check if python3 is available and prefer it
|
|
528
|
-
try {
|
|
529
|
-
execSync("python3 --version", { stdio: "ignore" });
|
|
530
|
-
if (debug) {
|
|
531
|
-
console.log(`[DEBUG] python3 is available, using python3`);
|
|
532
|
-
}
|
|
533
|
-
return "python3";
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
if (debug) {
|
|
537
|
-
console.log(`[DEBUG] python3 not available, falling back to python`);
|
|
538
|
-
}
|
|
539
|
-
return "python";
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
async function detectProjectType(debug = false) {
|
|
543
|
-
// Helper to check if package.json has a dev script (indicates Node.js project)
|
|
544
|
-
const hasNodeDevScript = () => {
|
|
545
|
-
try {
|
|
546
|
-
if (existsSync("package.json")) {
|
|
547
|
-
const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
548
|
-
return !!packageJson.scripts?.dev;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
catch {
|
|
552
|
-
// Ignore parse errors
|
|
553
|
-
}
|
|
554
|
-
return false;
|
|
555
|
-
};
|
|
556
|
-
// Check for Node.js project FIRST if package.json has a dev script
|
|
557
|
-
// This takes priority over Python/Rails detection for hybrid projects
|
|
558
|
-
const detected = await detect();
|
|
559
|
-
if (detected && hasNodeDevScript()) {
|
|
560
|
-
if (debug) {
|
|
561
|
-
console.log(`[DEBUG] Node.js project detected (package.json with dev script takes priority)`);
|
|
562
|
-
}
|
|
563
|
-
// Continue to Node.js detection below
|
|
564
|
-
}
|
|
565
|
-
else {
|
|
566
|
-
// Check for Python project (only if no Node.js dev script)
|
|
567
|
-
if (existsSync("requirements.txt") || existsSync("pyproject.toml")) {
|
|
568
|
-
if (debug) {
|
|
569
|
-
console.log(`[DEBUG] Python project detected (found requirements.txt or pyproject.toml)`);
|
|
570
|
-
}
|
|
571
|
-
return {
|
|
572
|
-
type: "python",
|
|
573
|
-
defaultScript: "main.py",
|
|
574
|
-
defaultPort: "8000", // Common Python web server port
|
|
575
|
-
pythonCommand: detectPythonCommand(debug)
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
// Check for Rails project
|
|
579
|
-
if (existsSync("Gemfile") && existsSync("config/application.rb")) {
|
|
580
|
-
if (debug) {
|
|
581
|
-
console.log(`[DEBUG] Rails project detected (found Gemfile and config/application.rb)`);
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
type: "rails",
|
|
585
|
-
defaultScript: "server",
|
|
586
|
-
defaultPort: "3000" // Rails default port
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
// Helper to detect framework for Node.js projects
|
|
591
|
-
const detectFramework = () => {
|
|
592
|
-
// Check for Next.js
|
|
593
|
-
const nextConfigFiles = ["next.config.js", "next.config.ts", "next.config.mjs", "next.config.cjs"];
|
|
594
|
-
if (nextConfigFiles.some((file) => existsSync(file))) {
|
|
595
|
-
if (debug) {
|
|
596
|
-
console.log(`[DEBUG] Next.js framework detected`);
|
|
597
|
-
}
|
|
598
|
-
return "nextjs";
|
|
599
|
-
}
|
|
600
|
-
// Check for Svelte - look for svelte.config.js or svelte dependency
|
|
601
|
-
if (existsSync("svelte.config.js")) {
|
|
602
|
-
if (debug) {
|
|
603
|
-
console.log(`[DEBUG] Svelte framework detected (svelte.config.js)`);
|
|
604
|
-
}
|
|
605
|
-
return "svelte";
|
|
606
|
-
}
|
|
607
|
-
// Check package.json for svelte dependency
|
|
608
|
-
try {
|
|
609
|
-
if (existsSync("package.json")) {
|
|
610
|
-
const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
611
|
-
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
612
|
-
if (deps.svelte || deps["@sveltejs/kit"]) {
|
|
613
|
-
if (debug) {
|
|
614
|
-
console.log(`[DEBUG] Svelte framework detected (package.json dependency)`);
|
|
615
|
-
}
|
|
616
|
-
return "svelte";
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
catch {
|
|
621
|
-
// Ignore parse errors
|
|
622
|
-
}
|
|
623
|
-
return "other";
|
|
624
|
-
};
|
|
625
|
-
if (detected) {
|
|
626
|
-
const framework = detectFramework();
|
|
627
|
-
if (debug) {
|
|
628
|
-
console.log(`[DEBUG] Node.js project detected with ${detected.agent} package manager and ${framework} framework`);
|
|
629
|
-
}
|
|
630
|
-
return {
|
|
631
|
-
type: "node",
|
|
632
|
-
framework,
|
|
633
|
-
packageManager: detected.agent,
|
|
634
|
-
defaultScript: "dev",
|
|
635
|
-
defaultPort: "3000"
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
// Check if this is a valid project directory
|
|
639
|
-
// If we get here, no lock files or project markers were found
|
|
640
|
-
// Check if package.json exists - if not, this isn't a valid project directory
|
|
641
|
-
if (!existsSync("package.json")) {
|
|
642
|
-
if (debug) {
|
|
643
|
-
console.log(`[DEBUG] No project files detected - not a valid project directory`);
|
|
644
|
-
}
|
|
645
|
-
return {
|
|
646
|
-
type: "node",
|
|
647
|
-
framework: "other",
|
|
648
|
-
packageManager: "npm",
|
|
649
|
-
defaultScript: "dev",
|
|
650
|
-
defaultPort: "3000",
|
|
651
|
-
noProjectDetected: true // Flag to indicate no project was found
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
// Fallback to npm for Node.js (package.json exists but no lock file)
|
|
655
|
-
const framework = detectFramework();
|
|
656
|
-
if (debug) {
|
|
657
|
-
console.log(`[DEBUG] Node.js project detected (package.json exists, no lock file), defaulting to npm and ${framework} framework`);
|
|
658
|
-
}
|
|
659
|
-
return {
|
|
660
|
-
type: "node",
|
|
661
|
-
framework,
|
|
662
|
-
packageManager: "npm",
|
|
663
|
-
defaultScript: "dev",
|
|
664
|
-
defaultPort: "3000"
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
// Read version from package.json or use compile-time injected version
|
|
668
|
-
function getVersion() {
|
|
669
|
-
// Check for compile-time injected version first (for standalone binaries)
|
|
670
|
-
if (typeof __D3K_VERSION__ !== "undefined") {
|
|
671
|
-
return __D3K_VERSION__;
|
|
672
|
-
}
|
|
673
|
-
try {
|
|
674
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
675
|
-
const packageRoot = dirname(dirname(currentFile)); // Go up from dist/ to package root
|
|
676
|
-
const packageJsonPath = join(packageRoot, "package.json");
|
|
677
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
678
|
-
let version = packageJson.version;
|
|
679
|
-
// Use git to detect if we're in the dev3000 source repository
|
|
680
|
-
try {
|
|
681
|
-
const gitRemote = execSync("git remote get-url origin 2>/dev/null", {
|
|
682
|
-
cwd: packageRoot,
|
|
683
|
-
encoding: "utf8"
|
|
684
|
-
}).trim();
|
|
685
|
-
if (gitRemote.includes("vercel-labs/dev3000") && !version.includes("canary")) {
|
|
686
|
-
version += "-local";
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
catch {
|
|
690
|
-
// Not in git repo or no git - use version as-is
|
|
691
|
-
}
|
|
692
|
-
return version;
|
|
693
|
-
}
|
|
694
|
-
catch (_error) {
|
|
695
|
-
return "0.0.0"; // fallback
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
// Check if installed globally before proceeding
|
|
699
|
-
function checkGlobalInstall() {
|
|
700
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
701
|
-
const packageRoot = dirname(dirname(currentFile));
|
|
702
|
-
// Check common global install paths
|
|
703
|
-
const globalPaths = [
|
|
704
|
-
"/usr/local/lib/node_modules",
|
|
705
|
-
"/usr/lib/node_modules",
|
|
706
|
-
process.env.NPM_CONFIG_PREFIX && join(process.env.NPM_CONFIG_PREFIX, "lib/node_modules"),
|
|
707
|
-
process.env.PNPM_HOME,
|
|
708
|
-
process.platform === "win32" && process.env.APPDATA && join(process.env.APPDATA, "npm/node_modules"),
|
|
709
|
-
process.env.HOME && join(process.env.HOME, ".npm-global/lib/node_modules"),
|
|
710
|
-
process.env.HOME && join(process.env.HOME, ".pnpm"),
|
|
711
|
-
process.env.HOME && join(process.env.HOME, ".yarn/global/node_modules")
|
|
712
|
-
].filter(Boolean);
|
|
713
|
-
// Check if our package path contains any of these global paths
|
|
714
|
-
for (const globalPath of globalPaths) {
|
|
715
|
-
if (packageRoot.includes(globalPath)) {
|
|
716
|
-
return true;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Additional check: if we're in node_modules but not in a project's node_modules
|
|
720
|
-
if (packageRoot.includes("node_modules") && !existsSync(join(packageRoot, "..", "..", "..", "package.json"))) {
|
|
721
|
-
return true;
|
|
722
|
-
}
|
|
723
|
-
// If we're in dev (running from source), that's fine
|
|
724
|
-
if (!packageRoot.includes("node_modules")) {
|
|
725
|
-
return true;
|
|
726
|
-
}
|
|
727
|
-
return false;
|
|
728
|
-
}
|
|
729
|
-
// Perform the check
|
|
730
|
-
if (!checkGlobalInstall()) {
|
|
731
|
-
console.error(chalk.red("\n❌ Error: dev3000 must be installed globally.\n"));
|
|
732
|
-
console.error(chalk.white("This package won't work correctly as a local dependency.\n"));
|
|
733
|
-
console.error(chalk.cyan("To install globally, use one of these commands:"));
|
|
734
|
-
console.error(chalk.gray(" pnpm install -g dev3000"));
|
|
735
|
-
console.error(chalk.gray(" npm install -g dev3000"));
|
|
736
|
-
console.error(chalk.gray(" yarn global add dev3000\n"));
|
|
737
|
-
console.error(chalk.white("Then run 'd3k' or 'dev3000' from any project directory.\n"));
|
|
738
|
-
process.exit(1);
|
|
739
|
-
}
|
|
740
|
-
const program = new Command();
|
|
741
|
-
program
|
|
742
|
-
.name("dev3000")
|
|
743
|
-
.description("Agent-first local web debugging runtime with a managed browser")
|
|
744
|
-
.version(getVersion());
|
|
745
|
-
program
|
|
746
|
-
.description("Agent-first local web debugging runtime with a managed browser")
|
|
747
|
-
.option("-p, --port <port>", "Development server port (auto-detected by project type)")
|
|
748
|
-
.option("-s, --script <script>", "Script to run (e.g. dev, main.py) - auto-detected by project type")
|
|
749
|
-
.option("-c, --command <command>", "Custom command to run (overrides auto-detection and --script)")
|
|
750
|
-
.option("--startup-timeout <seconds>", "Seconds to wait for your app server to become reachable", "30")
|
|
751
|
-
.option("--browser-navigation-timeout <seconds>", "Seconds to wait for browser-initiated page navigation commands", "60")
|
|
752
|
-
.option("--profile-dir <dir>", "Chrome profile directory")
|
|
753
|
-
.option("--data-dir <dir>", "Override the per-project data directory (default: ~/.d3k/<project>/). Holds session.json, logs, screenshots, and the Chrome profile.")
|
|
754
|
-
.option("--log-file <path>", "Override the consolidated log file path (default: <data-dir>/logs/<timestamp>.log)")
|
|
755
|
-
.option("--screenshots-dir <dir>", "Override the screenshots directory (default: <data-dir>/screenshots)")
|
|
756
|
-
.option("--browser-tool <tool>", "Preferred local browser CLI: 'agent-browser'", "agent-browser")
|
|
757
|
-
.option("--browser <path>", "Full path to browser executable (e.g. for Arc: '/Applications/Arc.app/Contents/MacOS/Arc')")
|
|
758
|
-
.option("--app-url <url>", "URL to open in the monitored browser instead of the local app URL")
|
|
759
|
-
.option("--external-cdp-base <url>", "Existing CDP HTTP endpoint to connect to instead of launching Chrome")
|
|
760
|
-
.option("--servers-only", "Run servers only, skip browser launch")
|
|
761
|
-
.option("--debug", "Enable debug logging to console (automatically disables TUI)")
|
|
762
|
-
.option("-t, --tail", "Output consolidated logfile to terminal (like tail -f)")
|
|
763
|
-
.option("--tui", "Enable the interactive TUI dashboard (off by default)", false)
|
|
764
|
-
.option("--portless", "Use stable Portless URLs (default)", true)
|
|
765
|
-
.option("--no-portless", "Use a direct localhost URL instead of Portless")
|
|
766
|
-
.option("--date-time <format>", "Timestamp format: 'local' (default, e.g. 12:54:03 PM) or 'utc' (ISO string)", "local")
|
|
767
|
-
.option("--plugin-react-scan", "Enable react-scan performance monitoring for React applications")
|
|
768
|
-
.option("--headless", "Run Chrome in headless mode (for serverless/CI environments)")
|
|
769
|
-
.option("--auto-skills", "Automatically install recommended skills without prompts (headless-safe, installs to project skills dir)")
|
|
770
|
-
.option("--no-skills", "Disable skill installation and update checks")
|
|
771
|
-
.option("--with-agent <command>", 'Run an agent (e.g. claude) in split-screen mode using tmux. Example: --with-agent "claude"')
|
|
772
|
-
.option("--agent-name <name>", "Selected agent name (internal)")
|
|
773
|
-
.option("--no-agent", "Skip agent selection prompt and run d3k standalone")
|
|
774
|
-
.action(async (options) => {
|
|
775
|
-
if (options.dataDir) {
|
|
776
|
-
process.env.D3K_DATA_DIR = resolve(options.dataDir);
|
|
777
|
-
}
|
|
778
|
-
const projectName = getProjectDisplayName();
|
|
779
|
-
setTerminalTitle(projectName);
|
|
780
|
-
try {
|
|
781
|
-
if (options.port) {
|
|
782
|
-
options.port = validatePortOption(options.port);
|
|
783
|
-
}
|
|
784
|
-
if (options.script) {
|
|
785
|
-
options.script = validateScriptOption(options.script);
|
|
786
|
-
}
|
|
787
|
-
options.startupTimeout = validatePositiveIntegerOption("--startup-timeout", options.startupTimeout);
|
|
788
|
-
options.browserNavigationTimeout = validatePositiveIntegerOption("--browser-navigation-timeout", options.browserNavigationTimeout);
|
|
789
|
-
if (options.browserTool && options.browserTool !== "agent-browser") {
|
|
790
|
-
throw new Error("--browser-tool must be 'agent-browser'.");
|
|
791
|
-
}
|
|
792
|
-
options.browserTool = "agent-browser";
|
|
793
|
-
options.dateTime = validateDateTimeOption(options.dateTime || "local");
|
|
794
|
-
}
|
|
795
|
-
catch (error) {
|
|
796
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
797
|
-
console.error(chalk.red(`\n❌ ${message}\n`));
|
|
798
|
-
process.exit(1);
|
|
799
|
-
}
|
|
800
|
-
// Load user config early so it can be used for --with-agent and agent selection flows
|
|
801
|
-
const userConfig = loadUserConfig();
|
|
802
|
-
const agentDetection = detectAIAgent();
|
|
803
|
-
// Apply browser default from user config if not explicitly provided via CLI
|
|
804
|
-
const browserOption = options.browser || userConfig.browser;
|
|
805
|
-
// Handle --with-agent by spawning tmux with split panes
|
|
806
|
-
if (options.withAgent) {
|
|
807
|
-
await launchWithTmux(options.withAgent, {
|
|
808
|
-
port: options.port,
|
|
809
|
-
script: options.script,
|
|
810
|
-
command: options.command,
|
|
811
|
-
startupTimeout: options.startupTimeout,
|
|
812
|
-
browserNavigationTimeout: options.browserNavigationTimeout,
|
|
813
|
-
profileDir: options.profileDir,
|
|
814
|
-
browserTool: options.browserTool,
|
|
815
|
-
browser: browserOption,
|
|
816
|
-
appUrl: options.appUrl,
|
|
817
|
-
externalCdpBase: options.externalCdpBase,
|
|
818
|
-
serversOnly: options.serversOnly,
|
|
819
|
-
headless: options.headless,
|
|
820
|
-
dateTime: options.dateTime,
|
|
821
|
-
pluginReactScan: options.pluginReactScan,
|
|
822
|
-
agentName: options.agentName,
|
|
823
|
-
portless: options.portless
|
|
824
|
-
});
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
// Handle agent selection for split-screen mode (explicit with --tui)
|
|
828
|
-
// Skip unless --tui is provided, or if already inside tmux (to avoid nested prompts)
|
|
829
|
-
const insideTmux = !!process.env.TMUX;
|
|
830
|
-
let selectedAgent = null;
|
|
831
|
-
let didPromptAgentSelection = false;
|
|
832
|
-
let skillsAgentId = options.agentName
|
|
833
|
-
? getSkillsAgentId(options.agentName)
|
|
834
|
-
: agentDetection.agentId || null;
|
|
835
|
-
if (agentDetection.isAgent) {
|
|
836
|
-
if (options.tui) {
|
|
837
|
-
if (options.debug) {
|
|
838
|
-
console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-disabling TUI`);
|
|
839
|
-
}
|
|
840
|
-
options.tui = false;
|
|
841
|
-
}
|
|
842
|
-
if (agentDetection.agentId && options.skills !== false && !options.autoSkills) {
|
|
843
|
-
if (options.debug) {
|
|
844
|
-
console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-enabling project skill installs`);
|
|
845
|
-
}
|
|
846
|
-
options.autoSkills = true;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
if (process.stdin.isTTY && options.agent !== false && options.tui === true && !options.debug && !insideTmux) {
|
|
850
|
-
// Clear the terminal so d3k UI starts at the top of the screen
|
|
851
|
-
process.stdout.write("\x1B[2J\x1B[0f");
|
|
852
|
-
// Check if tmux is available before showing prompt
|
|
853
|
-
const tmuxAvailable = await isTmuxInstalled();
|
|
854
|
-
if (!tmuxAvailable) {
|
|
855
|
-
console.warn(chalk.yellow("⚠️ tmux not installed - agent split-screen mode unavailable"));
|
|
856
|
-
console.warn(chalk.gray(" Install tmux to enable: brew install tmux (macOS)"));
|
|
857
|
-
// Continue with normal startup
|
|
858
|
-
}
|
|
859
|
-
else {
|
|
860
|
-
// Always show prompt, pre-selecting the last-used option
|
|
861
|
-
const preferredAgentName = readProjectAgentName() || userConfig.defaultAgent?.name;
|
|
862
|
-
selectedAgent = await promptAgentSelection(preferredAgentName);
|
|
863
|
-
didPromptAgentSelection = true;
|
|
864
|
-
if (selectedAgent) {
|
|
865
|
-
if (selectedAgent.name === "debug") {
|
|
866
|
-
// User chose debug mode - enable debug and continue with normal startup
|
|
867
|
-
options.debug = true;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
else if (options.debug) {
|
|
871
|
-
console.log("[DEBUG] No agent selected, continuing with normal startup");
|
|
872
|
-
}
|
|
873
|
-
// User chose "No agent" or "debug" - continue with normal startup
|
|
874
|
-
}
|
|
875
|
-
const skillsAgentName = selectedAgent?.name && selectedAgent.name !== "debug"
|
|
876
|
-
? selectedAgent.name
|
|
877
|
-
: !didPromptAgentSelection
|
|
878
|
-
? userConfig.defaultAgent?.name
|
|
879
|
-
: undefined;
|
|
880
|
-
skillsAgentId = getSkillsAgentId(skillsAgentName);
|
|
881
|
-
if (skillsAgentId && options.skills !== false) {
|
|
882
|
-
const resolvedSkillsAgentId = skillsAgentId;
|
|
883
|
-
// Check for skill updates and offer new packages
|
|
884
|
-
try {
|
|
885
|
-
// Show loading message
|
|
886
|
-
process.stdout.write(chalk.gray(" Checking for skills...\r"));
|
|
887
|
-
// 1. Check for updates to existing skills
|
|
888
|
-
const { hasUpdates } = await checkForSkillUpdates();
|
|
889
|
-
// Clear the loading message line
|
|
890
|
-
process.stdout.write("\x1B[2K\r");
|
|
891
|
-
if (hasUpdates) {
|
|
892
|
-
// Show which packages have updates (use applicable packages since lock file may not exist)
|
|
893
|
-
const applicablePackages = getApplicablePackages();
|
|
894
|
-
console.log(chalk.cyan("📦 Skill updates available"));
|
|
895
|
-
for (const pkg of applicablePackages) {
|
|
896
|
-
console.log(chalk.gray(` • ${pkg.repo}`));
|
|
897
|
-
}
|
|
898
|
-
const { default: readline } = await import("readline");
|
|
899
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
900
|
-
const answer = await new Promise((resolve) => {
|
|
901
|
-
rl.question(chalk.white(" Update now? (Y/n) "), resolve);
|
|
902
|
-
});
|
|
903
|
-
rl.close();
|
|
904
|
-
if (answer.toLowerCase() !== "n") {
|
|
905
|
-
console.log(chalk.gray(" Updating skills..."));
|
|
906
|
-
const updateResult = await updateSkills();
|
|
907
|
-
if (updateResult.success) {
|
|
908
|
-
console.log(chalk.green(" ✓ Skills updated"));
|
|
909
|
-
}
|
|
910
|
-
else {
|
|
911
|
-
console.log(chalk.yellow(" ⚠ Some skills failed to update"));
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
console.log("");
|
|
915
|
-
}
|
|
916
|
-
// 2. Show all applicable packages with install status
|
|
917
|
-
// Skip if we just handled updates
|
|
918
|
-
if (!hasUpdates) {
|
|
919
|
-
const applicablePackages = getApplicablePackages();
|
|
920
|
-
const packagesWithStatus = applicablePackages.map((pkg) => ({
|
|
921
|
-
...pkg,
|
|
922
|
-
installed: isPackageInstalled(pkg, resolvedSkillsAgentId)
|
|
923
|
-
}));
|
|
924
|
-
const hasUninstalled = packagesWithStatus.some((p) => !p.installed);
|
|
925
|
-
// Only show package selector if there are packages to install
|
|
926
|
-
if (packagesWithStatus.length > 0 && hasUninstalled) {
|
|
927
|
-
const { packages: selectedPackages, location } = await promptPackageSelection(packagesWithStatus, resolvedSkillsAgentId);
|
|
928
|
-
if (selectedPackages.length > 0) {
|
|
929
|
-
const locationLabel = location === "global" ? "globally" : "to project";
|
|
930
|
-
const targetPath = getSkillsPathForLocation(resolvedSkillsAgentId, location);
|
|
931
|
-
if (targetPath) {
|
|
932
|
-
const displayPath = targetPath.path.replace(process.env.HOME || "", "~");
|
|
933
|
-
console.log(chalk.cyan(`Installing ${selectedPackages.length} skill package(s) ${locationLabel} → ${displayPath}...`));
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
console.log(chalk.cyan(`Installing ${selectedPackages.length} skill package(s) ${locationLabel}...`));
|
|
937
|
-
}
|
|
938
|
-
const results = { success: [], failed: [] };
|
|
939
|
-
for (let i = 0; i < selectedPackages.length; i++) {
|
|
940
|
-
const pkg = selectedPackages[i];
|
|
941
|
-
console.log(chalk.gray(` [${i + 1}/${selectedPackages.length}] ${pkg.displayName}...`));
|
|
942
|
-
const result = await installSkillPackage(pkg, location, resolvedSkillsAgentId);
|
|
943
|
-
if (result.success) {
|
|
944
|
-
results.success.push(pkg.displayName);
|
|
945
|
-
}
|
|
946
|
-
else {
|
|
947
|
-
results.failed.push(pkg.displayName);
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
if (results.success.length > 0) {
|
|
951
|
-
console.log(chalk.green(`✓ Installed: ${results.success.join(", ")}`));
|
|
952
|
-
}
|
|
953
|
-
if (results.failed.length > 0) {
|
|
954
|
-
console.log(chalk.yellow(`⚠ Failed: ${results.failed.join(", ")}`));
|
|
955
|
-
}
|
|
956
|
-
console.log("");
|
|
957
|
-
}
|
|
958
|
-
else {
|
|
959
|
-
// User skipped package installation, show skills are up to date
|
|
960
|
-
console.log(chalk.green("✓ Skills up to date"));
|
|
961
|
-
console.log("");
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
// No updates and no new packages - show success
|
|
966
|
-
console.log(chalk.green("✓ Skills up to date"));
|
|
967
|
-
console.log("");
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
catch {
|
|
972
|
-
// Show error briefly, then continue
|
|
973
|
-
console.log(chalk.yellow("⚠ Could not check for skill updates"));
|
|
974
|
-
console.log("");
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
if (tmuxAvailable && selectedAgent && selectedAgent.name !== "debug") {
|
|
978
|
-
// User selected an agent - launch with tmux after skills install
|
|
979
|
-
if (options.debug) {
|
|
980
|
-
console.log(`[DEBUG] Launching tmux with agent command: ${selectedAgent.command}`);
|
|
981
|
-
}
|
|
982
|
-
if (skillsAgentId === "claude-code" && options.skills !== false) {
|
|
983
|
-
ensureD3kSkill("claude-code");
|
|
984
|
-
}
|
|
985
|
-
// Clear screen and scrollback before launching tmux so when tmux exits, terminal is clean
|
|
986
|
-
process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
|
|
987
|
-
await launchWithTmux(selectedAgent.command, {
|
|
988
|
-
port: options.port,
|
|
989
|
-
script: options.script,
|
|
990
|
-
command: options.command,
|
|
991
|
-
startupTimeout: options.startupTimeout,
|
|
992
|
-
browserNavigationTimeout: options.browserNavigationTimeout,
|
|
993
|
-
profileDir: options.profileDir,
|
|
994
|
-
browserTool: options.browserTool,
|
|
995
|
-
browser: browserOption,
|
|
996
|
-
appUrl: options.appUrl,
|
|
997
|
-
externalCdpBase: options.externalCdpBase,
|
|
998
|
-
serversOnly: options.serversOnly,
|
|
999
|
-
headless: options.headless,
|
|
1000
|
-
dateTime: options.dateTime,
|
|
1001
|
-
pluginReactScan: options.pluginReactScan,
|
|
1002
|
-
agentName: selectedAgent.name,
|
|
1003
|
-
portless: options.portless
|
|
1004
|
-
});
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
if (options.autoSkills && options.skills !== false && !skillsAgentId) {
|
|
1009
|
-
skillsAgentId = agentDetection.agentId || "codex";
|
|
1010
|
-
}
|
|
1011
|
-
// Detect project type and configuration
|
|
1012
|
-
const projectConfig = await detectProjectType(options.debug);
|
|
1013
|
-
// Check if we're in a valid project directory
|
|
1014
|
-
if (projectConfig.noProjectDetected) {
|
|
1015
|
-
console.error(chalk.red("\n❌ No project detected in current directory.\n"));
|
|
1016
|
-
console.error(chalk.white("dev3000 requires a project with one of these files:"));
|
|
1017
|
-
console.error(chalk.gray(" • package.json (Node.js/JavaScript)"));
|
|
1018
|
-
console.error(chalk.gray(" • requirements.txt or pyproject.toml (Python)"));
|
|
1019
|
-
console.error(chalk.gray(" • Gemfile + config/application.rb (Rails)\n"));
|
|
1020
|
-
console.error(chalk.cyan("💡 To get started:"));
|
|
1021
|
-
console.error(chalk.gray(" • Navigate to an existing project directory, or"));
|
|
1022
|
-
console.error(chalk.gray(" • Create a new project (e.g., 'npx create-next-app@latest'), or"));
|
|
1023
|
-
console.error(chalk.gray(" • Use --command to run a custom command:\n"));
|
|
1024
|
-
console.error(chalk.yellow(` d3k --command "node server.js" -p 3000\n`));
|
|
1025
|
-
process.exit(1);
|
|
1026
|
-
}
|
|
1027
|
-
// Use defaults from project detection if not explicitly provided
|
|
1028
|
-
const port = options.port || projectConfig.defaultPort;
|
|
1029
|
-
const script = options.script || projectConfig.defaultScript;
|
|
1030
|
-
const userSetPort = options.port !== undefined;
|
|
1031
|
-
const startupTimeoutSeconds = Number.parseInt(options.startupTimeout, 10);
|
|
1032
|
-
const browserNavigationTimeoutSeconds = Number.parseInt(options.browserNavigationTimeout, 10);
|
|
1033
|
-
const browserTool = "agent-browser";
|
|
1034
|
-
if (Number.isNaN(startupTimeoutSeconds) || startupTimeoutSeconds <= 0) {
|
|
1035
|
-
console.error(chalk.red("\n❌ --startup-timeout must be a positive integer (seconds).\n"));
|
|
1036
|
-
process.exit(1);
|
|
1037
|
-
}
|
|
1038
|
-
if (Number.isNaN(browserNavigationTimeoutSeconds) || browserNavigationTimeoutSeconds <= 0) {
|
|
1039
|
-
console.error(chalk.red("\n❌ --browser-navigation-timeout must be a positive integer (seconds).\n"));
|
|
1040
|
-
process.exit(1);
|
|
1041
|
-
}
|
|
1042
|
-
// Generate server command based on custom command or project type
|
|
1043
|
-
let serverCommand;
|
|
1044
|
-
if (options.command) {
|
|
1045
|
-
// Use custom command if provided - this overrides everything
|
|
1046
|
-
serverCommand = options.command;
|
|
1047
|
-
if (options.debug) {
|
|
1048
|
-
console.log(`[DEBUG] Using custom command: ${serverCommand}`);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
else if (projectConfig.type === "python") {
|
|
1052
|
-
serverCommand = `${projectConfig.pythonCommand} ${script}`;
|
|
1053
|
-
// Python frameworks typically use --port or -p, but it varies by framework
|
|
1054
|
-
// For now, we'll let users handle Python port config manually
|
|
1055
|
-
}
|
|
1056
|
-
else if (projectConfig.type === "rails") {
|
|
1057
|
-
serverCommand = `bundle exec rails ${script}`;
|
|
1058
|
-
// Append port for Rails when user explicitly sets it
|
|
1059
|
-
if (userSetPort) {
|
|
1060
|
-
serverCommand += ` -p ${port}`;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
else {
|
|
1064
|
-
// Node.js project
|
|
1065
|
-
serverCommand = `${projectConfig.packageManager} run ${script}`;
|
|
1066
|
-
// Append port for Node.js projects when user explicitly sets it
|
|
1067
|
-
if (userSetPort) {
|
|
1068
|
-
// Different frameworks need different port argument syntax
|
|
1069
|
-
// Note: We don't use -- separator as it conflicts with scripts that already have args
|
|
1070
|
-
if (projectConfig.framework === "nextjs") {
|
|
1071
|
-
// Next.js uses -p or --port flag
|
|
1072
|
-
serverCommand += ` -p ${port}`;
|
|
1073
|
-
}
|
|
1074
|
-
else {
|
|
1075
|
-
// Other frameworks (Vite, etc.) typically use --port
|
|
1076
|
-
serverCommand += ` --port ${port}`;
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
// Check for circular dependency - detect if the script would invoke dev3000 itself
|
|
1081
|
-
// Skip this check if using a custom command
|
|
1082
|
-
if (projectConfig.type === "node" && !options.command) {
|
|
1083
|
-
try {
|
|
1084
|
-
const packageJson = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
|
|
1085
|
-
const scriptContent = packageJson.scripts?.[script];
|
|
1086
|
-
// Check if the script invokes dev3000 or d3k
|
|
1087
|
-
if (scriptContent && (scriptContent.includes("dev3000") || /\bd3k\b/.test(scriptContent))) {
|
|
1088
|
-
console.error(chalk.red(`\n❌ Circular dependency detected!`));
|
|
1089
|
-
console.error(chalk.yellow(` The "${script}" script in package.json calls dev3000, which would create an infinite loop.`));
|
|
1090
|
-
console.error(chalk.yellow(` Current script content: "${scriptContent}"`));
|
|
1091
|
-
console.error(chalk.yellow(`\n💡 Fix this by either:`));
|
|
1092
|
-
console.error(chalk.yellow(` 1. Change the "${script}" script to call your actual dev server`));
|
|
1093
|
-
console.error(chalk.yellow(` 2. Use dev3000 globally (run 'd3k' directly) instead of via package.json scripts`));
|
|
1094
|
-
console.error(chalk.yellow(` 3. Use a different script name that doesn't invoke dev3000 (e.g., '${script === "dev" ? "dev:next" : "dev:server"}')`));
|
|
1095
|
-
process.exit(1);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
catch (_error) {
|
|
1099
|
-
// Ignore errors reading package.json
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
if (options.debug) {
|
|
1103
|
-
console.log(`[DEBUG] Project type: ${projectConfig.type}`);
|
|
1104
|
-
console.log(`[DEBUG] Port: ${port} (${options.port ? "explicit" : "auto-detected"})`);
|
|
1105
|
-
console.log(`[DEBUG] Script: ${script} (${options.script ? "explicit" : "auto-detected"})`);
|
|
1106
|
-
console.log(`[DEBUG] Server command: ${serverCommand}`);
|
|
1107
|
-
}
|
|
1108
|
-
// Detect which command name was used (dev3000 or d3k)
|
|
1109
|
-
const executablePath = process.argv[1];
|
|
1110
|
-
const commandName = executablePath.endsWith("/d3k") || executablePath.includes("/d3k") ? "d3k" : "dev3000";
|
|
1111
|
-
try {
|
|
1112
|
-
const portlessEnabled = options.portless !== false;
|
|
1113
|
-
if (portlessEnabled) {
|
|
1114
|
-
const portlessStatus = getPortlessProxyStatus();
|
|
1115
|
-
if (portlessStatus.setupRequired) {
|
|
1116
|
-
console.log(chalk.cyan("Canonical Portless HTTPS routing is not ready; setting it up now..."));
|
|
1117
|
-
const setupResult = await installPortlessService();
|
|
1118
|
-
if (!setupResult.success) {
|
|
1119
|
-
throw new Error(setupResult.error || "Portless setup failed");
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
// Create persistent log file
|
|
1124
|
-
const logFile = createPersistentLogFile(options.logFile);
|
|
1125
|
-
// Use a per-project Chrome profile by default so concurrent d3k sessions
|
|
1126
|
-
// don't fight over the same browser state. Honor explicit overrides.
|
|
1127
|
-
const profileDir = options.profileDir || join(getProjectDir(), "chrome-profile");
|
|
1128
|
-
// Find available Chrome debug port (starting from 9222)
|
|
1129
|
-
// Each d3k instance needs its own debug port to avoid conflicts
|
|
1130
|
-
const debugPort = await findAvailablePort(9222);
|
|
1131
|
-
await startDevEnvironment({
|
|
1132
|
-
...options,
|
|
1133
|
-
browser: browserOption,
|
|
1134
|
-
browserTool,
|
|
1135
|
-
appUrl: options.appUrl,
|
|
1136
|
-
externalCdpBase: options.externalCdpBase,
|
|
1137
|
-
port,
|
|
1138
|
-
debugPort: Number.parseInt(debugPort, 10),
|
|
1139
|
-
defaultPort: projectConfig.defaultPort,
|
|
1140
|
-
framework: projectConfig.framework,
|
|
1141
|
-
userSetPort,
|
|
1142
|
-
logFile,
|
|
1143
|
-
profileDir,
|
|
1144
|
-
serverCommand,
|
|
1145
|
-
debug: options.debug,
|
|
1146
|
-
serversOnly: options.serversOnly,
|
|
1147
|
-
commandName,
|
|
1148
|
-
startupTimeoutSeconds,
|
|
1149
|
-
browserNavigationTimeoutSeconds,
|
|
1150
|
-
tail: options.tail,
|
|
1151
|
-
// TUI is opt-in now; only enable when explicitly requested and not in debug
|
|
1152
|
-
tui: options.tui === true && !options.debug,
|
|
1153
|
-
portless: portlessEnabled,
|
|
1154
|
-
customServerCommand: Boolean(options.command),
|
|
1155
|
-
dateTimeFormat: options.dateTime || "local",
|
|
1156
|
-
pluginReactScan: options.pluginReactScan || false,
|
|
1157
|
-
agentName: options.agentName || undefined,
|
|
1158
|
-
skillsAgentId: skillsAgentId || undefined,
|
|
1159
|
-
autoSkills: options.skills !== false ? options.autoSkills || false : false,
|
|
1160
|
-
installSkills: options.skills !== false,
|
|
1161
|
-
screenshotsDir: options.screenshotsDir
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
catch (error) {
|
|
1165
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1166
|
-
console.error(chalk.red(`❌ Failed to start development environment: ${message}`));
|
|
1167
|
-
process.exit(1);
|
|
1168
|
-
}
|
|
1169
|
-
});
|
|
1170
|
-
const portlessCommand = program.command("portless").description("Set up the canonical port-free Portless proxy");
|
|
1171
|
-
portlessCommand
|
|
1172
|
-
.command("status")
|
|
1173
|
-
.description("Check whether canonical Portless HTTPS routing is ready")
|
|
1174
|
-
.option("--json", "Output machine-readable Portless status")
|
|
1175
|
-
.action((options) => {
|
|
1176
|
-
const status = getPortlessProxyStatus();
|
|
1177
|
-
if (options.json) {
|
|
1178
|
-
console.log(JSON.stringify(status, null, 2));
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
console.log(status.canonical
|
|
1182
|
-
? chalk.green("Canonical Portless HTTPS routing is ready.")
|
|
1183
|
-
: chalk.yellow("Portless setup is required."));
|
|
1184
|
-
if (status.target)
|
|
1185
|
-
console.log(chalk.gray(`Proxy target: ${status.target}`));
|
|
1186
|
-
console.log(chalk.gray(`Startup service: ${status.serviceInstalled ? "installed" : "not installed"}`));
|
|
1187
|
-
if (status.setupRequired)
|
|
1188
|
-
console.log(chalk.cyan("Run `d3k portless setup` once before starting d3k."));
|
|
1189
|
-
process.exitCode = status.setupRequired ? 1 : 0;
|
|
1190
|
-
});
|
|
1191
|
-
portlessCommand
|
|
1192
|
-
.command("setup")
|
|
1193
|
-
.description("Install the HTTPS Portless proxy as an OS startup service on port 443")
|
|
1194
|
-
.action(async () => {
|
|
1195
|
-
console.log(chalk.cyan("Installing the Portless HTTPS service on port 443..."));
|
|
1196
|
-
const result = await installPortlessService();
|
|
1197
|
-
if (!result.success) {
|
|
1198
|
-
console.error(chalk.red(`Portless service setup failed: ${result.error || "unknown error"}`));
|
|
1199
|
-
process.exit(1);
|
|
1200
|
-
}
|
|
1201
|
-
console.log(chalk.green("Portless is ready. d3k will now use URLs such as https://my-app.localhost."));
|
|
1202
|
-
});
|
|
1203
|
-
// Upgrade command
|
|
1204
|
-
program
|
|
1205
|
-
.command("upgrade")
|
|
1206
|
-
.description("Upgrade dev3000 to the latest version")
|
|
1207
|
-
.option("--check", "Only check for updates without upgrading")
|
|
1208
|
-
.action(async (options) => {
|
|
1209
|
-
console.log(chalk.cyan("Checking for updates...\n"));
|
|
1210
|
-
const versionInfo = await checkForUpdates();
|
|
1211
|
-
console.log(chalk.white(`Current version: ${chalk.yellow(versionInfo.currentVersion)}`));
|
|
1212
|
-
if (versionInfo.latestVersion) {
|
|
1213
|
-
console.log(chalk.white(`Latest version: ${chalk.green(versionInfo.latestVersion)}`));
|
|
1214
|
-
}
|
|
1215
|
-
else {
|
|
1216
|
-
console.log(chalk.gray("Could not fetch latest version from npm registry"));
|
|
1217
|
-
}
|
|
1218
|
-
if (!versionInfo.updateAvailable) {
|
|
1219
|
-
console.log(chalk.green("\n✓ You're already on the latest version!"));
|
|
1220
|
-
process.exit(0);
|
|
1221
|
-
}
|
|
1222
|
-
console.log(chalk.yellow(`\n↑ Update available: ${versionInfo.currentVersion} → ${versionInfo.latestVersion}`));
|
|
1223
|
-
if (options.check) {
|
|
1224
|
-
const upgradeCmd = getUpgradeCommand(versionInfo.packageManager);
|
|
1225
|
-
console.log(chalk.cyan(`\nTo upgrade, run: ${chalk.white(upgradeCmd)}`));
|
|
1226
|
-
console.log(chalk.gray(`Or run: ${chalk.white("d3k upgrade")}`));
|
|
1227
|
-
process.exit(0);
|
|
1228
|
-
}
|
|
1229
|
-
console.log("");
|
|
1230
|
-
const result = performUpgrade();
|
|
1231
|
-
if (result.success) {
|
|
1232
|
-
console.log(chalk.green("\n✓ Upgrade completed successfully!"));
|
|
1233
|
-
console.log(chalk.gray("Run 'd3k --version' to verify the new version."));
|
|
1234
|
-
}
|
|
1235
|
-
else {
|
|
1236
|
-
console.error(chalk.red(`\n✗ Upgrade failed: ${result.error}`));
|
|
1237
|
-
const upgradeCmd = getUpgradeCommand(versionInfo.packageManager);
|
|
1238
|
-
console.log(chalk.yellow(`\nTry running manually: ${chalk.white(upgradeCmd)}`));
|
|
1239
|
-
process.exit(1);
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
// Agent-browser command - registered for --help display only
|
|
1243
|
-
// Actual handling happens at the top of the file before Commander runs
|
|
1244
|
-
program
|
|
1245
|
-
.command("agent-browser [args...]")
|
|
1246
|
-
.description("Run the bundled agent-browser CLI. `open` requires d3k's managed browser unless --allow-new-browser is passed.")
|
|
1247
|
-
.allowUnknownOption(true);
|
|
1248
|
-
// Skill command - get skill content for use in prompts/workflows
|
|
1249
|
-
program
|
|
1250
|
-
.command("skill [name]")
|
|
1251
|
-
.description("Get skill content or list available skills")
|
|
1252
|
-
.option("-l, --list", "List all available skills")
|
|
1253
|
-
.option("-v, --verbose", "Show detailed skill information")
|
|
1254
|
-
.action(async (name, options) => {
|
|
1255
|
-
// List skills if --list flag or no name provided
|
|
1256
|
-
if (options.list || !name) {
|
|
1257
|
-
if (options.verbose) {
|
|
1258
|
-
const skills = getSkillsInfo();
|
|
1259
|
-
if (skills.length === 0) {
|
|
1260
|
-
console.log(chalk.yellow("No skills found."));
|
|
1261
|
-
console.log(chalk.gray("\nSkills are loaded from:"));
|
|
1262
|
-
console.log(chalk.gray(" • .agents/skills/ (project-local)"));
|
|
1263
|
-
console.log(chalk.gray(" • d3k built-in skills"));
|
|
1264
|
-
process.exit(0);
|
|
1265
|
-
}
|
|
1266
|
-
console.log(chalk.cyan("Available skills:\n"));
|
|
1267
|
-
for (const skill of skills) {
|
|
1268
|
-
console.log(chalk.white(` ${chalk.bold(skill.name)}`));
|
|
1269
|
-
console.log(chalk.gray(` ${skill.description}`));
|
|
1270
|
-
console.log(chalk.gray(` Path: ${skill.path}\n`));
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
else {
|
|
1274
|
-
const skills = listAvailableSkills();
|
|
1275
|
-
if (skills.length === 0) {
|
|
1276
|
-
console.log(chalk.yellow("No skills found."));
|
|
1277
|
-
process.exit(0);
|
|
1278
|
-
}
|
|
1279
|
-
console.log(chalk.cyan("Available skills:"));
|
|
1280
|
-
for (const skill of skills) {
|
|
1281
|
-
console.log(chalk.white(` • ${skill}`));
|
|
1282
|
-
}
|
|
1283
|
-
console.log(chalk.gray("\nUse 'd3k skill <name>' to get skill content"));
|
|
1284
|
-
console.log(chalk.gray("Use 'd3k skill --list --verbose' for details"));
|
|
1285
|
-
}
|
|
1286
|
-
process.exit(0);
|
|
1287
|
-
}
|
|
1288
|
-
// Get specific skill content
|
|
1289
|
-
const result = getSkill(name);
|
|
1290
|
-
if (!result.found) {
|
|
1291
|
-
console.error(chalk.red(`Error: ${result.error}`));
|
|
1292
|
-
if (result.availableSkills && result.availableSkills.length > 0) {
|
|
1293
|
-
console.log(chalk.yellow("\nAvailable skills:"));
|
|
1294
|
-
for (const skill of result.availableSkills) {
|
|
1295
|
-
console.log(chalk.gray(` • ${skill}`));
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
process.exit(1);
|
|
1299
|
-
}
|
|
1300
|
-
// Output skill content to stdout (no formatting, for piping/parsing)
|
|
1301
|
-
console.log(result.content);
|
|
1302
|
-
});
|
|
1303
|
-
// Errors command - quick view of recent errors
|
|
1304
|
-
program
|
|
1305
|
-
.command("errors")
|
|
1306
|
-
.description("Show recent errors from d3k logs (browser + server)")
|
|
1307
|
-
.option("-n, --count <count>", "Number of errors to show", "10")
|
|
1308
|
-
.option("-a, --all", "Show all errors, not just recent")
|
|
1309
|
-
.option("-c, --context", "Show interactions before each error (for replay)")
|
|
1310
|
-
.option("--json", "Output as JSON for parsing")
|
|
1311
|
-
.action(async (options) => {
|
|
1312
|
-
const { showErrors } = await import("./commands/errors.js");
|
|
1313
|
-
await showErrors(options);
|
|
1314
|
-
});
|
|
1315
|
-
// Logs command - view recent logs
|
|
1316
|
-
program
|
|
1317
|
-
.command("logs")
|
|
1318
|
-
.description("Show recent logs from d3k (browser + server)")
|
|
1319
|
-
.option("-n, --count <count>", "Number of lines to show", "50")
|
|
1320
|
-
.option("-t, --type <type>", "Filter by type: browser, server, network, all", "all")
|
|
1321
|
-
.option("--json", "Output as JSON for parsing")
|
|
1322
|
-
.action(async (options) => {
|
|
1323
|
-
const { showLogs } = await import("./commands/logs.js");
|
|
1324
|
-
await showLogs(options);
|
|
1325
|
-
});
|
|
1326
|
-
// Fix command - diagnose application errors from logs
|
|
1327
|
-
program
|
|
1328
|
-
.command("fix")
|
|
1329
|
-
.description("Diagnose application errors from d3k logs")
|
|
1330
|
-
.option("-f, --focus <area>", "Focus area: build, runtime, network, ui, all", "all")
|
|
1331
|
-
.option("-t, --time <minutes>", "Minutes to analyze back", "10")
|
|
1332
|
-
.option("--json", "Output as JSON for parsing")
|
|
1333
|
-
.action(async (options) => {
|
|
1334
|
-
const { fixMyApp } = await import("./commands/fix.js");
|
|
1335
|
-
await fixMyApp(options);
|
|
1336
|
-
});
|
|
1337
|
-
// Crawl command - discover app URLs
|
|
1338
|
-
program
|
|
1339
|
-
.command("crawl")
|
|
1340
|
-
.description("Discover URLs by crawling the app")
|
|
1341
|
-
.option("-d, --depth <depth>", "Crawl depth (1, 2, 3, or 'all')", "1")
|
|
1342
|
-
.option("-l, --limit <limit>", "Max links per page to follow", "3")
|
|
1343
|
-
.action(async (options) => {
|
|
1344
|
-
const { crawlApp } = await import("./commands/crawl.js");
|
|
1345
|
-
await crawlApp(options);
|
|
1346
|
-
});
|
|
1347
|
-
program
|
|
1348
|
-
.command("resume")
|
|
1349
|
-
.description("Resume the last supported AI agent session for this project")
|
|
1350
|
-
.action(async () => {
|
|
1351
|
-
const { resumeLastAgent } = await import("./commands/resume.js");
|
|
1352
|
-
await resumeLastAgent();
|
|
1353
|
-
});
|
|
1354
|
-
program
|
|
1355
|
-
.command("status")
|
|
1356
|
-
.description("Show whether this project's d3k runtime is ready")
|
|
1357
|
-
.option("--json", "Output machine-readable runtime details")
|
|
1358
|
-
.action(async (options) => {
|
|
1359
|
-
const { printD3kStatus } = await import("./commands/status.js");
|
|
1360
|
-
process.exitCode = printD3kStatus(options);
|
|
1361
|
-
});
|
|
1362
|
-
// CDP port command - get the CDP port from the session file
|
|
1363
|
-
program
|
|
1364
|
-
.command("cdp-port")
|
|
1365
|
-
.description("Output the CDP port for the current d3k session (for use in scripts)")
|
|
1366
|
-
.action(() => {
|
|
1367
|
-
console.log(getSessionCdpPort() || "9222");
|
|
1368
|
-
});
|
|
1369
|
-
program.parse();
|
|
1
|
+
import "./runtime/cli.js";
|
|
1370
2
|
//# sourceMappingURL=cli.js.map
|