@kodycodes/cli 1.25.1 → 1.26.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.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * `kody dev` bundles of `kody.app.entry`: same esbuild options as one-shot
3
3
  * local bundles, plus an in-memory source map (for mapping workerd stacks back
4
- * to `.ts`) and the list of input files (for the watcher's stat poll).
4
+ * to `.ts`) and the list of input files (for the watcher's stat poll). Uses an
5
+ * incremental esbuild context so rebuilds only reparse changed files.
5
6
  */
6
7
  import * as esbuild from 'esbuild';
7
8
  import { join, resolve } from 'node:path';
@@ -18,20 +19,36 @@ export function createDevBundler(input) {
18
19
  outfile: join(input.root, '__kody_dev_bundle.js'),
19
20
  };
20
21
  const paths = { workingDir: base.absWorkingDir, displayRoot: input.root };
22
+ let disposed = false;
23
+ let context = null;
21
24
  return {
22
25
  build: async () => {
23
26
  let result;
24
27
  try {
25
- result = await esbuild.build(options);
28
+ // A context created after dispose would never be released and keeps Node alive.
29
+ if (disposed)
30
+ throw new Error('kody dev bundler was already disposed.');
31
+ context ??= esbuild.context(options);
32
+ result = await (await context).rebuild();
26
33
  }
27
34
  catch (error) {
28
35
  throw createBundleError(input.entryPath, error, paths);
29
36
  }
30
37
  return readBundle(result, base.absWorkingDir, input.entryPath);
31
38
  },
32
- dispose: async () => { },
39
+ dispose: async () => {
40
+ disposed = true;
41
+ const current = context;
42
+ context = null;
43
+ await (await current?.catch(() => null))?.dispose();
44
+ },
33
45
  };
34
46
  }
47
+ export function bundleInputs(metafile, workingDir) {
48
+ return Object.keys(metafile?.inputs ?? {})
49
+ .filter((path) => !path.includes('node_modules') && !path.includes(':'))
50
+ .map((path) => resolve(workingDir, path));
51
+ }
35
52
  export function readBundle(result, workingDir, entryPath) {
36
53
  const outputs = result.outputFiles ?? [];
37
54
  const js = outputs.find((file) => file.path.endsWith('.js'));
@@ -39,13 +56,10 @@ export function readBundle(result, workingDir, entryPath) {
39
56
  if (!js) {
40
57
  throw new BundleError(`Could not bundle ${entryPath}: esbuild produced no output.`, []);
41
58
  }
42
- const inputs = Object.keys(result.metafile?.inputs ?? {})
43
- .filter((path) => !path.includes('node_modules') && !path.includes(':'))
44
- .map((path) => resolve(workingDir, path));
45
59
  return {
46
60
  code: js.text.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, '\n'),
47
61
  sourceMap: map?.text ?? null,
48
- inputs,
62
+ inputs: bundleInputs(result.metafile, workingDir),
49
63
  };
50
64
  }
51
65
  //# sourceMappingURL=dev-bundler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev-bundler.js","sourceRoot":"","sources":["../../src/dev/dev-bundler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAevF,MAAM,UAAU,gBAAgB,CAAC,KAA0C;IAC1E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACtC,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;IACtC,MAAM,OAAO,GAAG;QACf,GAAG,IAAI;QACP,SAAS,EAAE,UAAU;QACrB,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE,IAAI;QACd,2EAA2E;QAC3E,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC;KAClB,CAAA;IAChC,MAAM,KAAK,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;IAEzE,OAAO;QACN,KAAK,EAAE,KAAK,IAAI,EAAE;YACjB,IAAI,MAA2C,CAAA;YAC/C,IAAI,CAAC;gBACJ,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,iBAAiB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;YACvD,CAAC;YACD,OAAO,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;QAC/D,CAAC;QACD,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;KACvB,CAAA;AACF,CAAC;AAED,MAAM,UAAU,UAAU,CACzB,MAA6D,EAC7D,UAAkB,EAClB,SAAiB;IAEjB,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAA;IACxC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA;IACjE,IAAI,CAAC,EAAE,EAAE,CAAC;QACT,MAAM,IAAI,WAAW,CAAC,oBAAoB,SAAS,+BAA+B,EAAE,EAAE,CAAC,CAAA;IACxF,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;SACvD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;SACvE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAA;IAC1C,OAAO;QACN,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,kCAAkC,EAAE,IAAI,CAAC;QAC/D,SAAS,EAAE,GAAG,EAAE,IAAI,IAAI,IAAI;QAC5B,MAAM;KACN,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"dev-bundler.js","sourceRoot":"","sources":["../../src/dev/dev-bundler.ts"],"names":[],"mappings":"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"}
