kandev 0.15.0 → 0.17.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/dist/args.js ADDED
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseError = void 0;
4
+ exports.parseArgs = parseArgs;
5
+ exports.resolvePorts = resolvePorts;
6
+ class ParseError extends Error {
7
+ }
8
+ exports.ParseError = ParseError;
9
+ function parseArgs(argv) {
10
+ const opts = { command: "run" };
11
+ let showHelp = false;
12
+ const deprecatedFlags = [];
13
+ const noteDeprecated = (flag) => {
14
+ if (!deprecatedFlags.includes(flag))
15
+ deprecatedFlags.push(flag);
16
+ };
17
+ for (let i = 0; i < argv.length; i += 1) {
18
+ const arg = argv[i];
19
+ if (arg === "--help" || arg === "-h") {
20
+ showHelp = true;
21
+ continue;
22
+ }
23
+ if (arg === "dev" || arg === "run" || arg === "start") {
24
+ opts.command = arg;
25
+ continue;
26
+ }
27
+ if (arg === "--version") {
28
+ opts.version = takeValue(argv, i, "--version");
29
+ i += 1;
30
+ continue;
31
+ }
32
+ if (arg.startsWith("--version=")) {
33
+ const value = arg.slice("--version=".length);
34
+ if (value.length === 0)
35
+ throw new ParseError("--version requires a value");
36
+ opts.version = value;
37
+ continue;
38
+ }
39
+ if (arg === "--dev") {
40
+ opts.command = "dev";
41
+ continue;
42
+ }
43
+ // --port is an alias for --backend-port (the user-facing port in run/start).
44
+ if (arg === "--port" || arg === "--backend-port") {
45
+ opts.backendPort = parsePort(takeValue(argv, i, arg), arg);
46
+ i += 1;
47
+ continue;
48
+ }
49
+ if (arg.startsWith("--port=") || arg.startsWith("--backend-port=")) {
50
+ const flag = arg.startsWith("--port=") ? "--port" : "--backend-port";
51
+ opts.backendPort = parsePort(arg.slice(flag.length + 1), flag);
52
+ continue;
53
+ }
54
+ if (arg === "--web-internal-port") {
55
+ opts.webPort = parsePort(takeValue(argv, i, "--web-internal-port"), "--web-internal-port");
56
+ i += 1;
57
+ continue;
58
+ }
59
+ if (arg.startsWith("--web-internal-port=")) {
60
+ opts.webPort = parsePort(arg.slice("--web-internal-port=".length), "--web-internal-port");
61
+ continue;
62
+ }
63
+ if (arg === "--web-port") {
64
+ opts.webPort = parsePort(takeValue(argv, i, "--web-port"), "--web-port");
65
+ noteDeprecated("--web-port");
66
+ i += 1;
67
+ continue;
68
+ }
69
+ if (arg.startsWith("--web-port=")) {
70
+ opts.webPort = parsePort(arg.slice("--web-port=".length), "--web-port");
71
+ noteDeprecated("--web-port");
72
+ continue;
73
+ }
74
+ if (arg === "--verbose" || arg === "-v") {
75
+ opts.verbose = true;
76
+ continue;
77
+ }
78
+ if (arg === "--debug") {
79
+ opts.debug = true;
80
+ continue;
81
+ }
82
+ }
83
+ return { options: opts, showHelp, deprecatedFlags };
84
+ }
85
+ function takeValue(argv, i, flag) {
86
+ const v = argv[i + 1];
87
+ if (v === undefined || v.startsWith("-")) {
88
+ throw new ParseError(`${flag} requires a value`);
89
+ }
90
+ return v;
91
+ }
92
+ function parsePort(raw, flag) {
93
+ const n = Number(raw);
94
+ if (raw === "" || !Number.isInteger(n) || n < 1 || n > 65535) {
95
+ throw new ParseError(`${flag} value must be an integer between 1 and 65535, got "${raw}"`);
96
+ }
97
+ return n;
98
+ }
99
+ // CLI flags beat env vars; KANDEV_PORT is an alias for KANDEV_BACKEND_PORT.
100
+ function resolvePorts(options, env) {
101
+ return {
102
+ backendPort: options.backendPort ?? envPort(env, "KANDEV_BACKEND_PORT") ?? envPort(env, "KANDEV_PORT"),
103
+ webPort: options.webPort ?? envPort(env, "KANDEV_WEB_PORT"),
104
+ };
105
+ }
106
+ function envPort(env, name) {
107
+ const val = env[name];
108
+ if (val === undefined)
109
+ return undefined;
110
+ const n = Number(val);
111
+ if (val === "" || !Number.isInteger(n) || n < 1 || n > 65535) {
112
+ throw new ParseError(`${name} must be an integer between 1 and 65535, got "${val}"`);
113
+ }
114
+ return n;
115
+ }
package/dist/cli.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const node_path_1 = __importDefault(require("node:path"));
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const package_json_1 = __importDefault(require("../package.json"));
9
+ const args_1 = require("./args");
9
10
  const dev_1 = require("./dev");