@@ -33,7 +33,7 @@ export type DevEvent = {
33
33
  request: DevRequestTag | null;
34
34
  } | {
35
35
  type: 'log';
36
- source: 'app' | 'workerd';
36
+ source: 'app' | 'workerd' | 'prebuild';
37
37
  level: DevLogLevel;
38
38
  message: string;
39
39
  request: DevRequestTag | null;
@@ -67,7 +67,7 @@ export function formatDevEvent(event) {
67
67
  text: `${formatRequestTag(event.request)} Uncaught ${event.stack || event.message}`,
68
68
  };
69
69
  case 'log': {
70
- const tag = event.source === 'workerd' ? '[workerd]' : formatRequestTag(event.request);
70
+ const tag = event.source === 'app' ? formatRequestTag(event.request) : `[${event.source}]`;
71
71
  const level = event.level === 'log' || event.level === 'info' ? '' : `${event.level}: `;
72
72
  const stream = event.level === 'error' || event.level === 'warn' ? 'stderr' : 'stdout';
73
73
  return { stream, text: `${tag} ${level}${event.message}` };
@@ -1 +1 @@
1
- {"version":3,"file":"dev-events.js","sourceRoot":"","sources":["../../src/dev/dev-events.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dev-events.js","sourceRoot":"","sources":["../../src/dev/dev-events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `kody dev` ports: each package gets a stable default (so bookmarks, OAuth
3
+ * redirect URIs, and agent notes keep working across restarts) and walks up to
4
+ * the next free port when that one is taken. An explicit `--port` is honored
5
+ * exactly and fails with an actionable message instead.
6
+ */
7
+ import type { Server } from 'node:http';
8
+ export declare const devPortRange: {
9
+ readonly min: 4100;
10
+ readonly max: 4999;
11
+ };
12
+ export declare function defaultDevPort(kodyId: string): number;
13
+ export declare function parseDevPort(raw: string): number;
14
+ /**
15
+ * Listen on `port` exactly when `explicit`, otherwise on `port` or the next
16
+ * free port above it. Returns the bound port.
17
+ */
18
+ export declare function listenDevServer(server: Server, input: {
19
+ host: string;
20
+ port: number;
21
+ explicit: boolean;
22
+ }): Promise<number>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * `kody dev` ports: each package gets a stable default (so bookmarks, OAuth
3
+ * redirect URIs, and agent notes keep working across restarts) and walks up to
4
+ * the next free port when that one is taken. An explicit `--port` is honored
5
+ * exactly and fails with an actionable message instead.
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import { StructuredCliError } from '../structured-error.js';
9
+ export const devPortRange = { min: 4100, max: 4999 };
10
+ const fallbackAttempts = 20;
11
+ export function defaultDevPort(kodyId) {
12
+ const span = devPortRange.max - devPortRange.min + 1;
13
+ const hash = createHash('sha256').update(kodyId).digest();
14
+ return devPortRange.min + (hash.readUInt32BE(0) % span);
15
+ }
16
+ export function parseDevPort(raw) {
17
+ const port = /^\d+$/.test(raw.trim()) ? Number(raw) : Number.NaN;
18
+ if (!Number.isInteger(port) || port > 65_535) {
19
+ throw new StructuredCliError({
20
+ code: 'invalid_port',
21
+ message: `--port must be an integer from 0 to 65535 (got "${raw}").`,
22
+ fix: 'Pass --port 0 for any free port, or omit --port to use this package’s stable default.',
23
+ });
24
+ }
25
+ return port;
26
+ }
27
+ /**
28
+ * Listen on `port` exactly when `explicit`, otherwise on `port` or the next
29
+ * free port above it. Returns the bound port.
30
+ */
31
+ export async function listenDevServer(server, input) {
32
+ const attempts = input.explicit || input.port === 0 ? 1 : fallbackAttempts;
33
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
34
+ const port = input.port === 0 ? 0 : wrapPort(input.port + attempt);
35
+ try {
36
+ await listenOnce(server, input.host, port);
37
+ return server.address().port;
38
+ }
39
+ catch (error) {
40
+ if (!isAddressInUse(error))
41
+ throw error;
42
+ if (input.explicit)
43
+ throw portInUseError(input.host, port);
44
+ }
45
+ }
46
+ throw new StructuredCliError({
47
+ code: 'dev_port_in_use',
48
+ message: `Ports ${input.port}–${wrapPort(input.port + attempts - 1)} on ${input.host} are all in use.`,
49
+ fix: 'Stop old `kody dev` processes, or pass --port 0 for any free port.',
50
+ });
51
+ }
52
+ function portInUseError(host, port) {
53
+ return new StructuredCliError({
54
+ code: 'dev_port_in_use',
55
+ message: `Port ${port} on ${host} is already in use.`,
56
+ fix: `Find the owner with \`lsof -nP -iTCP:${port} -sTCP:LISTEN\` and stop it, pass another --port, or omit --port to use this package’s default (it moves to the next free port automatically).`,
57
+ });
58
+ }
59
+ function wrapPort(port) {
60
+ return port > 65_535 ? devPortRange.min + (port - 65_536) : port;
61
+ }
62
+ function listenOnce(server, host, port) {
63
+ return new Promise((resolve, reject) => {
64
+ const onError = (error) => {
65
+ server.off('listening', onListening);
66
+ reject(error);
67
+ };
68
+ const onListening = () => {
69
+ server.off('error', onError);
70
+ resolve();
71
+ };
72
+ server.once('error', onError);
73
+ server.once('listening', onListening);
74
+ server.listen(port, host);
75
+ });
76
+ }
77
+ function isAddressInUse(error) {
78
+ return error?.code === 'EADDRINUSE';
79
+ }
80
+ //# sourceMappingURL=dev-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-port.js","sourceRoot":"","sources":["../../src/dev/dev-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `kody.dev.prebuild`: a shell command `kody dev` runs from the package root
3
+ * before every build, so generated assets (CSS, icons, data files) stay in
4
+ * sync with the source without a second watcher process.
5
+ */
6
+ export declare class PrebuildError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ export declare function runPrebuild(input: {
10
+ command: string;
11
+ cwd: string;
12
+ onSpawn?: (pid: number) => void;
13
+ onExit?: (pid: number) => void;
14
+ onLine?: (line: string, stream: 'stdout' | 'stderr') => void;
15
+ /** When aborted, the child is SIGKILLed so graceful `kody dev` shutdown is not blocked. */
16
+ signal?: AbortSignal;
17
+ }): Promise<void>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * `kody.dev.prebuild`: a shell command `kody dev` runs from the package root
3
+ * before every build, so generated assets (CSS, icons, data files) stay in
4
+ * sync with the source without a second watcher process.
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import { apiTokenEnvVar } from '../defaults.js';
8
+ const outputTailLines = 20;
9
+ export class PrebuildError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'PrebuildError';
13
+ }
14
+ }
15
+ export async function runPrebuild(input) {
16
+ if (input.signal?.aborted) {
17
+ throw new PrebuildError('kody.dev.prebuild aborted before start');
18
+ }
19
+ const env = { ...process.env, KODY_DEV: '1' };
20
+ delete env[apiTokenEnvVar];
21
+ // New process group so abort can SIGKILL the whole tree (sh + npm/node/…).
22
+ const child = spawn('sh', ['-c', input.command], {
23
+ cwd: input.cwd,
24
+ env,
25
+ stdio: ['ignore', 'pipe', 'pipe'],
26
+ detached: true,
27
+ });
28
+ if (child.pid)
29
+ input.onSpawn?.(child.pid);
30
+ const tail = [];
31
+ const collect = (stream) => (line) => {
32
+ tail.push(line);
33
+ if (tail.length > outputTailLines)
34
+ tail.shift();
35
+ input.onLine?.(line, stream);
36
+ };
37
+ forEachLine(child.stdout, collect('stdout'));
38
+ forEachLine(child.stderr, collect('stderr'));
39
+ const killChild = () => {
40
+ if (child.exitCode !== null || child.signalCode !== null)
41
+ return;
42
+ const pid = child.pid;
43
+ if (pid === undefined)
44
+ return;
45
+ try {
46
+ process.kill(-pid, 'SIGKILL');
47
+ }
48
+ catch {
49
+ try {
50
+ child.kill('SIGKILL');
51
+ }
52
+ catch {
53
+ // already gone
54
+ }
55
+ }
56
+ };
57
+ if (input.signal) {
58
+ if (input.signal.aborted)
59
+ killChild();
60
+ else
61
+ input.signal.addEventListener('abort', killChild, { once: true });
62
+ }
63
+ try {
64
+ const result = await new Promise((resolve, reject) => {
65
+ child.once('error', reject);
66
+ child.once('close', (code, signal) => resolve({ code, signal }));
67
+ });
68
+ if (input.signal?.aborted) {
69
+ throw new PrebuildError('kody.dev.prebuild aborted');
70
+ }
71
+ if (result.code === 0)
72
+ return;
73
+ const how = result.signal ? `signal ${result.signal}` : `exit ${result.code}`;
74
+ throw new PrebuildError([`kody.dev.prebuild failed (${how}): ${input.command}`, ...tail.map((line) => ` ${line}`)].join('\n'));
75
+ }
76
+ finally {
77
+ input.signal?.removeEventListener('abort', killChild);
78
+ if (child.pid)
79
+ input.onExit?.(child.pid);
80
+ }
81
+ }
82
+ /** Split stream chunks into lines, including a final incomplete line on end. */
83
+ function forEachLine(stream, onLine) {
84
+ if (!stream)
85
+ return;
86
+ let buffered = '';
87
+ stream.on('data', (chunk) => {
88
+ buffered += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
89
+ const lines = buffered.split('\n');
90
+ buffered = lines.pop() ?? '';
91
+ for (const line of lines) {
92
+ if (line.length > 0)
93
+ onLine(line);
94
+ }
95
+ });
96
+ stream.on('end', () => {
97
+ if (buffered.length > 0)
98
+ onLine(buffered);
99
+ buffered = '';
100
+ });
101
+ }
102
+ //# sourceMappingURL=prebuild.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prebuild.js","sourceRoot":"","sources":["../../src/dev/prebuild.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,9 @@ export declare const devWorkDirPrefix = "kody-dev-";
11
11
  export type ProcessReaper = {
12
12
  track: (pid: number) => void;
13
13
  untrack: (pid: number) => void;
14
+ /** Track a detached process-group leader; hard CLI death SIGKILLs the whole tree. */
15
+ trackGroup: (pid: number) => void;
16
+ untrackGroup: (pid: number) => void;
14
17
  close: () => void;
15
18
  };
16
19
  export declare function startProcessReaper(input: {
@@ -24,7 +24,12 @@ while IFS= read -r line; do
24
24
  -*) pids="$(printf '%s' "$pids" | sed "s/ \${line#-} / /g")" ;;
25
25
  esac
26
26
  done
27
- for pid in $pids; do kill -9 "$pid" 2>/dev/null; done
27
+ for entry in $pids; do
28
+ case "$entry" in
29
+ g*) /bin/kill -s KILL -- -\${entry#g} 2>/dev/null ;;
30
+ *) /bin/kill -s KILL -- \$entry 2>/dev/null ;;
31
+ esac
32
+ done
28
33
  rm -rf "$1"
29
34
  `;
30
35
  export async function startProcessReaper(input) {
@@ -52,6 +57,8 @@ export async function startProcessReaper(input) {
52
57
  return {
53
58
  track: (pid) => send(`+${pid}`),
54
59
  untrack: (pid) => send(`-${pid}`),
60
+ trackGroup: (pid) => send(`+g${pid}`),
61
+ untrackGroup: (pid) => send(`-g${pid}`),
55
62
  close: () => {
56
63
  reaper?.stdin?.end();
57
64
  reaper = undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"process-reaper.js","sourceRoot":"","sources":["../../src/dev/process-reaper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-reaper.js","sourceRoot":"","sources":["../../src/dev/process-reaper.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { type MockServer } from './mock/mock-server.js';
7
7
  import type { KodyTarget } from './target.js';
8
+ export { parseDevPort } from './dev/dev-port.js';
8
9
  export type DevIo = {
9
10
  stdout: (text: string) => void;
10
11
  stderr: (text: string) => void;
@@ -15,7 +15,9 @@ import { StructuredCliError } from './structured-error.js';
15
15
  import { formatToolResult } from './tool-result.js';
16
16
  import { bundleLocalUserModule } from './local-bundle.js';
17
17
  import { formatDevEvent } from './dev/dev-events.js';
18
+ import { defaultDevPort } from './dev/dev-port.js';
18
19
  import { devStatusPath, findRunningDevServer, removeDevRegistration, writeDevRegistration, } from './dev/dev-registry.js';
20
+ export { parseDevPort } from './dev/dev-port.js';
19
21
  export async function resolveDevAuth(input) {
20
22
  if (input.target !== 'mock') {
21
23
  return {
@@ -55,29 +57,71 @@ export async function resolveDevAuth(input) {
55
57
  }
56
58
  export async function runDevServe(input) {
57
59
  const manifest = await loadPackageManifest(input.cwd);
60
+ if (input.port === undefined) {
61
+ const running = await findRunningDevServer({ root: manifest.root, target: input.target });
62
+ if (running) {
63
+ const { registration } = running;
64
+ throw new StructuredCliError({
65
+ code: 'dev_already_running',
66
+ target: input.target,
67
+ message: `kody dev is already running for ${manifest.name} at ${registration.origin}${registration.basePath || '/'} (pid ${registration.pid}).`,
68
+ fix: `Reuse it (\`kody dev fetch <path>\` attaches automatically), stop it with \`kill ${registration.pid}\`, or pass --port to start a second server.`,
69
+ });
70
+ }
71
+ }
58
72
  const onEvent = input.json