10
11
  const run_1 = require("./run");
11
12
  const start_1 = require("./start");
@@ -15,11 +16,11 @@ function printHelp() {
15
16
  console.log(`kandev launcher
16
17
 
17
18
  Usage:
18
- kandev run [--version <tag>] [--backend-port <port>] [--web-port <port>] [--verbose] [--debug]
19
- kandev dev [--backend-port <port>] [--web-port <port>]
20
- kandev start [--backend-port <port>] [--web-port <port>] [--verbose] [--debug]
21
- kandev [--version <tag>] [--backend-port <port>] [--web-port <port>] [--verbose] [--debug]
22
- kandev --dev [--backend-port <port>] [--web-port <port>]
19
+ kandev run [--version <tag>] [--port <port>] [--verbose] [--debug]
20
+ kandev dev [--port <port>]
21
+ kandev start [--port <port>] [--verbose] [--debug]
22
+ kandev [--version <tag>] [--port <port>] [--verbose] [--debug]
23
+ kandev --dev [--port <port>]
23
24
 
24
25
  Examples:
25
26
  kandev
@@ -28,80 +29,30 @@ Examples:
28
29
  kandev dev
29
30
  kandev start
30
31
  kandev --version v0.1.0
31
- kandev --backend-port 18080 --web-port 13000
32
+ kandev --port 3000
32
33
  kandev --debug
33
34
 
34
35
  Options:
35
- dev Use local repo for dev (make dev + next dev) if available.
36
- start Use local production build (make build + next start).
37
- run Use release bundles (default).
36
+ dev Use local repo for dev (make dev + next dev) if available.
37
+ start Use local production build (make build + next start).
38
+ run Use release bundles (default).
38
39
  --dev Alias for "dev".
39
40
  --version Release tag to install (default: latest).
40
- --backend-port Override backend port (or KANDEV_BACKEND_PORT env var).
41
- --web-port Override web port (or KANDEV_WEB_PORT env var).
41
+ --port Port for the Go backend (the URL kandev opens on in
42
+ start/run). Alias for --backend-port. Also reads
43
+ KANDEV_PORT or KANDEV_BACKEND_PORT.
42
44
  --verbose, -v Show info logs from backend + web.
43
45
  --debug Show debug logs + agent message dumps.
44
46
  --help, -h Show help.
47
+
48
+ Advanced:
49
+ --backend-port Same as --port.
50
+ --web-internal-port Override the internal Next.js port. The Go backend
51
+ reverse-proxies to it; users hit the backend port.
52
+ Also reads KANDEV_WEB_PORT.
53
+ --web-port Deprecated alias for --web-internal-port.
45
54
  `);
46
55
  }
47
- function parseArgs(argv) {
48
- const opts = { command: "run" };
49
- for (let i = 0; i < argv.length; i += 1) {
50
- const arg = argv[i];
51
- if (arg === "--help" || arg === "-h") {
52
- printHelp();
53
- process.exit(0);
54
- }
55
- if (arg === "dev" || arg === "run" || arg === "start") {
56
- opts.command = arg;
57
- continue;
58
- }
59
- if (arg === "--version") {
60
- opts.version = argv[i + 1];
61
- i += 1;
62
- continue;
63
- }
64
- if (arg.startsWith("--version=")) {
65
- opts.version = arg.split("=")[1];
66
- continue;
67
- }
68
- if (arg === "--dev") {
69
- opts.command = "dev";
70
- continue;
71
- }
72
- if (arg === "--backend-port") {
73
- opts.backendPort = Number(argv[i + 1]);
74
- i += 1;
75
- continue;
76
- }
77
- if (arg.startsWith("--backend-port=")) {
78
- opts.backendPort = Number(arg.split("=")[1]);
79
- continue;
80
- }
81
- if (arg === "--web-port") {
82
- opts.webPort = Number(argv[i + 1]);
83
- i += 1;
84
- continue;
85
- }
86
- if (arg.startsWith("--web-port=")) {
87
- opts.webPort = Number(arg.split("=")[1]);
88
- continue;
89
- }
90
- if (arg === "--verbose" || arg === "-v") {
91
- opts.verbose = true;
92
- continue;
93
- }
94
- if (arg === "--debug") {
95
- opts.debug = true;
96
- continue;
97
- }
98
- }
99
- return opts;
100
- }
101
- function envPort(name) {
102
- const val = process.env[name];
103
- return val ? Number(val) : undefined;
104
- }
105
56
  function findRepoRoot(startDir) {
106
57
  let current = node_path_1.default.resolve(startDir);
107
58
  while (true) {
@@ -125,10 +76,18 @@ function findRepoRoot(startDir) {
125
76
  }
126
77
  }
127
78
  async function main() {
128
- const raw = parseArgs(process.argv.slice(2));
129
- const backendPort = (0, ports_1.ensureValidPort)(raw.backendPort ?? envPort("KANDEV_BACKEND_PORT"), "backend port");
130
- const webPort = (0, ports_1.ensureValidPort)(raw.webPort ?? envPort("KANDEV_WEB_PORT"), "web port");
131
- if (raw.command === "dev") {
79
+ const { options, showHelp, deprecatedFlags } = (0, args_1.parseArgs)(process.argv.slice(2));
80
+ if (showHelp) {
81
+ printHelp();
82
+ return;
83
+ }
84
+ for (const flag of deprecatedFlags) {
85
+ process.stderr.write(`[kandev] ${flag} is deprecated; use --web-internal-port\n`);
86
+ }
87
+ const resolved = (0, args_1.resolvePorts)(options, process.env);
88
+ const backendPort = (0, ports_1.ensureValidPort)(resolved.backendPort, "backend port");
89
+ const webPort = (0, ports_1.ensureValidPort)(resolved.webPort, "web port");
90
+ if (options.command === "dev") {
132
91
  const repoRoot = findRepoRoot(process.cwd());
133
92
  if (!repoRoot) {
134
93
  throw new Error("Unable to locate repo root for dev. Run from the repo.");
@@ -136,24 +95,35 @@ async function main() {
136
95
  await (0, dev_1.runDev)({ repoRoot, backendPort, webPort });
137
96
  return;
138
97
  }
139
- if (raw.command === "start") {
98
+ if (options.command === "start") {
140
99
  const repoRoot = findRepoRoot(process.cwd());
141
100
  if (!repoRoot) {
142
101
  throw new Error("Unable to locate repo root for start. Run from the repo.");
143
102
  }
144
- await (0, start_1.runStart)({ repoRoot, backendPort, webPort, verbose: raw.verbose, debug: raw.debug });
103
+ await (0, start_1.runStart)({
104
+ repoRoot,
105
+ backendPort,
106
+ webPort,
107
+ verbose: options.verbose,
108
+ debug: options.debug,
109
+ });
145
110
  return;
146
111
  }
147
112
  await (0, update_1.maybePromptForUpdate)(package_json_1.default.version, process.argv.slice(2));
148
113
  await (0, run_1.runRelease)({
149
- version: raw.version,
114
+ version: options.version,
150
115
  backendPort,
151
116
  webPort,
152
- verbose: raw.verbose,
153
- debug: raw.debug,
117
+ verbose: options.verbose,
118
+ debug: options.debug,
154
119
  });
155
120
  }
156
121
  main().catch((err) => {
122
+ if (err instanceof args_1.ParseError) {
123
+ console.error(`[kandev] ${err.message}`);
124
+ console.error("[kandev] run --help for usage");
125
+ process.exit(2);
126
+ }
157
127
  console.error(`[kandev] ${err instanceof Error ? err.message : String(err)}`);
158
128
  process.exit(1);
159
129
  });
package/dist/constants.js CHANGED
@@ -3,14 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DATA_DIR = exports.CACHE_DIR = exports.HEALTH_TIMEOUT_MS_DEV = exports.HEALTH_TIMEOUT_MS_RELEASE = exports.RANDOM_PORT_RETRIES = exports.RANDOM_PORT_MAX = exports.RANDOM_PORT_MIN = exports.DEFAULT_MCP_PORT = exports.DEFAULT_AGENTCTL_PORT = exports.DEFAULT_WEB_PORT = exports.DEFAULT_BACKEND_PORT = void 0;
6
+ exports.DEV_KANDEV_DOTDIR = exports.DATA_DIR = exports.CACHE_DIR = exports.KANDEV_TASKS_DIR = exports.KANDEV_HOME_DIR = exports.KANDEV_DOTDIR = exports.HEALTH_TIMEOUT_MS_DEV = exports.HEALTH_TIMEOUT_MS_RELEASE = exports.RANDOM_PORT_RETRIES = exports.RANDOM_PORT_MAX = exports.RANDOM_PORT_MIN = exports.DEFAULT_AGENTCTL_PORT = exports.DEFAULT_WEB_PORT = exports.DEFAULT_BACKEND_PORT = void 0;
7
+ exports.devKandevHome = devKandevHome;
7
8
  const node_os_1 = __importDefault(require("node:os"));
8
9
  const node_path_1 = __importDefault(require("node:path"));
9
10
  // Default service ports (will auto-fallback if busy).
10
- exports.DEFAULT_BACKEND_PORT = 8080;
11
+ // Clustered near the web port (37429) to avoid collisions with commonly used
12
+ // ports (8080, 9090, 9999, etc.) while keeping the numbers memorable.
13
+ exports.DEFAULT_BACKEND_PORT = 38429;
11
14
  exports.DEFAULT_WEB_PORT = 37429;
12
- exports.DEFAULT_AGENTCTL_PORT = 9999;
13
- exports.DEFAULT_MCP_PORT = 9090;
15
+ exports.DEFAULT_AGENTCTL_PORT = 39429;
14
16
  // Random fallback range for port selection.
15
17
  exports.RANDOM_PORT_MIN = 10000;
16
18
  exports.RANDOM_PORT_MAX = 60000;
@@ -18,6 +20,19 @@ exports.RANDOM_PORT_RETRIES = 10;
18
20
  // Backend healthcheck timeout during startup.
19
21
  exports.HEALTH_TIMEOUT_MS_RELEASE = 45000;
20
22
  exports.HEALTH_TIMEOUT_MS_DEV = 600000;
23
+ // Kandev root directory. Single source of truth for the dotdir name and
24
+ // everything derived from it (data, tasks, bin). Dev mode uses a separate
25
+ // root under the repo (see DEV_KANDEV_DOTDIR).
26
+ exports.KANDEV_DOTDIR = ".kandev";
27
+ exports.KANDEV_HOME_DIR = node_path_1.default.join(node_os_1.default.homedir(), exports.KANDEV_DOTDIR);
28
+ exports.KANDEV_TASKS_DIR = node_path_1.default.join(exports.KANDEV_HOME_DIR, "tasks");
21
29
  // Local user cache/data directories for release bundles and DB.
22
- exports.CACHE_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".kandev", "bin");
23
- exports.DATA_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".kandev", "data");
30
+ exports.CACHE_DIR = node_path_1.default.join(exports.KANDEV_HOME_DIR, "bin");
31
+ exports.DATA_DIR = node_path_1.default.join(exports.KANDEV_HOME_DIR, "data");
32
+ // Dev-mode root: an isolated kandev home inside the repo so that running
33
+ // `make dev` from inside a kandev-spawned task workspace does not touch the
34
+ // user's production state.
35
+ exports.DEV_KANDEV_DOTDIR = ".kandev-dev";
36
+ function devKandevHome(repoRoot) {
37
+ return node_path_1.default.join(repoRoot, exports.DEV_KANDEV_DOTDIR);
38
+ }
package/dist/dev.js CHANGED
@@ -4,24 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runDev = runDev;
7
+ exports.resolveDevBackendEnv = resolveDevBackendEnv;
7
8
  const node_child_process_1 = require("node:child_process");
8
9
  const node_path_1 = __importDefault(require("node:path"));
9
10
  const constants_1 = require("./constants");
10
11
  const health_1 = require("./health");
12
+ const kandev_env_1 = require("./kandev-env");
11
13
  const process_1 = require("./process");
12
14
  const shared_1 = require("./shared");
13
15
  const web_1 = require("./web");
14
16
  async function runDev({ repoRoot, backendPort, webPort }) {
15
17
  const ports = await (0, shared_1.pickPorts)(backendPort, webPort);
16
- const dbPath = process.env.KANDEV_DATABASE_PATH || node_path_1.default.join(repoRoot, "apps", "backend", "kandev.db");
17
- const extra = {
18
- KANDEV_DATABASE_PATH: dbPath,
19
- KANDEV_MOCK_AGENT: process.env.KANDEV_MOCK_AGENT || "true",
20
- KANDEV_DEBUG_PPROF_ENABLED: "true",
21
- };
18
+ const { dbPath, extra } = resolveDevBackendEnv(repoRoot);
22
19
  const backendEnv = (0, shared_1.buildBackendEnv)({ ports, extra });
23
20
  const webEnv = (0, shared_1.buildWebEnv)({ ports, debug: true });
24
21
  const logLevel = process.env.KANDEV_LOGGING_LEVEL?.trim() || process.env.KANDEV_LOG_LEVEL?.trim() || "info";
22
+ const webUrl = `http://localhost:${ports.webPort}`;
25
23
  (0, shared_1.logStartupInfo)({
26
24
  header: "dev mode: using local repo",
27
25
  ports,
@@ -41,7 +39,6 @@ async function runDev({ repoRoot, backendPort, webPort }) {
41
39
  console.log("[kandev] starting backend...");
42
40
  await (0, health_1.waitForHealth)(ports.backendUrl, backendProc, healthTimeoutMs);
43
41
  console.log(`[kandev] backend ready at ${ports.backendUrl}`);
44
- const webUrl = `http://localhost:${ports.webPort}`;
45
42
  console.log("[kandev] starting web...");
46
43
  const webProc = (0, web_1.launchWebApp)({
47
44
  command: "pnpm",
@@ -52,6 +49,51 @@ async function runDev({ repoRoot, backendPort, webPort }) {
52
49
  label: "web",
53
50
  });
54
51
  await (0, health_1.waitForUrlReady)(webUrl, webProc, healthTimeoutMs);
55
- console.log(`[kandev] web ready at ${webUrl}`);
52
+ console.log(`[kandev] open: ${webUrl}`);
56
53
  (0, web_1.openBrowser)(webUrl);
57
54
  }
55
+ // Computes the dev-mode backend env. Dev mode always roots kandev under
56
+ // <repo>/.kandev-dev so state is isolated from the user's production ~/.kandev
57
+ // and so `make clean-db` (which removes .kandev-dev/) matches what `make dev`
58
+ // writes.
59
+ //
60
+ // When invoked from inside a kandev task workspace, any KANDEV_DATABASE_PATH
61
+ // is assumed to be leaked from the parent backend and is ignored. In a normal
62
+ // shell, an explicit KANDEV_DATABASE_PATH is honored as an escape hatch.
63
+ function resolveDevBackendEnv(repoRoot) {
64
+ const baseExtra = {
65
+ KANDEV_MOCK_AGENT: process.env.KANDEV_MOCK_AGENT || "true",
66
+ KANDEV_DEBUG_PPROF_ENABLED: "true",
67
+ };
68
+ const devHome = (0, constants_1.devKandevHome)(repoRoot);
69
+ // Display only; the backend derives its own DB path from KANDEV_HOME_DIR
70
+ // via ResolvedDataDir(). Both resolve to the same location.
71
+ const devDbPath = node_path_1.default.join(devHome, "data", "kandev.db");
72
+ if ((0, kandev_env_1.isInsideKandevTask)(repoRoot)) {
73
+ console.log("[kandev] task workspace detected → using local dev state");
74
+ return {
75
+ dbPath: devDbPath,
76
+ extra: {
77
+ ...baseExtra,
78
+ KANDEV_HOME_DIR: devHome,
79
+ // Clear a parent-leaked DB path so the backend uses the HomeDir-derived default.
80
+ KANDEV_DATABASE_PATH: "",
81
+ },
82
+ };
83
+ }
84
+ const override = process.env.KANDEV_DATABASE_PATH;
85
+ if (override) {
86
+ return {
87
+ dbPath: override,
88
+ extra: { ...baseExtra, KANDEV_DATABASE_PATH: override },
89
+ };
90
+ }
91
+ return {
92
+ dbPath: devDbPath,
93
+ extra: {
94
+ ...baseExtra,
95
+ KANDEV_HOME_DIR: devHome,
96
+ KANDEV_DATABASE_PATH: "",
97
+ },
98
+ };
99
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isInsideKandevTask = isInsideKandevTask;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const constants_1 = require("./constants");
9
+ /**
10
+ * Returns true when the current process looks like it was spawned inside a
11
+ * kandev-created task workspace. Two signals:
12
+ * 1. The parent kandev backend exports KANDEV_TASK_ID into every task shell.
13
+ * 2. Task worktrees live under ~/.kandev/tasks/ (see KANDEV_TASKS_DIR).
14
+ *
15
+ * Used by dev mode to auto-isolate the backend onto a local dev root so that
16
+ * `make dev` never mutates the user's production state.
17
+ *
18
+ * Note: the path-prefix fallback is a defensive secondary signal for nested
19
+ * shells where KANDEV_TASK_ID was stripped. It is case-sensitive and does not
20
+ * resolve symlinks, so a realpath'd repoRoot may miss a symlinked HOME on
21
+ * macOS / Windows. KANDEV_TASK_ID remains the primary guarantee.
22
+ */
23
+ function isInsideKandevTask(repoRoot) {
24
+ if (process.env.KANDEV_TASK_ID) {
25
+ return true;
26
+ }
27
+ return repoRoot.startsWith(constants_1.KANDEV_TASKS_DIR + node_path_1.default.sep);
28
+ }
package/dist/run.js CHANGED
@@ -189,9 +189,7 @@ function launchReleaseApps(prepared) {
189
189
  backendPort: Number(prepared.backendEnv.KANDEV_SERVER_PORT),
190
190
  webPort: prepared.webPort,
191
191
  agentctlPort: prepared.agentctlPort,
192
- mcpPort: 0,
193
192
  backendUrl: prepared.backendUrl,
194
- mcpUrl: "",
195
193
  },
196
194
  dbPath: prepared.dbPath,
197
195
  logLevel: prepared.logLevel,
@@ -247,6 +245,6 @@ async function runRelease({ version, backendPort, webPort, verbose = false, debu
247
245
  quiet: !prepared.showOutput,
248
246
  });
249
247
  await (0, health_1.waitForUrlReady)(webUrl, webProc, healthTimeoutMs);
250
- console.log("[kandev] ready at " + prepared.backendUrl);
248
+ console.log("[kandev] open: " + prepared.backendUrl);
251
249
  (0, web_1.openBrowser)(prepared.backendUrl);
252
250
  }
package/dist/shared.js CHANGED
@@ -24,14 +24,11 @@ async function pickPorts(backendPort, webPort) {
24
24
  const resolvedBackendPort = backendPort ?? (await (0, ports_1.pickAvailablePort)(constants_1.DEFAULT_BACKEND_PORT));
25
25
  const resolvedWebPort = webPort ?? (await (0, ports_1.pickAvailablePort)(constants_1.DEFAULT_WEB_PORT));
26
26
  const agentctlPort = await (0, ports_1.pickAvailablePort)(constants_1.DEFAULT_AGENTCTL_PORT);
27
- const mcpPort = await (0, ports_1.pickAvailablePort)(constants_1.DEFAULT_MCP_PORT);
28
27
  return {
29
28
  backendPort: resolvedBackendPort,
30
29
  webPort: resolvedWebPort,
31
30
  agentctlPort,
32
- mcpPort,
33
31
  backendUrl: `http://localhost:${resolvedBackendPort}`,
34
- mcpUrl: `http://localhost:${mcpPort}/sse`,
35
32
  };
36
33
  }
37
34
  /**
@@ -47,7 +44,6 @@ function buildBackendEnv(options) {
47
44
  KANDEV_SERVER_PORT: String(ports.backendPort),
48
45
  KANDEV_WEB_INTERNAL_URL: `http://localhost:${ports.webPort}`,
49
46
  KANDEV_AGENT_STANDALONE_PORT: String(ports.agentctlPort),
50
- KANDEV_AGENT_MCP_SERVER_PORT: String(ports.mcpPort),
51
47
  ...(logLevel ? { KANDEV_LOG_LEVEL: logLevel } : {}),
52
48
  ...extra,
53
49
  };
@@ -85,11 +81,13 @@ function buildWebEnv(options) {
85
81
  */
86
82
  function logStartupInfo(options) {
87
83
  const { header, ports, dbPath, logLevel } = options;
84
+ const backendUrl = ports.backendUrl;
85
+ const webUrl = `http://localhost:${ports.webPort}`;
88
86
  console.log(`[kandev] ${header}`);
89
- console.log("[kandev] url: http://localhost:" + ports.backendPort);
87
+ console.log("[kandev] backend:", backendUrl);
88
+ console.log("[kandev] web:", webUrl);
90
89
  console.log("[kandev] agentctl port:", ports.agentctlPort);
91
- console.log("[kandev] mcp port:", ports.mcpPort);
92
- console.log("[kandev] mcp url:", ports.mcpUrl);
90
+ console.log("[kandev] mcp url:", `${backendUrl}/mcp`);
93
91
  if (dbPath) {
94
92
  console.log("[kandev] db path:", dbPath);
95
93
  }
package/dist/start.js CHANGED
@@ -15,6 +15,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
15
  return (mod && mod.__esModule) ? mod : { "default": mod };
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.resolveStandaloneServerPath = resolveStandaloneServerPath;
18
19
  exports.runStart = runStart;
19
20
  const node_child_process_1 = require("node:child_process");
20
21
  const node_fs_1 = __importDefault(require("node:fs"));
@@ -25,6 +26,50 @@ const platform_1 = require("./platform");
25
26
  const process_1 = require("./process");
26
27
  const shared_1 = require("./shared");
27
28
  const web_1 = require("./web");
29
+ /**
30
+ * Locates the standalone Next.js `server.js` inside `apps/web/.next/standalone/`.
31
+ *
32
+ * Normally this sits at `.next/standalone/web/server.js`, but Turbopack may
33
+ * place it under a deeper path (e.g. `.next/standalone/Users/.../web/server.js`)
34
+ * when it detects the wrong project root (typically caused by a stray
35
+ * `package-lock.json` in a parent directory). We look for any `web/server.js`
36
+ * below the standalone directory so `kandev start` keeps working.
37
+ *
38
+ * @returns absolute path to `server.js`, or null if it cannot be found.
39
+ */
40
+ function resolveStandaloneServerPath(repoRoot) {
41
+ const standaloneDir = node_path_1.default.join(repoRoot, "apps", "web", ".next", "standalone");
42
+ const expected = node_path_1.default.join(standaloneDir, "web", "server.js");
43
+ if (node_fs_1.default.existsSync(expected))
44
+ return expected;
45
+ if (!node_fs_1.default.existsSync(standaloneDir))
46
+ return null;
47
+ return findWebServerJs(standaloneDir);
48
+ }
49
+ // Walks `dir` manually instead of relying on `Dirent.parentPath` (Node 20.12+),
50
+ // so the CLI keeps working on older Node 20.x runtimes installed via npx.
51
+ function findWebServerJs(dir) {
52
+ const stack = [dir];
53
+ while (stack.length > 0) {
54
+ const current = stack.pop();
55
+ let entries;
56
+ try {
57
+ entries = node_fs_1.default.readdirSync(current, { withFileTypes: true });
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ for (const entry of entries) {
63
+ if (entry.isDirectory() && entry.name !== "node_modules") {
64
+ stack.push(node_path_1.default.join(current, entry.name));
65
+ }
66
+ else if (entry.isFile() && entry.name === "server.js" && node_path_1.default.basename(current) === "web") {
67
+ return node_path_1.default.join(current, entry.name);
68
+ }
69
+ }
70
+ }
71
+ return null;
72
+ }
28
73
  /**
29
74
  * Runs the application in production mode using local builds.
30
75
  *
@@ -45,9 +90,16 @@ async function runStart({ repoRoot, backendPort, webPort, verbose = false, debug
45
90
  throw new Error("Backend binary not found. Run `make build` first.");
46
91
  }
47
92
  // Check for standalone build (Next.js standalone output)
48
- const webServerPath = node_path_1.default.join(repoRoot, "apps", "web", ".next", "standalone", "web", "server.js");
49
- if (!node_fs_1.default.existsSync(webServerPath)) {
50
- throw new Error("Web standalone build not found. Run `make build` first.");
93
+ const webServerPath = resolveStandaloneServerPath(repoRoot);
94
+ if (!webServerPath) {
95
+ const standaloneDir = node_path_1.default.join(repoRoot, "apps", "web", ".next", "standalone");
96
+ if (!node_fs_1.default.existsSync(standaloneDir)) {
97
+ throw new Error("Web standalone build not found. Run `make build` first.");
98
+ }
99
+ throw new Error(`Web standalone build is missing server.js under ${standaloneDir}. ` +
100
+ "This can happen when Next.js/Turbopack detects a different project root " +
101
+ "(e.g. a stray package-lock.json in a parent directory). " +
102
+ "Remove the stray lockfile and re-run `make build`.");
51
103
  }
52
104
  const webStandaloneDir = node_path_1.default.dirname(webServerPath);
53
105
  const webStaticDir = node_path_1.default.join(repoRoot, "apps", "web", ".next", "static");
@@ -119,6 +171,6 @@ async function runStart({ repoRoot, backendPort, webPort, verbose = false, debug
119
171
  quiet: !showOutput,
120
172
  });
121
173
  await (0, health_1.waitForUrlReady)(webUrl, webProc, healthTimeoutMs);
122
- console.log("[kandev] ready at " + ports.backendUrl);
174
+ console.log("[kandev] open: " + ports.backendUrl);
123
175
  (0, web_1.openBrowser)(ports.backendUrl);
124
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kandev",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "private": false,
5
5
  "description": "NPX launcher for Kandev",
6
6
  "license": "AGPL-3.0-only",
@@ -24,7 +24,7 @@
24
24
  "vitest": "^1.6.0"
25
25
  },
26
26
  "scripts": {
27
- "dev": "tsx src/cli.ts",
27
+ "dev": "unset npm_config_prefix && tsx src/cli.ts",
28
28
  "build": "tsc -p tsconfig.json",
29
29
  "start": "node dist/cli.js",
30
30
  "test": "vitest run",