59
73
  ? (event) => writeJsonEvent(input.io, event)
60
74
  : (event) => writeDevEvent(input.io, event);
61
- const server = await startPackageAppDev({
62
- manifest,
63
- token: input.auth.token,
64
- apiUrl: input.auth.apiUrl,
65
- target: input.target,
66
- port: input.port,
67
- appBasePath: input.appBasePath,
68
- ...(input.auth.org ? { org: input.auth.org } : {}),
69
- ...(input.auth.depResolve ? { depResolve: input.auth.depResolve } : {}),
70
- ...(input.passthroughHosts?.length
71
- ? { passthroughHosts: input.passthroughHosts }
72
- : input.auth.passthroughHosts?.length
73
- ? { passthroughHosts: input.auth.passthroughHosts }
74
- : {}),
75
- allowPrivateNetwork: input.allowPrivateNetwork,
76
- liveReload: input.liveReload ?? true,
77
- onStatus: (message) => input.io.stderr(`${message}\n`),
78
- onError: (message) => input.io.stderr(`${message}\n`),
79
- onEvent,
80
- });
75
+ let registered = false;
76
+ let server;
77
+ try {
78
+ server = await startPackageAppDev({
79
+ manifest,
80
+ token: input.auth.token,
81
+ apiUrl: input.auth.apiUrl,
82
+ target: input.target,
83
+ port: input.port,
84
+ defaultPort: defaultDevPort(manifest.kodyId),
85
+ appBasePath: input.appBasePath,
86
+ ...(input.auth.org ? { org: input.auth.org } : {}),
87
+ ...(input.auth.depResolve ? { depResolve: input.auth.depResolve } : {}),
88
+ ...(input.passthroughHosts?.length
89
+ ? { passthroughHosts: input.passthroughHosts }
90
+ : input.auth.passthroughHosts?.length
91
+ ? { passthroughHosts: input.auth.passthroughHosts }
92
+ : {}),
93
+ allowPrivateNetwork: input.allowPrivateNetwork,
94
+ liveReload: input.liveReload ?? true,
95
+ onStatus: (message) => input.io.stderr(`${message}\n`),
96
+ onError: (message) => input.io.stderr(`${message}\n`),
97
+ onEvent,
98
+ // Register as soon as the port is bound so a second `kody dev`
99
+ // during the first build hits `dev_already_running` instead of
100
+ // stealing the next free port and overwriting the registry.
101
+ // Explicit `--port` starts a second server on purpose — leave the
102
+ // primary attach registry alone so fetch keeps talking to it.
103
+ onListening: async ({ port, hostedUrl, appBasePath }) => {
104
+ if (input.port !== undefined)
105
+ return;
106
+ await writeDevRegistration({
107
+ pid: process.pid,
108
+ origin: hostedUrl,
109
+ port,
110
+ basePath: appBasePath,
111
+ target: input.target,
112
+ root: manifest.root,
113
+ });
114
+ registered = true;
115
+ },
116
+ });
117
+ }
118
+ catch (error) {
119
+ if (registered) {
120
+ await removeDevRegistration(manifest.root, process.pid, input.target).catch(() => { });
121
+ }
122
+ await input.auth.closeMock?.();
123
+ throw error;
124
+ }
81
125
  const shutdown = async () => {
82
126
  await server.close();
83
127
  await removeDevRegistration(manifest.root, process.pid, input.target).catch(() => { });
@@ -95,14 +139,6 @@ export async function runDevServe(input) {
95
139
  pid: process.pid,
96
140
  statusUrl,
97
141
  };
98
- await writeDevRegistration({
99
- pid: process.pid,
100
- origin: server.hostedUrl,
101
- port: server.port,
102
- basePath: server.appBasePath,
103
- target: input.target,
104
- root: manifest.root,
105
- }).catch(() => { });
106
142
  if (input.readyFile) {
107
143
  await mkdir(dirname(input.readyFile), { recursive: true });
108
144
  await writeFile(input.readyFile, `${JSON.stringify(ready, null, 2)}\n`);
@@ -127,7 +163,7 @@ export async function runDevServe(input) {
127
163
  }
128
164
  }
129
165
  catch (error) {
130
- // Ready-file / registration failures must not leave the listen handles up.
166
+ // Ready-file failures must not leave the listen handles up.
131
167
  await shutdown().catch(() => { });
132
168
  throw error;
133
169
  }