specflow-dev-service 0.0.0-beta.11 → 0.0.0-beta.12
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/logger.js +0 -43
- package/dist/logger.js.map +1 -1
- package/dist/orchestrator.js +18 -14
- package/dist/orchestrator.js.map +1 -1
- package/package.json +1 -1
package/dist/logger.js
CHANGED
|
@@ -107,48 +107,6 @@ function logProgress(module, action, detail) {
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
/**
|
|
111
|
-
* 耗时操作 loading 转圈(在 TTY 中用 braille 字符 + 已用时间,pipe 中用 dots)
|
|
112
|
-
* @param module 模块名
|
|
113
|
-
* @param action 动作描述
|
|
114
|
-
* @param task 实际执行的任务
|
|
115
|
-
* @example const result = await withLoading('go', 'starting', () => startGoService());
|
|
116
|
-
*/
|
|
117
|
-
async function withLoading(module, action, task) {
|
|
118
|
-
const tag = `${c.blue}${icons.info}${c.reset}`;
|
|
119
|
-
const mod = `${c.bold}${padRight(module, 10)}${c.reset}`;
|
|
120
|
-
const act = `${c.dim}${action}${c.reset}`;
|
|
121
|
-
const isTTY = process.stdout.isTTY === true;
|
|
122
|
-
const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
123
|
-
const start = Date.now();
|
|
124
|
-
let i = 0;
|
|
125
|
-
let stopped = false;
|
|
126
|
-
const render = frame => {
|
|
127
|
-
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
128
|
-
const line = ` ${tag} ${mod} ${act} ${c.cyan}${frame}${c.reset} ${c.dim}${elapsed}s${c.reset}`;
|
|
129
|
-
if (isTTY) {
|
|
130
|
-
process.stdout.write(`\r\x1b[K${line}`);
|
|
131
|
-
} else {
|
|
132
|
-
process.stdout.write(`\r${line}`);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// 初始帧
|
|
137
|
-
render(isTTY ? FRAMES[0] : '·');
|
|
138
|
-
const timer = setInterval(() => {
|
|
139
|
-
if (stopped) return;
|
|
140
|
-
i++;
|
|
141
|
-
render(isTTY ? FRAMES[i % FRAMES.length] : '·');
|
|
142
|
-
}, 100);
|
|
143
|
-
try {
|
|
144
|
-
return await task();
|
|
145
|
-
} finally {
|
|
146
|
-
stopped = true;
|
|
147
|
-
clearInterval(timer);
|
|
148
|
-
if (isTTY) process.stdout.write('\r\x1b[K');
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
110
|
/**
|
|
153
111
|
* 文件变化通知(始终展示)
|
|
154
112
|
*/
|
|
@@ -309,5 +267,4 @@ exports.logWarn = logWarn;
|
|
|
309
267
|
exports.logWatch = logWatch;
|
|
310
268
|
exports.setLogLevel = setLogLevel;
|
|
311
269
|
exports.setVerbose = setVerbose;
|
|
312
|
-
exports.withLoading = withLoading;
|
|
313
270
|
//# sourceMappingURL=logger.js.map
|
package/dist/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sources":["../src/logger.ts"],"sourcesContent":["/**\n * 精简日志系统\n *\n * 设计原则:\n * - 默认模式:只显示进度条 + URL,不刷屏\n * - verbose 模式:展示全部详细信息\n * - 错误始终展示\n */\n\n// ============================================================\n// 颜色工具(轻量,不依赖 chalk)\n// ============================================================\n\nconst isColor = process.stdout.isTTY !== false;\nconst isTTY = process.stdout.isTTY === true;\n\nconst c = {\n reset: isColor ? '\\x1b[0m' : '',\n bold: isColor ? '\\x1b[1m' : '',\n dim: isColor ? '\\x1b[2m' : '',\n green: isColor ? '\\x1b[32m' : '',\n yellow: isColor ? '\\x1b[33m' : '',\n red: isColor ? '\\x1b[31m' : '',\n cyan: isColor ? '\\x1b[36m' : '',\n blue: isColor ? '\\x1b[34m' : '',\n magenta: isColor ? '\\x1b[35m' : '',\n gray: isColor ? '\\x1b[90m' : '',\n clearLine: isTTY ? '\\x1b[2K\\r' : '',\n};\n\n// ============================================================\n// 图标\n// ============================================================\n\nconst icons = {\n success: isColor ? '✓' : '[OK]',\n error: isColor ? '✗' : '[ERR]',\n warn: isColor ? '⚠' : '[WARN]',\n info: isColor ? '●' : '[*]',\n arrow: isColor ? '→' : '->',\n spinner: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\n// ============================================================\n// 模式控制\n// ============================================================\n\nexport type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nlet currentLevel: LogLevel = 'info';\nlet verboseMode = false;\n\n/** 设置日志级别 */\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\n/** 设置 verbose 模式(展示全部详细信息) */\nexport function setVerbose(v: boolean): void {\n verboseMode = v;\n}\n\n/** 是否为 verbose 模式 */\nexport function isVerbose(): boolean {\n return verboseMode;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] <= LOG_LEVELS[currentLevel];\n}\n\n// ============================================================\n// 进度条\n// ============================================================\n\nexport interface ProgressStep {\n label: string;\n status: 'pending' | 'running' | 'done' | 'error';\n detail?: string;\n}\n\n/**\n * 启动进度条\n *\n * 非 verbose 模式下:覆盖式单行显示进度\n * verbose 模式下:每步一行\n */\nexport class ProgressBar {\n private steps: ProgressStep[] = [];\n private spinnerFrame = 0;\n private spinnerTimer: ReturnType<typeof setInterval> | null = null;\n private startTime = Date.now();\n /** 记录哪些步骤已经在非 TTY 模式下打印过 */\n private printedStepIds = new Set<number>();\n /** 当前是否有未换行的进度行占着终端(TTY 覆盖模式) */\n private hasInlineLine = false;\n\n constructor(private title: string) {}\n\n /** 添加步骤 */\n addStep(label: string): number {\n const idx = this.steps.length;\n this.steps.push({ label, status: 'pending' });\n return idx;\n }\n\n /** 开始一个步骤 */\n startStep(idx: number, detail?: string): void {\n this.steps[idx].status = 'running';\n this.steps[idx].detail = detail;\n\n if (verboseMode) {\n this.printVerboseStep(idx);\n } else {\n this.renderCompact();\n }\n }\n\n /** 完成一个步骤 */\n completeStep(idx: number, detail?: string): void {\n this.steps[idx].status = 'done';\n if (detail !== undefined) this.steps[idx].detail = detail;\n\n if (verboseMode) {\n this.printVerboseStepDone(idx);\n } else {\n this.renderCompact();\n }\n }\n\n /** 步骤出错 */\n errorStep(idx: number, detail?: string): void {\n this.steps[idx].status = 'error';\n if (detail !== undefined) this.steps[idx].detail = detail;\n\n if (verboseMode) {\n this.printVerboseStepError(idx);\n } else {\n this.renderCompact();\n }\n }\n\n /** 启动动画(仅 TTY) */\n startSpinner(): void {\n if (!isTTY || verboseMode) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerFrame = (this.spinnerFrame + 1) % icons.spinner.length;\n this.renderCompact();\n }, 80);\n }\n\n /** 停止动画 */\n stopSpinner(): void {\n if (this.spinnerTimer) {\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n }\n\n /** 完成,打印最终摘要 */\n finish(): void {\n this.stopSpinner();\n const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);\n\n // 清掉 TTY 覆盖行(如果有的话)\n this.clearInline();\n\n // 打印完成行\n const doneCount = this.steps.filter((s) => s.status === 'done').length;\n const errCount = this.steps.filter((s) => s.status === 'error').length;\n\n if (errCount > 0) {\n console.log(\n ` ${c.yellow}${icons.warn}${c.reset} ${c.bold}${this.title}${c.reset} ${c.dim}${doneCount}/${this.steps.length} done, ${errCount} errors${c.reset} ${c.dim}${elapsed}s${c.reset}`,\n );\n } else {\n console.log(\n ` ${c.green}${icons.success}${c.reset} ${c.bold}${this.title}${c.reset} ${c.dim}${doneCount}/${this.steps.length} steps${c.reset} ${c.dim}${elapsed}s${c.reset}`,\n );\n }\n }\n\n // ---------- 内部方法 ----------\n\n /** 清除 TTY 模式下的覆盖行 */\n private clearInline(): void {\n if (this.hasInlineLine && isTTY) {\n process.stdout.write('\\r\\x1b[2K');\n this.hasInlineLine = false;\n }\n }\n\n /**\n * 非 verbose 渲染\n *\n * - TTY 终端:覆盖式单行(spinner + 进度条),无换行\n * - 非 TTY(VSCode 终端 / CI):每个步骤完成时打印一行,无 spinner 动画\n */\n private renderCompact(): void {\n if (!shouldLog('info')) return;\n\n if (!isTTY) {\n this.renderNonTty();\n return;\n }\n\n this.renderTty();\n }\n\n private renderNonTty(): void {\n for (let i = 0; i < this.steps.length; i++) {\n if (this.steps[i].status === 'done' && !this.printedStepIds.has(i)) {\n this.printedStepIds.add(i);\n const step = this.steps[i];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.green}${icons.success}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n }\n }\n\n private renderTty(): void {\n const running = this.steps.find((s) => s.status === 'running');\n const doneCount = this.steps.filter((s) => s.status === 'done').length;\n const total = this.steps.length;\n const pct = total > 0 ? Math.round((doneCount / total) * 100) : 0;\n\n const barWidth = 15;\n const filled = Math.round((doneCount / Math.max(total, 1)) * barWidth);\n const bar = `${c.green}${'█'.repeat(filled)}${c.dim}${'░'.repeat(barWidth - filled)}${c.reset}`;\n\n const spinner = running\n ? `${c.cyan}${icons.spinner[this.spinnerFrame]}${c.reset} `\n : `${c.green}${icons.success}${c.reset} `;\n\n const label = running ? running.label : 'done';\n const detail = running?.detail ? ` ${c.dim}${running.detail}${c.reset}` : '';\n\n process.stdout.write(`\\r\\x1b[2K ${spinner}${bar} ${c.dim}${pct}%${c.reset} ${c.bold}${label}${c.reset}${detail}`);\n this.hasInlineLine = true;\n }\n\n /** verbose:逐行打印 */\n private printVerboseStep(idx: number): void {\n if (!shouldLog('info')) return;\n const step = this.steps[idx];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.blue}${icons.info}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n\n private printVerboseStepDone(idx: number): void {\n if (!shouldLog('info')) return;\n const step = this.steps[idx];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.green}${icons.success}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n\n private printVerboseStepError(idx: number): void {\n if (!shouldLog('error')) return;\n const step = this.steps[idx];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.red}${icons.error}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n}\n\n// ============================================================\n// 核心输出\n// ============================================================\n\n/**\n * 服务启动消息(始终展示——这是用户最需要看到的)\n *\n * 输出示例:\n * ✓ Dev Service http://localhost:3001\n * ✓ Client http://localhost:2029\n */\nexport function logServiceReady(name: string, url: string, extra?: string): void {\n if (!shouldLog('info')) return;\n\n const tag = `${c.green}${icons.success}${c.reset}`;\n const svcName = `${c.bold}${padRight(name, 8)}${c.reset}`;\n const link = `${c.cyan}${url}${c.reset}`;\n const suffix = extra ? ` ${c.dim}${extra}${c.reset}` : '';\n\n console.log(` ${tag} ${svcName} ${link}${suffix}`);\n\n logBus.emit({\n level: 'success',\n module: name,\n message: `${url}${extra ? ` ${extra}` : ''}`,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 进度消息(仅 verbose 模式展示)\n */\nexport function logProgress(module: string, action: string, detail?: string): void {\n if (!shouldLog('info')) return;\n\n const tag = `${c.blue}${icons.info}${c.reset}`;\n const mod = `${c.bold}${padRight(module, 10)}${c.reset}`;\n const act = action === 'ready' ? `${c.green}${icons.success} ${action}${c.reset}` : `${c.dim}${action}${c.reset}`;\n const suffix = detail ? ` ${c.dim}${detail}${c.reset}` : '';\n console.log(` ${tag} ${mod} ${act}${suffix}`);\n\n logBus.emit({\n level: 'info',\n module,\n message: action,\n detail,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 耗时操作 loading 转圈(在 TTY 中用 braille 字符 + 已用时间,pipe 中用 dots)\n * @param module 模块名\n * @param action 动作描述\n * @param task 实际执行的任务\n * @example const result = await withLoading('go', 'starting', () => startGoService());\n */\nexport async function withLoading<T>(module: string, action: string, task: () => Promise<T>): Promise<T> {\n const tag = `${c.blue}${icons.info}${c.reset}`;\n const mod = `${c.bold}${padRight(module, 10)}${c.reset}`;\n const act = `${c.dim}${action}${c.reset}`;\n const isTTY = process.stdout.isTTY === true;\n const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n const start = Date.now();\n let i = 0;\n let stopped = false;\n\n const render = (frame: string): void => {\n const elapsed = ((Date.now() - start) / 1000).toFixed(1);\n const line = ` ${tag} ${mod} ${act} ${c.cyan}${frame}${c.reset} ${c.dim}${elapsed}s${c.reset}`;\n if (isTTY) {\n process.stdout.write(`\\r\\x1b[K${line}`);\n } else {\n process.stdout.write(`\\r${line}`);\n }\n };\n\n // 初始帧\n render(isTTY ? FRAMES[0] : '·');\n\n const timer = setInterval(() => {\n if (stopped) return;\n i++;\n render(isTTY ? FRAMES[i % FRAMES.length] : '·');\n }, 100);\n\n try {\n return await task();\n } finally {\n stopped = true;\n clearInterval(timer);\n if (isTTY) process.stdout.write('\\r\\x1b[K');\n }\n}\n\n/**\n * 文件变化通知(始终展示)\n */\nexport function logWatch(file: string, action: string): void {\n if (!shouldLog('info')) return;\n\n const tag = `${c.yellow}${icons.arrow}${c.reset}`;\n const label = `${c.bold}${padRight('watch', 10)}${c.reset}`;\n\n console.log(` ${tag} ${label} ${c.dim}${file} ${action}${c.reset}`);\n\n logBus.emit({\n level: 'info',\n module: 'watch',\n message: `${file} ${action}`,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 警告(始终展示)\n */\nexport function logWarn(msg: string): void {\n if (!shouldLog('warn')) return;\n console.log(` ${c.yellow}${icons.warn}${c.reset} ${c.yellow}${msg}${c.reset}`);\n\n logBus.emit({\n level: 'warn',\n module: 'system',\n message: msg,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 错误(展示详情)\n */\nexport function logError(msg: string, err?: Error | string): void {\n if (!shouldLog('error')) return;\n console.log(` ${c.red}${icons.error} ${msg}${c.reset}`);\n\n let detail: string | undefined;\n if (err) {\n detail = err instanceof Error ? err.message : err;\n // 错误详情缩进展示(不再递归调用 logError)\n for (const line of detail.split('\\n')) {\n console.log(` ${c.dim}${line}${c.reset}`);\n }\n\n // debug 模式下展示堆栈\n if (currentLevel === 'debug' && err instanceof Error && err.stack) {\n const stackLines = err.stack.split('\\n').slice(1);\n for (const line of stackLines) {\n console.log(` ${c.gray}${line.trim()}${c.reset}`);\n }\n }\n }\n\n logBus.emit({\n level: 'error',\n module: 'system',\n message: msg,\n detail,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 致命错误(抛出异常)\n */\nexport function logFatal(msg: string, err?: Error | string): never {\n logError(msg, err);\n throw new FatalError(msg, toErrorCause(err));\n}\n\n/** 致命错误类型 */\nclass FatalError extends Error {\n constructor(\n message: string,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = 'FatalError';\n }\n}\n\n/** 转换错误原因 */\nfunction toErrorCause(err?: Error | string): Error | undefined {\n if (err instanceof Error) return err;\n if (err) return new Error(err);\n return undefined;\n}\n\n// ============================================================\n// 分隔与标题\n// ============================================================\n\n/**\n * 启动 banner(极简版)\n */\nexport function logBanner(command: string): void {\n if (!shouldLog('info')) return;\n console.log();\n console.log(` ${c.bold}specflow ${command}${c.reset}`);\n console.log(` ${c.dim}${'─'.repeat(20)}${c.reset}`);\n}\n\n/**\n * 完成消息\n */\nexport function logReady(startTime: number): void {\n if (!shouldLog('info')) return;\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n console.log();\n console.log(` ${c.green}${icons.success} Ready in ${elapsed}s${c.reset}`);\n console.log();\n}\n\n/**\n * debug 日志(仅 debug 模式)\n */\nexport function logDebug(msg: string): void {\n if (!shouldLog('debug')) return;\n console.log(` ${c.gray}[debug] ${msg}${c.reset}`);\n}\n\n// ============================================================\n// 空行\n// ============================================================\n\nexport function logNewline(): void {\n if (!shouldLog('info')) return;\n console.log();\n}\n\n// ============================================================\n// 工具函数\n// ============================================================\n\nfunction padRight(s: string, len: number): string {\n return s.length >= len ? s : s + ' '.repeat(len - s.length);\n}\n\n// ============================================================\n// LogBus — 全局日志事件总线\n// ============================================================\n\nexport type LogEventLevel = 'info' | 'warn' | 'error' | 'success' | 'debug';\n\nexport interface LogEvent {\n /** 日志级别 */\n level: LogEventLevel;\n /** 模块标签(如 'handler', 'codegen', 'watcher') */\n module: string;\n /** 日志消息 */\n message: string;\n /** 附加详情(如错误堆栈、文件路径) */\n detail?: string;\n /** 时间戳(ms) */\n timestamp: number;\n}\n\ntype LogListener = (event: LogEvent) => void;\n\n/**\n * 全局日志事件总线\n *\n * 所有 logXxx 函数在输出终端的同时,都会向此总线推送事件。\n * AdminServer 订阅此总线,通过 SSE 将日志实时推送给订阅者(前端/模型)。\n */\nexport class LogBus {\n private listeners: Set<LogListener> = new Set();\n /** 最近 N 条日志缓存(用于新连接时回放) */\n private recentLogs: LogEvent[] = [];\n private maxRecent: number;\n\n constructor(maxRecent = 200) {\n this.maxRecent = maxRecent;\n }\n\n /** 订阅日志事件 */\n subscribe(listener: LogListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** 推送日志事件(内部调用) */\n emit(event: LogEvent): void {\n // 缓存最近日志\n this.recentLogs.push(event);\n if (this.recentLogs.length > this.maxRecent) {\n this.recentLogs.shift();\n }\n // 通知所有订阅者\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // 忽略监听器错误\n }\n }\n }\n\n /** 获取最近日志(新连接时回放) */\n getRecent(limit = 50): LogEvent[] {\n return this.recentLogs.slice(-limit);\n }\n\n /** 清空缓存 */\n clear(): void {\n this.recentLogs = [];\n }\n}\n\n/** 全局单例 LogBus */\nexport const logBus = new LogBus();\n"],"names":["isColor","process","stdout","isTTY","c","reset","bold","dim","green","yellow","red","cyan","blue","magenta","gray","icons","success","error","warn","info","arrow","LOG_LEVELS","silent","debug","currentLevel","setLogLevel","level","setVerbose","v","shouldLog","logServiceReady","name","url","extra","tag","svcName","padRight","link","suffix","console","log","logBus","emit","module","message","timestamp","Date","now","logProgress","action","detail","mod","act","withLoading","task","FRAMES","start","i","stopped","render","frame","elapsed","toFixed","line","write","timer","setInterval","length","clearInterval","logWatch","file","label","logWarn","msg","logError","err","Error","split","stack","stackLines","slice","trim","logBanner","command","repeat","logReady","startTime","s","len","LogBus","listeners","Set","recentLogs","constructor","maxRecent","subscribe","listener","add","delete","event","push","shift","getRecent","limit","clear"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"logger.js","sources":["../src/logger.ts"],"sourcesContent":["/**\n * 精简日志系统\n *\n * 设计原则:\n * - 默认模式:只显示进度条 + URL,不刷屏\n * - verbose 模式:展示全部详细信息\n * - 错误始终展示\n */\n\n// ============================================================\n// 颜色工具(轻量,不依赖 chalk)\n// ============================================================\n\nconst isColor = process.stdout.isTTY !== false;\nconst isTTY = process.stdout.isTTY === true;\n\nconst c = {\n reset: isColor ? '\\x1b[0m' : '',\n bold: isColor ? '\\x1b[1m' : '',\n dim: isColor ? '\\x1b[2m' : '',\n green: isColor ? '\\x1b[32m' : '',\n yellow: isColor ? '\\x1b[33m' : '',\n red: isColor ? '\\x1b[31m' : '',\n cyan: isColor ? '\\x1b[36m' : '',\n blue: isColor ? '\\x1b[34m' : '',\n magenta: isColor ? '\\x1b[35m' : '',\n gray: isColor ? '\\x1b[90m' : '',\n clearLine: isTTY ? '\\x1b[2K\\r' : '',\n};\n\n// ============================================================\n// 图标\n// ============================================================\n\nconst icons = {\n success: isColor ? '✓' : '[OK]',\n error: isColor ? '✗' : '[ERR]',\n warn: isColor ? '⚠' : '[WARN]',\n info: isColor ? '●' : '[*]',\n arrow: isColor ? '→' : '->',\n spinner: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\n// ============================================================\n// 模式控制\n// ============================================================\n\nexport type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nlet currentLevel: LogLevel = 'info';\nlet verboseMode = false;\n\n/** 设置日志级别 */\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\n/** 设置 verbose 模式(展示全部详细信息) */\nexport function setVerbose(v: boolean): void {\n verboseMode = v;\n}\n\n/** 是否为 verbose 模式 */\nexport function isVerbose(): boolean {\n return verboseMode;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] <= LOG_LEVELS[currentLevel];\n}\n\n// ============================================================\n// 进度条\n// ============================================================\n\nexport interface ProgressStep {\n label: string;\n status: 'pending' | 'running' | 'done' | 'error';\n detail?: string;\n}\n\n/**\n * 启动进度条\n *\n * 非 verbose 模式下:覆盖式单行显示进度\n * verbose 模式下:每步一行\n */\nexport class ProgressBar {\n private steps: ProgressStep[] = [];\n private spinnerFrame = 0;\n private spinnerTimer: ReturnType<typeof setInterval> | null = null;\n private startTime = Date.now();\n /** 记录哪些步骤已经在非 TTY 模式下打印过 */\n private printedStepIds = new Set<number>();\n /** 当前是否有未换行的进度行占着终端(TTY 覆盖模式) */\n private hasInlineLine = false;\n\n constructor(private title: string) {}\n\n /** 添加步骤 */\n addStep(label: string): number {\n const idx = this.steps.length;\n this.steps.push({ label, status: 'pending' });\n return idx;\n }\n\n /** 开始一个步骤 */\n startStep(idx: number, detail?: string): void {\n this.steps[idx].status = 'running';\n this.steps[idx].detail = detail;\n\n if (verboseMode) {\n this.printVerboseStep(idx);\n } else {\n this.renderCompact();\n }\n }\n\n /** 完成一个步骤 */\n completeStep(idx: number, detail?: string): void {\n this.steps[idx].status = 'done';\n if (detail !== undefined) this.steps[idx].detail = detail;\n\n if (verboseMode) {\n this.printVerboseStepDone(idx);\n } else {\n this.renderCompact();\n }\n }\n\n /** 步骤出错 */\n errorStep(idx: number, detail?: string): void {\n this.steps[idx].status = 'error';\n if (detail !== undefined) this.steps[idx].detail = detail;\n\n if (verboseMode) {\n this.printVerboseStepError(idx);\n } else {\n this.renderCompact();\n }\n }\n\n /** 启动动画(仅 TTY) */\n startSpinner(): void {\n if (!isTTY || verboseMode) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerFrame = (this.spinnerFrame + 1) % icons.spinner.length;\n this.renderCompact();\n }, 80);\n }\n\n /** 停止动画 */\n stopSpinner(): void {\n if (this.spinnerTimer) {\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n }\n\n /** 完成,打印最终摘要 */\n finish(): void {\n this.stopSpinner();\n const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);\n\n // 清掉 TTY 覆盖行(如果有的话)\n this.clearInline();\n\n // 打印完成行\n const doneCount = this.steps.filter((s) => s.status === 'done').length;\n const errCount = this.steps.filter((s) => s.status === 'error').length;\n\n if (errCount > 0) {\n console.log(\n ` ${c.yellow}${icons.warn}${c.reset} ${c.bold}${this.title}${c.reset} ${c.dim}${doneCount}/${this.steps.length} done, ${errCount} errors${c.reset} ${c.dim}${elapsed}s${c.reset}`,\n );\n } else {\n console.log(\n ` ${c.green}${icons.success}${c.reset} ${c.bold}${this.title}${c.reset} ${c.dim}${doneCount}/${this.steps.length} steps${c.reset} ${c.dim}${elapsed}s${c.reset}`,\n );\n }\n }\n\n // ---------- 内部方法 ----------\n\n /** 清除 TTY 模式下的覆盖行 */\n private clearInline(): void {\n if (this.hasInlineLine && isTTY) {\n process.stdout.write('\\r\\x1b[2K');\n this.hasInlineLine = false;\n }\n }\n\n /**\n * 非 verbose 渲染\n *\n * - TTY 终端:覆盖式单行(spinner + 进度条),无换行\n * - 非 TTY(VSCode 终端 / CI):每个步骤完成时打印一行,无 spinner 动画\n */\n private renderCompact(): void {\n if (!shouldLog('info')) return;\n\n if (!isTTY) {\n this.renderNonTty();\n return;\n }\n\n this.renderTty();\n }\n\n private renderNonTty(): void {\n for (let i = 0; i < this.steps.length; i++) {\n if (this.steps[i].status === 'done' && !this.printedStepIds.has(i)) {\n this.printedStepIds.add(i);\n const step = this.steps[i];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.green}${icons.success}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n }\n }\n\n private renderTty(): void {\n const running = this.steps.find((s) => s.status === 'running');\n const doneCount = this.steps.filter((s) => s.status === 'done').length;\n const total = this.steps.length;\n const pct = total > 0 ? Math.round((doneCount / total) * 100) : 0;\n\n const barWidth = 15;\n const filled = Math.round((doneCount / Math.max(total, 1)) * barWidth);\n const bar = `${c.green}${'█'.repeat(filled)}${c.dim}${'░'.repeat(barWidth - filled)}${c.reset}`;\n\n const spinner = running\n ? `${c.cyan}${icons.spinner[this.spinnerFrame]}${c.reset} `\n : `${c.green}${icons.success}${c.reset} `;\n\n const label = running ? running.label : 'done';\n const detail = running?.detail ? ` ${c.dim}${running.detail}${c.reset}` : '';\n\n process.stdout.write(`\\r\\x1b[2K ${spinner}${bar} ${c.dim}${pct}%${c.reset} ${c.bold}${label}${c.reset}${detail}`);\n this.hasInlineLine = true;\n }\n\n /** verbose:逐行打印 */\n private printVerboseStep(idx: number): void {\n if (!shouldLog('info')) return;\n const step = this.steps[idx];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.blue}${icons.info}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n\n private printVerboseStepDone(idx: number): void {\n if (!shouldLog('info')) return;\n const step = this.steps[idx];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.green}${icons.success}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n\n private printVerboseStepError(idx: number): void {\n if (!shouldLog('error')) return;\n const step = this.steps[idx];\n const detail = step.detail ? ` ${c.dim}${step.detail}${c.reset}` : '';\n console.log(` ${c.red}${icons.error}${c.reset} ${c.bold}${padRight(step.label, 10)}${c.reset}${detail}`);\n }\n}\n\n// ============================================================\n// 核心输出\n// ============================================================\n\n/**\n * 服务启动消息(始终展示——这是用户最需要看到的)\n *\n * 输出示例:\n * ✓ Dev Service http://localhost:3001\n * ✓ Client http://localhost:2029\n */\nexport function logServiceReady(name: string, url: string, extra?: string): void {\n if (!shouldLog('info')) return;\n\n const tag = `${c.green}${icons.success}${c.reset}`;\n const svcName = `${c.bold}${padRight(name, 8)}${c.reset}`;\n const link = `${c.cyan}${url}${c.reset}`;\n const suffix = extra ? ` ${c.dim}${extra}${c.reset}` : '';\n\n console.log(` ${tag} ${svcName} ${link}${suffix}`);\n\n logBus.emit({\n level: 'success',\n module: name,\n message: `${url}${extra ? ` ${extra}` : ''}`,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 进度消息(仅 verbose 模式展示)\n */\nexport function logProgress(module: string, action: string, detail?: string): void {\n if (!shouldLog('info')) return;\n\n const tag = `${c.blue}${icons.info}${c.reset}`;\n const mod = `${c.bold}${padRight(module, 10)}${c.reset}`;\n const act = action === 'ready' ? `${c.green}${icons.success} ${action}${c.reset}` : `${c.dim}${action}${c.reset}`;\n const suffix = detail ? ` ${c.dim}${detail}${c.reset}` : '';\n console.log(` ${tag} ${mod} ${act}${suffix}`);\n\n logBus.emit({\n level: 'info',\n module,\n message: action,\n detail,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 耗时操作 loading 转圈(在 TTY 中用 braille 字符 + 已用时间,pipe 中用 dots)\n * @param module 模块名\n * @param action 动作描述\n * @param task 实际执行的任务\n * @example const result = await withLoading('go', 'starting', () => startGoService());\n */\nexport async function withLoading<T>(module: string, action: string, task: () => Promise<T>): Promise<T> {\n const tag = `${c.blue}${icons.info}${c.reset}`;\n const mod = `${c.bold}${padRight(module, 10)}${c.reset}`;\n const act = `${c.dim}${action}${c.reset}`;\n const isTTY = process.stdout.isTTY === true;\n const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n const start = Date.now();\n let i = 0;\n let stopped = false;\n\n const render = (frame: string): void => {\n const elapsed = ((Date.now() - start) / 1000).toFixed(1);\n const line = ` ${tag} ${mod} ${act} ${c.cyan}${frame}${c.reset} ${c.dim}${elapsed}s${c.reset}`;\n if (isTTY) {\n process.stdout.write(`\\r\\x1b[K${line}`);\n } else {\n process.stdout.write(`\\r${line}`);\n }\n };\n\n // 初始帧\n render(isTTY ? FRAMES[0] : '·');\n\n const timer = setInterval(() => {\n if (stopped) return;\n i++;\n render(isTTY ? FRAMES[i % FRAMES.length] : '·');\n }, 100);\n\n try {\n return await task();\n } finally {\n stopped = true;\n clearInterval(timer);\n if (isTTY) process.stdout.write('\\r\\x1b[K');\n }\n}\n\n/**\n * 文件变化通知(始终展示)\n */\nexport function logWatch(file: string, action: string): void {\n if (!shouldLog('info')) return;\n\n const tag = `${c.yellow}${icons.arrow}${c.reset}`;\n const label = `${c.bold}${padRight('watch', 10)}${c.reset}`;\n\n console.log(` ${tag} ${label} ${c.dim}${file} ${action}${c.reset}`);\n\n logBus.emit({\n level: 'info',\n module: 'watch',\n message: `${file} ${action}`,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 警告(始终展示)\n */\nexport function logWarn(msg: string): void {\n if (!shouldLog('warn')) return;\n console.log(` ${c.yellow}${icons.warn}${c.reset} ${c.yellow}${msg}${c.reset}`);\n\n logBus.emit({\n level: 'warn',\n module: 'system',\n message: msg,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 错误(展示详情)\n */\nexport function logError(msg: string, err?: Error | string): void {\n if (!shouldLog('error')) return;\n console.log(` ${c.red}${icons.error} ${msg}${c.reset}`);\n\n let detail: string | undefined;\n if (err) {\n detail = err instanceof Error ? err.message : err;\n // 错误详情缩进展示(不再递归调用 logError)\n for (const line of detail.split('\\n')) {\n console.log(` ${c.dim}${line}${c.reset}`);\n }\n\n // debug 模式下展示堆栈\n if (currentLevel === 'debug' && err instanceof Error && err.stack) {\n const stackLines = err.stack.split('\\n').slice(1);\n for (const line of stackLines) {\n console.log(` ${c.gray}${line.trim()}${c.reset}`);\n }\n }\n }\n\n logBus.emit({\n level: 'error',\n module: 'system',\n message: msg,\n detail,\n timestamp: Date.now(),\n });\n}\n\n/**\n * 致命错误(抛出异常)\n */\nexport function logFatal(msg: string, err?: Error | string): never {\n logError(msg, err);\n throw new FatalError(msg, toErrorCause(err));\n}\n\n/** 致命错误类型 */\nclass FatalError extends Error {\n constructor(\n message: string,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = 'FatalError';\n }\n}\n\n/** 转换错误原因 */\nfunction toErrorCause(err?: Error | string): Error | undefined {\n if (err instanceof Error) return err;\n if (err) return new Error(err);\n return undefined;\n}\n\n// ============================================================\n// 分隔与标题\n// ============================================================\n\n/**\n * 启动 banner(极简版)\n */\nexport function logBanner(command: string): void {\n if (!shouldLog('info')) return;\n console.log();\n console.log(` ${c.bold}specflow ${command}${c.reset}`);\n console.log(` ${c.dim}${'─'.repeat(20)}${c.reset}`);\n}\n\n/**\n * 完成消息\n */\nexport function logReady(startTime: number): void {\n if (!shouldLog('info')) return;\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);\n console.log();\n console.log(` ${c.green}${icons.success} Ready in ${elapsed}s${c.reset}`);\n console.log();\n}\n\n/**\n * debug 日志(仅 debug 模式)\n */\nexport function logDebug(msg: string): void {\n if (!shouldLog('debug')) return;\n console.log(` ${c.gray}[debug] ${msg}${c.reset}`);\n}\n\n// ============================================================\n// 空行\n// ============================================================\n\nexport function logNewline(): void {\n if (!shouldLog('info')) return;\n console.log();\n}\n\n// ============================================================\n// 工具函数\n// ============================================================\n\nfunction padRight(s: string, len: number): string {\n return s.length >= len ? s : s + ' '.repeat(len - s.length);\n}\n\n// ============================================================\n// LogBus — 全局日志事件总线\n// ============================================================\n\nexport type LogEventLevel = 'info' | 'warn' | 'error' | 'success' | 'debug';\n\nexport interface LogEvent {\n /** 日志级别 */\n level: LogEventLevel;\n /** 模块标签(如 'handler', 'codegen', 'watcher') */\n module: string;\n /** 日志消息 */\n message: string;\n /** 附加详情(如错误堆栈、文件路径) */\n detail?: string;\n /** 时间戳(ms) */\n timestamp: number;\n}\n\ntype LogListener = (event: LogEvent) => void;\n\n/**\n * 全局日志事件总线\n *\n * 所有 logXxx 函数在输出终端的同时,都会向此总线推送事件。\n * AdminServer 订阅此总线,通过 SSE 将日志实时推送给订阅者(前端/模型)。\n */\nexport class LogBus {\n private listeners: Set<LogListener> = new Set();\n /** 最近 N 条日志缓存(用于新连接时回放) */\n private recentLogs: LogEvent[] = [];\n private maxRecent: number;\n\n constructor(maxRecent = 200) {\n this.maxRecent = maxRecent;\n }\n\n /** 订阅日志事件 */\n subscribe(listener: LogListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** 推送日志事件(内部调用) */\n emit(event: LogEvent): void {\n // 缓存最近日志\n this.recentLogs.push(event);\n if (this.recentLogs.length > this.maxRecent) {\n this.recentLogs.shift();\n }\n // 通知所有订阅者\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // 忽略监听器错误\n }\n }\n }\n\n /** 获取最近日志(新连接时回放) */\n getRecent(limit = 50): LogEvent[] {\n return this.recentLogs.slice(-limit);\n }\n\n /** 清空缓存 */\n clear(): void {\n this.recentLogs = [];\n }\n}\n\n/** 全局单例 LogBus */\nexport const logBus = new LogBus();\n"],"names":["isColor","process","stdout","isTTY","c","reset","bold","dim","green","yellow","red","cyan","blue","magenta","gray","icons","success","error","warn","info","arrow","LOG_LEVELS","silent","debug","currentLevel","setLogLevel","level","setVerbose","v","shouldLog","logServiceReady","name","url","extra","tag","svcName","padRight","link","suffix","console","log","logBus","emit","module","message","timestamp","Date","now","logProgress","action","detail","mod","act","logWatch","file","label","logWarn","msg","logError","err","Error","line","split","stack","stackLines","slice","trim","logBanner","command","repeat","logReady","startTime","elapsed","toFixed","s","len","length","LogBus","listeners","Set","recentLogs","constructor","maxRecent","subscribe","listener","add","delete","event","push","shift","getRecent","limit","clear"],"mappings":"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"}
|
package/dist/orchestrator.js
CHANGED
|
@@ -36,6 +36,8 @@ class GoProcessManager {
|
|
|
36
36
|
/** 端口真正就绪的 Promise(running 信号后开始等待) */
|
|
37
37
|
portReadyResolver = null;
|
|
38
38
|
portReadyPromise = null;
|
|
39
|
+
/** 端口是否真实就绪(区别于 isRunning — air 进程活着但端口可能还没响应) */
|
|
40
|
+
portReady = false;
|
|
39
41
|
constructor(serviceDir, port, env, options) {
|
|
40
42
|
this.serviceDir = serviceDir;
|
|
41
43
|
this.port = port;
|
|
@@ -262,6 +264,7 @@ class GoProcessManager {
|
|
|
262
264
|
const socket = require('net').createConnection(port, '127.0.0.1');
|
|
263
265
|
socket.on('connect', () => {
|
|
264
266
|
socket.destroy();
|
|
267
|
+
this.portReady = true; // 端口真正就绪
|
|
265
268
|
// 端口就绪 → 输出 + 通知 startWithAir 继续
|
|
266
269
|
if (this.restartCount > 1) {
|
|
267
270
|
console.log(`${prefix} \x1b[32m✓ reloaded\x1b[0m`);
|
|
@@ -275,6 +278,7 @@ class GoProcessManager {
|
|
|
275
278
|
if (Date.now() - start < maxWait) setTimeout(check, 300);else {
|
|
276
279
|
// 超时:仍然放行(可能是服务没监听此端口),打印警告
|
|
277
280
|
console.log(`${prefix} \x1b[33m⚠ port :${port} not responding in ${maxWait / 1000}s, proceeding anyway\x1b[0m`);
|
|
281
|
+
this.portReady = false; // 端口始终未就绪
|
|
278
282
|
this.portReadyResolver?.();
|
|
279
283
|
}
|
|
280
284
|
});
|
|
@@ -287,6 +291,7 @@ class GoProcessManager {
|
|
|
287
291
|
}, 2000);
|
|
288
292
|
} catch {
|
|
289
293
|
// net 模块异常,直接放行
|
|
294
|
+
this.portReady = false;
|
|
290
295
|
this.portReadyResolver?.();
|
|
291
296
|
}
|
|
292
297
|
};
|
|
@@ -511,13 +516,16 @@ class ForgeService {
|
|
|
511
516
|
// === 第零步:按 preset 注入自定义环境变量(前端/Go 进程均可读取) ===
|
|
512
517
|
this.injectEnvPreset();
|
|
513
518
|
|
|
514
|
-
// === 第一步:启动 Go
|
|
519
|
+
// === 第一步:启动 Go 后端 ===
|
|
515
520
|
if (opt.service) {
|
|
516
521
|
const goPort = this.config.service.port;
|
|
517
|
-
|
|
522
|
+
logger.logProgress('go', 'compiling', `http://localhost:${goPort}`);
|
|
523
|
+
await this.startGoService();
|
|
518
524
|
// GoProcessManager.start() 内部已等待端口就绪(最多 15s)
|
|
519
|
-
if (this.goManager?.
|
|
525
|
+
if (this.goManager?.portReady) {
|
|
520
526
|
logger.logProgress('go', 'ready', `http://localhost:${goPort}`);
|
|
527
|
+
} else if (this.goManager?.isRunning) {
|
|
528
|
+
logger.logWarn(`[go] port :${goPort} 未响应,进程仍存活`);
|
|
521
529
|
} else {
|
|
522
530
|
logger.logWarn(`[go] failed — http://localhost:${goPort} 未就绪`);
|
|
523
531
|
}
|
|
@@ -525,17 +533,13 @@ class ForgeService {
|
|
|
525
533
|
|
|
526
534
|
// === 第二步:启动前端 ===
|
|
527
535
|
if (opt.client) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
// 给 spawn 一点时间启动
|
|
536
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
537
|
-
});
|
|
538
|
-
logger.logProgress('client', 'ready', `http://localhost:${clientPort}`);
|
|
536
|
+
logger.logProgress('client', 'starting', `http://localhost:${this.config.client.port}`);
|
|
537
|
+
const clientConfig = {
|
|
538
|
+
port: this.config.client.port,
|
|
539
|
+
dir: node_path.resolve(this.cwd, this.config.client.dir)
|
|
540
|
+
};
|
|
541
|
+
this.clientHandle = client.startClient(clientConfig);
|
|
542
|
+
logger.logProgress('client', 'ready', `http://localhost:${this.config.client.port}`);
|
|
539
543
|
}
|
|
540
544
|
this.running = true;
|
|
541
545
|
}
|
package/dist/orchestrator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sources":["../src/orchestrator.ts"],"sourcesContent":["/**\n * ForgeService — 核心协调器\n *\n * 职责:\n * 1. 解析 specflow.config.ts 配置文件\n * 2. 前端启动逻辑(端口管理 + Vite 进程管理)\n * 3. 后端热更新逻辑(air)\n *\n * 日志原则:只输出关键信息,静默处理正常流程\n */\n\nimport { spawn, type ChildProcess, execSync } from 'node:child_process';\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { defaultConfig } from './config/defaults';\nimport { loadConfig } from './config/loader';\nimport { logProgress, logWarn, logError, withLoading } from './logger';\nimport { startClient, stopClient, type ClientHandle } from './runner';\n\nimport type { ResolvedConfig } from './config/schema';\n\n// ============================================================\n// 类型定义\n// ============================================================\n\nexport interface ForgeServiceStatus {\n running: boolean;\n config: { clientPort: number; servicePort: number };\n client: { port: number; running: boolean; pid?: number };\n service: { port: number; running: boolean; pid?: number };\n}\n\n// ============================================================\n// Go 进程管理器(基于 air,极致精简)\n// ============================================================\n\nclass GoProcessManager {\n private process: ChildProcess | null = null;\n private state: 'idle' | 'starting' | 'running' | 'stopped' = 'idle';\n private restartCount = 0;\n /** 端口真正就绪的 Promise(running 信号后开始等待) */\n private portReadyResolver: (() => void) | null = null;\n private portReadyPromise: Promise<void> | null = null;\n\n constructor(\n private serviceDir: string,\n private port: number,\n private env: Record<string, string | undefined>,\n private options?: { command?: string; args?: string[] },\n ) {}\n\n get isRunning(): boolean {\n return this.state === 'running' && this.process !== null && !this.process.killed;\n }\n\n get pid(): number | undefined {\n return this.process?.pid ?? undefined;\n }\n\n async start(): Promise<void> {\n if (this.state === 'starting' || this.state === 'running') return;\n this.state = 'starting';\n\n // 用户自定义命令优先\n if (this.options?.command) {\n await this.startCustom();\n return;\n }\n\n // 默认使用 air\n await this.startWithAir();\n }\n\n async stop(): Promise<void> {\n if (this.state === 'stopped' || this.state === 'idle') return;\n this.state = 'stopped';\n\n if (this.process) {\n this.killProcess();\n this.process = null;\n }\n\n this.state = 'idle';\n }\n\n /** 使用 air 启动 */\n private async startWithAir(): Promise<void> {\n // 检查并自动安装 air\n if (!(await this.ensureAirInstalled())) return;\n\n // 自动生成 .air.toml(不存在时)\n this.ensureAirToml();\n\n const args = this.buildAirArgs();\n\n this.process = spawn('air', args, {\n cwd: this.serviceDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n env: this.env, // 不额外注入 PORT,由 Go 程序自身配置决定端口\n windowsHide: true,\n shell: process.platform === 'win32',\n });\n\n this.setupAirOutputHandler();\n\n // 创建端口就绪 Promise(output handler 的 running... 信号会触发 resolve)\n this.portReadyPromise = new Promise<void>((resolve) => {\n this.portReadyResolver = resolve;\n });\n\n // 等待 air 启动完成\n await new Promise<void>((res) => setTimeout(res, 1200));\n\n if (this.process && !this.process.killed) {\n this.state = 'running';\n this.restartCount++;\n // 等待端口真正就绪(覆盖 MySQL/Redis/tRPC 初始化窗口)\n await this.portReadyPromise;\n }\n }\n\n /**\n * 自动生成 .air.toml 配置文件(仅当不存在时)\n *\n * 设计原则:\n * - 生成的配置与用户手动编写的完全等价,无任何降级\n * - 平台自适应:Windows 生成 .exe,Linux/Mac 无后缀\n * - 输出同时写 stdout+stderr(forge-service 通过 pipe 捕获)\n */\n private ensureAirToml(): void {\n const tomlPath = resolve(this.serviceDir, '.air.toml');\n const binExt = process.platform === 'win32' ? '.exe' : '';\n const tmpDir = 'tmp';\n const binName = `./tmp/main${binExt}`;\n\n // 检测损坏的旧版本:如果文件存在但包含未替换的模板变量,则重新生成\n if (existsSync(tomlPath)) {\n try {\n const existing = require('node:fs').readFileSync(tomlPath, 'utf-8');\n if (existing.includes('${binExt}') || existing.includes('${tmpDir}')) {\n // 损坏的旧版 → 删除并重新生成\n require('node:fs').unlinkSync(tomlPath);\n } else {\n return; // 已有有效配置,不覆盖\n }\n } catch {\n /* 读取失败,重新生成 */\n }\n }\n\n // 确保 tmp 目录存在\n const tmpFullPath = resolve(this.serviceDir, tmpDir);\n if (!existsSync(tmpFullPath)) {\n mkdirSync(tmpFullPath, { recursive: true });\n }\n\n const tomlContent = [\n '# Air 配置文件 - Go 热更新(自动生成,可手动修改)',\n '',\n 'root = \".\"',\n `tmp_dir = \"${tmpDir}\"`,\n 'working_dir = \".\"',\n '',\n '[build]',\n ` entrypoint = \"${binName}\"`,\n ` cmd = \"go build -o ./tmp/main${binExt} .\"`,\n ' delay = 1000',\n ` exclude_dir = [\"${tmpDir}\", \"vendor\", \"client\", \"bin\", \"log\", \"node_modules\", \".git\"]`,\n ' include_ext = [\"go\", \"yaml\", \"yml\", \"json\", \"toml\", \"mod\", \"sum\"]',\n ' kill_delay = \"3s\"',\n ' build_delay = \"6s\"',\n ' log = \"\"',\n ' send_interrupt = true',\n ' stop_on_error = false',\n '',\n '[log]',\n ' time = false',\n ' outputs = [\"stdout\", \"stderr\"]',\n '',\n '[color]',\n ' main = \"magenta\"',\n ' watcher = \"cyan\"',\n ' build = \"yellow\"',\n ' runner = \"green\"',\n '',\n '[misc]',\n ' clean_on_exit = true',\n ].join('\\n');\n\n writeFileSync(tomlPath, tomlContent, 'utf-8');\n logProgress('go', 'config', `.air.toml 已生成 (${binName})`);\n }\n\n /** 构建 air 参数(始终使用 .air.toml) */\n private buildAirArgs(): string[] {\n // 始终使用项目中的 .air.toml(ensureAirToml 保证其存在)\n const args = ['-c', '.air.toml'];\n if (this.options?.args) args.push(...this.options.args);\n return args;\n }\n\n /** 检查并自动安装 air */\n private async ensureAirInstalled(): Promise<boolean> {\n // 兼容旧版 air(v1.65.x 用 -v,新版用 --version)\n const versionFlags = ['-v', '--version'];\n for (const flag of versionFlags) {\n try {\n execSync(`air ${flag}`, { stdio: 'pipe', timeout: 2000 });\n return true; // 已安装\n } catch {\n // 这个 flag 不支持,试下一个\n }\n }\n\n // 都没成功 → 尝试自动安装\n logProgress('go', 'installing air', 'go install github.com/air-verse/air@latest');\n\n try {\n await new Promise<void>((resolvePromise, reject) => {\n const installProc = spawn('go', ['install', 'github.com/air-verse/air@latest'], {\n stdio: 'pipe',\n env: this.env,\n windowsHide: true,\n });\n installProc.on('close', (code) => {\n if (code === 0) resolvePromise();\n else reject(new Error(`air 安装失败 (exit ${code})`));\n });\n installProc.on('error', reject);\n });\n\n // 验证安装\n for (const flag of versionFlags) {\n try {\n execSync(`air ${flag}`, { stdio: 'pipe', timeout: 2000 });\n return true;\n } catch {\n /* continue */\n }\n }\n throw new Error('安装后验证失败');\n } catch (err) {\n logError(`[go] air 安装失败: ${err instanceof Error ? err.message : String(err)}`);\n logError('[go] 请手动执行: go install github.com/air-verse/air@latest');\n this.state = 'idle';\n return false;\n }\n }\n\n /**\n * 处理 air 输出 — 用户友好模式\n *\n * 正常情况只显示:\n * [go] compiling... (编译进度)\n * [go] ✓ ready http://localhost:11080 (启动成功+端口)\n * [go] ✓ reloaded (热更新成功)\n *\n * 出错才显示:\n * [go] ✗ compile error: (编译失败,显示具体错误)\n * [go] ✗ crashed (运行时崩溃,显示panic)\n */\n private setupAirOutputHandler(): void {\n if (!this.process) return;\n const prefix = '\\x1b[36m[go]\\x1b[0m';\n const isTTY = process.stdout.isTTY === true;\n\n let phase: 'idle' | 'building' | 'running' | 'failed' = 'idle';\n let errorLines: string[] = []; // 编译错误(只在 building 收集)\n let buildDots = 0;\n let buildTimer: ReturnType<typeof setInterval> | null = null;\n\n const clearLine = () => {\n if (isTTY) process.stdout.write('\\r\\x1b[K');\n };\n\n const startBuildProgress = () => {\n if (buildTimer) return;\n buildDots = 0;\n if (isTTY) {\n buildTimer = setInterval(() => {\n buildDots = (buildDots + 1) % 4;\n process.stdout.write(`\\r\\x1b[K${prefix} compiling${'.'.repeat(buildDots)}${' '.repeat(3 - buildDots)}`);\n }, 300);\n } else {\n process.stdout.write(`${prefix} compiling...\\n`);\n }\n };\n\n const stopBuildProgress = () => {\n if (buildTimer) {\n clearInterval(buildTimer);\n buildTimer = null;\n }\n clearLine();\n };\n\n /** 等待端口真正就绪后输出 ready 并 resolve portReadyPromise */\n const waitForPortReady = () => {\n const { port } = this;\n const maxWait = 15000; // 最多等 15s(覆盖慢速 DB/Redis 初始化)\n const start = Date.now();\n const check = (): void => {\n // 先检查进程是否还活着(避免死循环)\n if (!this.process || this.process.killed) return;\n\n try {\n const socket = require('net').createConnection(port, '127.0.0.1');\n socket.on('connect', () => {\n socket.destroy();\n // 端口就绪 → 输出 + 通知 startWithAir 继续\n if ((this as unknown as { restartCount: number }).restartCount > 1) {\n console.log(`${prefix} \\x1b[32m✓ reloaded\\x1b[0m`);\n } else {\n console.log(`${prefix} \\x1b[32m✓ ready\\x1b[0m \\x1b[4mhttp://localhost:${port}\\x1b[0m`);\n }\n this.portReadyResolver?.();\n });\n socket.on('error', () => {\n socket.destroy();\n if (Date.now() - start < maxWait) setTimeout(check, 300);\n else {\n // 超时:仍然放行(可能是服务没监听此端口),打印警告\n console.log(\n `${prefix} \\x1b[33m⚠ port :${port} not responding in ${maxWait / 1000}s, proceeding anyway\\x1b[0m`,\n );\n this.portReadyResolver?.();\n }\n });\n setTimeout(() => {\n try {\n socket.destroy();\n } catch {\n /* noop */\n }\n }, 2000);\n } catch {\n // net 模块异常,直接放行\n this.portReadyResolver?.();\n }\n };\n check();\n };\n\n // ---- 核心行处理 ----\n const stripAnsi = (s: string) => s.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '').replace(/\\r/g, '');\n const stripAirPrefix = (s: string) => s.replace(/^[\\s]*[✗✓]?\\s*\\[air\\]\\s*/i, '');\n\n const isAirNoise = (t: string): boolean =>\n /^[_/\\\\ |]+$/.test(t) ||\n /^(watching|!exclude|cleaning|see you)/i.test(t) ||\n t.startsWith('air') ||\n /^v\\d+\\.\\d+/.test(t) ||\n (/^[|/_\\\\-]+/.test(t) && t.length < 40) ||\n /built with Go/i.test(t);\n\n const handlePhaseSignal = (t: string): boolean => {\n if (/^building\\.\\.\\./.test(t)) {\n phase = 'building';\n errorLines = [];\n startBuildProgress();\n return true;\n }\n if (/^running\\.\\.\\./.test(t)) {\n stopBuildProgress();\n phase = 'running';\n waitForPortReady();\n return true;\n }\n const exitMatch = t.match(/Process Exit with Code:\\s*(\\d+)/);\n if (exitMatch) {\n stopBuildProgress();\n const code = exitMatch[1];\n phase = code === '0' ? 'idle' : 'failed';\n if (code !== '0' && errorLines.length > 0) {\n console.log(`\\n${prefix} \\x1b[31m✗ compile error:\\x1b[0m`);\n for (const line of errorLines.slice(0, 10)) {\n console.log(` \\x1b[31m${line}\\x1b[0m`);\n }\n errorLines = [];\n }\n return true;\n }\n return false;\n };\n\n const handleBuildingPhase = (t: string): void => {\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(t)) return;\n if (/\\.go:\\d+:\\d+:/.test(t) || /^#\\s/.test(t)) {\n errorLines.push(t);\n return;\n }\n if (\n /cannot\\s+(find|import)\\s+package|:\\s*undefined:|declared\\s+and\\s+not\\s+used|imported\\s+and\\s+not\\s+used/.test(\n t,\n )\n ) {\n errorLines.push(t);\n }\n };\n\n const handleLine = (raw: string) => {\n const t = stripAirPrefix(stripAnsi(raw)).trim();\n if (!t || isAirNoise(t)) return;\n if (handlePhaseSignal(t)) return;\n if (phase === 'building') handleBuildingPhase(t);\n };\n\n // ---- 绑定流 ----\n let buf = '';\n const drain = (chunk: Buffer) => {\n buf += chunk.toString();\n const lines = buf.split('\\n');\n buf = lines.pop() || '';\n for (const line of lines) handleLine(line);\n };\n this.process.stdout?.on('data', drain);\n this.process.stderr?.on('data', drain);\n\n this.process.on('error', () => {\n stopBuildProgress();\n this.state = 'idle';\n });\n this.process.on('exit', (_code, signal) => {\n stopBuildProgress();\n if (signal !== 'SIGTERM' && signal !== 'SIGINT') {\n logError(`[go] air stopped`);\n }\n this.process = null;\n this.state = 'idle';\n });\n }\n\n /** 自定义命令启动 */\n private async startCustom(): Promise<void> {\n const cmd = this.options.command;\n const args = this.options.args || [];\n\n this.process = spawn(cmd, args, {\n cwd: this.serviceDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n env: this.env,\n windowsHide: true,\n shell: process.platform === 'win32',\n });\n\n this.setupCustomOutputHandler(cmd);\n\n await new Promise<void>((res) => setTimeout(res, 1500));\n\n if (this.process && !this.process.killed) {\n this.state = 'running';\n this.restartCount++;\n logProgress('go', 'ready', `http://localhost:${this.port} (${cmd})`);\n }\n }\n\n private setupCustomOutputHandler(cmd: string): void {\n if (!this.process) return;\n\n const prefix = `\\x1b[36m[go]\\x1b[0m`;\n\n // ---- 绑定 stdout + stderr,复用 handleLine 过滤 ----\n let buf = '';\n const flush = () => {\n if (!buf.trim()) {\n buf = '';\n return;\n }\n for (const line of buf.split('\\n')) {\n const t = line.trim();\n if (!t) continue;\n // 自定义命令:透传关键信息,过滤噪音\n if (/launch\\s+success|listening|serving|started|ready|error|panic|fatal/i.test(t)) {\n console.log(`${prefix} ${t}`);\n }\n }\n buf = '';\n };\n\n this.process.stdout?.on('data', (d: Buffer) => {\n buf += d.toString();\n const nl = buf.lastIndexOf('\\n');\n if (nl !== -1) {\n const chunk = buf.slice(0, nl);\n buf = buf.slice(nl + 1);\n for (const line of chunk.split('\\n')) {\n const t = line.trim();\n if (!t) continue;\n if (/launch\\s+success|listening|serving|started|ready|error|panic|fatal/i.test(t)) {\n console.log(`${prefix} ${t}`);\n }\n }\n }\n });\n\n this.process.stderr?.on('data', (d: Buffer) => {\n const text = d.toString().trim();\n if (text) logError(`[${cmd}] ${text}`);\n });\n\n this.process.on('error', (err) => {\n logError(`[go] ${err.message}`);\n this.state = 'idle';\n });\n\n this.process.on('exit', () => {\n flush();\n this.process = null;\n this.state = 'idle';\n });\n }\n\n private killProcess(): void {\n if (!this.process || this.process.killed) return;\n\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /PID ${this.process.pid} /T /F`, {\n stdio: 'pipe',\n timeout: 5000,\n windowsHide: true,\n });\n } else {\n this.process.kill('SIGTERM');\n // 给进程 1 秒优雅退出\n setTimeout(() => {\n if (this.process && !this.process.killed) {\n this.process.kill('SIGKILL');\n }\n }, 1000);\n }\n } catch {\n // 进程可能已退出,忽略错误\n }\n }\n}\n\n// ============================================================\n// ForgeService 主类\n// ============================================================\n\nexport class ForgeService {\n public onLog?: (level: 'info' | 'warn' | 'error' | 'success', msg: string) => void;\n\n private config: ResolvedConfig;\n private cwd: string;\n private running = false;\n private mode?: string;\n\n private clientHandle: ClientHandle | null = null;\n private goManager: GoProcessManager | null = null;\n\n constructor(config: ResolvedConfig, cwd: string, mode?: string) {\n this.config = config;\n this.cwd = cwd;\n this.mode = mode;\n }\n\n /** 停止所有服务 */\n async stop(): Promise<void> {\n if (this.goManager) {\n await this.goManager.stop();\n this.goManager = null;\n }\n if (this.clientHandle) {\n stopClient(this.clientHandle);\n this.clientHandle = null;\n }\n this.running = false;\n }\n\n /** 获取状态 */\n getStatus(): ForgeServiceStatus {\n return {\n running: this.running,\n config: { clientPort: this.config.client.port, servicePort: this.config.service.port },\n client: this.getClientStatus(),\n service: this.getServiceStatus(),\n };\n }\n\n /** 启动所有服务(Go 后端先启动,前端后启动) */\n async start(options?: { client?: boolean; service?: boolean }): Promise<void> {\n const opt = { client: true, service: true, ...options };\n\n // === 第零步:按 preset 注入自定义环境变量(前端/Go 进程均可读取) ===\n this.injectEnvPreset();\n\n // === 第一步:启动 Go 后端(带 loading 转圈) ===\n if (opt.service) {\n const goPort = this.config.service.port;\n await withLoading('go', 'starting http://localhost:' + goPort, () => this.startGoService());\n // GoProcessManager.start() 内部已等待端口就绪(最多 15s)\n if (this.goManager?.isRunning) {\n logProgress('go', 'ready', `http://localhost:${goPort}`);\n } else {\n logWarn(`[go] failed — http://localhost:${goPort} 未就绪`);\n }\n }\n\n // === 第二步:启动前端 ===\n if (opt.client) {\n const clientPort = this.config.client.port;\n await withLoading('client', 'starting http://localhost:' + clientPort, async () => {\n const clientConfig = {\n port: clientPort,\n dir: resolve(this.cwd, this.config.client.dir),\n };\n this.clientHandle = startClient(clientConfig);\n // 给 spawn 一点时间启动\n await new Promise((r) => setTimeout(r, 1500));\n });\n logProgress('client', 'ready', `http://localhost:${clientPort}`);\n }\n\n this.running = true;\n }\n\n /** 按 preset 名称注入环境变量到 process.env */\n private injectEnvPreset(): void {\n const presets = this.config.env || {};\n const presetKeys = Object.keys(presets);\n if (presetKeys.length === 0) return;\n\n // 确定使用哪个 preset\n const name = this.mode || presetKeys[0];\n const vars = presets[name];\n if (!vars) {\n logError(`[specflow] 环境变量分组 \"${name}\" 不存在,可选: ${presetKeys.join(', ')}`);\n return;\n }\n\n for (const [key, value] of Object.entries(vars)) {\n process.env[key] = value;\n }\n logProgress('specflow', 'env', `\"${name}\" (${Object.keys(vars).length} 个变量)`);\n }\n\n private getClientStatus(): ForgeServiceStatus['client'] {\n const { port } = this.config.client;\n const pid = this.clientHandle?.child?.pid;\n return { port, running: pid !== null && pid !== undefined, pid };\n }\n\n private getServiceStatus(): ForgeServiceStatus['service'] {\n const { port } = this.config.service;\n const pid = this.goManager?.pid;\n return { port, running: this.goManager?.isRunning ?? false, pid };\n }\n\n private async waitForPort(port: number, timeoutMs: number): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const socket = require('net').createConnection(port, '127.0.0.1');\n await new Promise<void>((resolve, reject) => {\n socket.on('connect', () => {\n socket.destroy();\n resolve();\n });\n socket.on('error', reject);\n setTimeout(() => {\n socket.destroy();\n reject(new Error('timeout'));\n }, 2000);\n });\n return; // 端口可达\n } catch {\n // 端口未就绪,等 500ms 再试\n await new Promise((r) => setTimeout(r, 500));\n }\n }\n }\n\n // ---- Go 服务启动 ----\n\n private async startGoService(): Promise<void> {\n const serviceDir = resolve(this.cwd, this.config.service.dir);\n\n if (!existsSync(serviceDir)) return;\n\n // 检查 main.go 或 go.mod 是否存在\n const hasMainGo = existsSync(resolve(serviceDir, 'main.go'));\n const hasGoMod = existsSync(resolve(serviceDir, 'go.mod'));\n if (!hasMainGo && !hasGoMod) return;\n\n const goEnv = {\n ...process.env,\n // 不注入 PORT 环境变量 — 让 Go 程序使用自身配置文件(yaml/toml)中的端口设置\n // 强制注入 PORT 会导致 tRPC 等框架的行为与直接运行 air 时不一致\n };\n\n // 解析 dev 配置\n const { dev } = this.config.service;\n\n let devOpts: { command?: string; args?: string[] } | undefined;\n\n if (typeof dev === 'string' && dev !== 'air') {\n // \"make dev\" 或 \"./scripts/dev.sh\"(显式自定义命令)\n // 注意:'air' 是保留字,表示使用内置 air 管理(走 startWithAir 路径)\n devOpts = { command: dev };\n } else if (dev && typeof dev === 'object') {\n // { command: 'make', args: ['dev'] }\n devOpts = dev;\n }\n // dev === 'air' 或 dev 未配置 → 默认使用内置 startWithAir()\n\n this.goManager = new GoProcessManager(serviceDir, this.config.service.port, goEnv, devOpts);\n\n try {\n await this.goManager.start();\n } catch (err) {\n logError(`[go] ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n}\n\n// ============================================================\n// 工厂函数\n// ============================================================\n\nexport async function createForgeService(cwd?: string, configPath?: string, mode?: string): Promise<ForgeService> {\n const root = cwd || process.cwd();\n const config = configPath ? await loadConfigFromPath(configPath, root) : await loadConfig(root);\n return new ForgeService(config, root, mode);\n}\n\nasync function loadConfigFromPath(configPath: string, cwd: string): Promise<ResolvedConfig> {\n const { parseConfig } = await import('confmix');\n const { config: userConfig } = await parseConfig<Partial<ResolvedConfig>>(configPath);\n return {\n client: { ...defaultConfig.client, ...(userConfig.client || {}) },\n service: { ...defaultConfig.service, ...(userConfig.service || {}) },\n env: { ...defaultConfig.env, ...(userConfig.env || {}) },\n configDir: cwd,\n };\n}\n"],"names":["GoProcessManager","process","state","restartCount","portReadyResolver","portReadyPromise","constructor","serviceDir","port","env","options","isRunning","killed","pid","undefined","start","command","startCustom","startWithAir","stop","killProcess","ensureAirInstalled","ensureAirToml","args","buildAirArgs","spawn","cwd","stdio","windowsHide","shell","platform","setupAirOutputHandler","Promise","resolve","res","setTimeout","tomlPath","binExt","tmpDir","binName","existsSync","existing","require","readFileSync","includes","unlinkSync","tmpFullPath","mkdirSync","recursive","tomlContent","join","writeFileSync","logProgress","push","versionFlags","flag","execSync","timeout","resolvePromise","reject","installProc","on","code","Error","err","logError","message","String","prefix","isTTY","stdout","phase","errorLines","buildDots","buildTimer","clearLine","write","startBuildProgress","setInterval","repeat","stopBuildProgress","clearInterval","waitForPortReady","maxWait","Date","now","check","socket","createConnection","destroy","console","log","stripAnsi","s","replace","stripAirPrefix","isAirNoise","t","test","startsWith","length","handlePhaseSignal","exitMatch","match","line","slice","handleBuildingPhase","handleLine","raw","trim","buf","drain","chunk","toString","lines","split","pop","stderr","_code","signal","cmd","setupCustomOutputHandler","flush","d","nl","lastIndexOf","text","kill","ForgeService","running","clientHandle","goManager","config","mode","stopClient","getStatus","clientPort","client","servicePort","service","getClientStatus","getServiceStatus","opt","injectEnvPreset","goPort","withLoading","startGoService","logWarn","clientConfig","dir","startClient","r","presets","presetKeys","Object","keys","name","vars","key","value","entries","child","waitForPort","timeoutMs","hasMainGo","hasGoMod","goEnv","dev","devOpts","createForgeService","configPath","root","loadConfigFromPath","loadConfig","parseConfig","userConfig","defaultConfig","configDir"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sources":["../src/orchestrator.ts"],"sourcesContent":["/**\n * ForgeService — 核心协调器\n *\n * 职责:\n * 1. 解析 specflow.config.ts 配置文件\n * 2. 前端启动逻辑(端口管理 + Vite 进程管理)\n * 3. 后端热更新逻辑(air)\n *\n * 日志原则:只输出关键信息,静默处理正常流程\n */\n\nimport { spawn, type ChildProcess, execSync } from 'node:child_process';\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { defaultConfig } from './config/defaults';\nimport { loadConfig } from './config/loader';\nimport { logProgress, logWarn, logError } from './logger';\nimport { startClient, stopClient, type ClientHandle } from './runner';\n\nimport type { ResolvedConfig } from './config/schema';\n\n// ============================================================\n// 类型定义\n// ============================================================\n\nexport interface ForgeServiceStatus {\n running: boolean;\n config: { clientPort: number; servicePort: number };\n client: { port: number; running: boolean; pid?: number };\n service: { port: number; running: boolean; pid?: number };\n}\n\n// ============================================================\n// Go 进程管理器(基于 air,极致精简)\n// ============================================================\n\nclass GoProcessManager {\n private process: ChildProcess | null = null;\n private state: 'idle' | 'starting' | 'running' | 'stopped' = 'idle';\n private restartCount = 0;\n /** 端口真正就绪的 Promise(running 信号后开始等待) */\n private portReadyResolver: (() => void) | null = null;\n private portReadyPromise: Promise<void> | null = null;\n /** 端口是否真实就绪(区别于 isRunning — air 进程活着但端口可能还没响应) */\n public portReady = false;\n\n constructor(\n private serviceDir: string,\n private port: number,\n private env: Record<string, string | undefined>,\n private options?: { command?: string; args?: string[] },\n ) {}\n\n get isRunning(): boolean {\n return this.state === 'running' && this.process !== null && !this.process.killed;\n }\n\n get pid(): number | undefined {\n return this.process?.pid ?? undefined;\n }\n\n async start(): Promise<void> {\n if (this.state === 'starting' || this.state === 'running') return;\n this.state = 'starting';\n\n // 用户自定义命令优先\n if (this.options?.command) {\n await this.startCustom();\n return;\n }\n\n // 默认使用 air\n await this.startWithAir();\n }\n\n async stop(): Promise<void> {\n if (this.state === 'stopped' || this.state === 'idle') return;\n this.state = 'stopped';\n\n if (this.process) {\n this.killProcess();\n this.process = null;\n }\n\n this.state = 'idle';\n }\n\n /** 使用 air 启动 */\n private async startWithAir(): Promise<void> {\n // 检查并自动安装 air\n if (!(await this.ensureAirInstalled())) return;\n\n // 自动生成 .air.toml(不存在时)\n this.ensureAirToml();\n\n const args = this.buildAirArgs();\n\n this.process = spawn('air', args, {\n cwd: this.serviceDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n env: this.env, // 不额外注入 PORT,由 Go 程序自身配置决定端口\n windowsHide: true,\n shell: process.platform === 'win32',\n });\n\n this.setupAirOutputHandler();\n\n // 创建端口就绪 Promise(output handler 的 running... 信号会触发 resolve)\n this.portReadyPromise = new Promise<void>((resolve) => {\n this.portReadyResolver = resolve;\n });\n\n // 等待 air 启动完成\n await new Promise<void>((res) => setTimeout(res, 1200));\n\n if (this.process && !this.process.killed) {\n this.state = 'running';\n this.restartCount++;\n // 等待端口真正就绪(覆盖 MySQL/Redis/tRPC 初始化窗口)\n await this.portReadyPromise;\n }\n }\n\n /**\n * 自动生成 .air.toml 配置文件(仅当不存在时)\n *\n * 设计原则:\n * - 生成的配置与用户手动编写的完全等价,无任何降级\n * - 平台自适应:Windows 生成 .exe,Linux/Mac 无后缀\n * - 输出同时写 stdout+stderr(forge-service 通过 pipe 捕获)\n */\n private ensureAirToml(): void {\n const tomlPath = resolve(this.serviceDir, '.air.toml');\n const binExt = process.platform === 'win32' ? '.exe' : '';\n const tmpDir = 'tmp';\n const binName = `./tmp/main${binExt}`;\n\n // 检测损坏的旧版本:如果文件存在但包含未替换的模板变量,则重新生成\n if (existsSync(tomlPath)) {\n try {\n const existing = require('node:fs').readFileSync(tomlPath, 'utf-8');\n if (existing.includes('${binExt}') || existing.includes('${tmpDir}')) {\n // 损坏的旧版 → 删除并重新生成\n require('node:fs').unlinkSync(tomlPath);\n } else {\n return; // 已有有效配置,不覆盖\n }\n } catch {\n /* 读取失败,重新生成 */\n }\n }\n\n // 确保 tmp 目录存在\n const tmpFullPath = resolve(this.serviceDir, tmpDir);\n if (!existsSync(tmpFullPath)) {\n mkdirSync(tmpFullPath, { recursive: true });\n }\n\n const tomlContent = [\n '# Air 配置文件 - Go 热更新(自动生成,可手动修改)',\n '',\n 'root = \".\"',\n `tmp_dir = \"${tmpDir}\"`,\n 'working_dir = \".\"',\n '',\n '[build]',\n ` entrypoint = \"${binName}\"`,\n ` cmd = \"go build -o ./tmp/main${binExt} .\"`,\n ' delay = 1000',\n ` exclude_dir = [\"${tmpDir}\", \"vendor\", \"client\", \"bin\", \"log\", \"node_modules\", \".git\"]`,\n ' include_ext = [\"go\", \"yaml\", \"yml\", \"json\", \"toml\", \"mod\", \"sum\"]',\n ' kill_delay = \"3s\"',\n ' build_delay = \"6s\"',\n ' log = \"\"',\n ' send_interrupt = true',\n ' stop_on_error = false',\n '',\n '[log]',\n ' time = false',\n ' outputs = [\"stdout\", \"stderr\"]',\n '',\n '[color]',\n ' main = \"magenta\"',\n ' watcher = \"cyan\"',\n ' build = \"yellow\"',\n ' runner = \"green\"',\n '',\n '[misc]',\n ' clean_on_exit = true',\n ].join('\\n');\n\n writeFileSync(tomlPath, tomlContent, 'utf-8');\n logProgress('go', 'config', `.air.toml 已生成 (${binName})`);\n }\n\n /** 构建 air 参数(始终使用 .air.toml) */\n private buildAirArgs(): string[] {\n // 始终使用项目中的 .air.toml(ensureAirToml 保证其存在)\n const args = ['-c', '.air.toml'];\n if (this.options?.args) args.push(...this.options.args);\n return args;\n }\n\n /** 检查并自动安装 air */\n private async ensureAirInstalled(): Promise<boolean> {\n // 兼容旧版 air(v1.65.x 用 -v,新版用 --version)\n const versionFlags = ['-v', '--version'];\n for (const flag of versionFlags) {\n try {\n execSync(`air ${flag}`, { stdio: 'pipe', timeout: 2000 });\n return true; // 已安装\n } catch {\n // 这个 flag 不支持,试下一个\n }\n }\n\n // 都没成功 → 尝试自动安装\n logProgress('go', 'installing air', 'go install github.com/air-verse/air@latest');\n\n try {\n await new Promise<void>((resolvePromise, reject) => {\n const installProc = spawn('go', ['install', 'github.com/air-verse/air@latest'], {\n stdio: 'pipe',\n env: this.env,\n windowsHide: true,\n });\n installProc.on('close', (code) => {\n if (code === 0) resolvePromise();\n else reject(new Error(`air 安装失败 (exit ${code})`));\n });\n installProc.on('error', reject);\n });\n\n // 验证安装\n for (const flag of versionFlags) {\n try {\n execSync(`air ${flag}`, { stdio: 'pipe', timeout: 2000 });\n return true;\n } catch {\n /* continue */\n }\n }\n throw new Error('安装后验证失败');\n } catch (err) {\n logError(`[go] air 安装失败: ${err instanceof Error ? err.message : String(err)}`);\n logError('[go] 请手动执行: go install github.com/air-verse/air@latest');\n this.state = 'idle';\n return false;\n }\n }\n\n /**\n * 处理 air 输出 — 用户友好模式\n *\n * 正常情况只显示:\n * [go] compiling... (编译进度)\n * [go] ✓ ready http://localhost:11080 (启动成功+端口)\n * [go] ✓ reloaded (热更新成功)\n *\n * 出错才显示:\n * [go] ✗ compile error: (编译失败,显示具体错误)\n * [go] ✗ crashed (运行时崩溃,显示panic)\n */\n private setupAirOutputHandler(): void {\n if (!this.process) return;\n const prefix = '\\x1b[36m[go]\\x1b[0m';\n const isTTY = process.stdout.isTTY === true;\n\n let phase: 'idle' | 'building' | 'running' | 'failed' = 'idle';\n let errorLines: string[] = []; // 编译错误(只在 building 收集)\n let buildDots = 0;\n let buildTimer: ReturnType<typeof setInterval> | null = null;\n\n const clearLine = () => {\n if (isTTY) process.stdout.write('\\r\\x1b[K');\n };\n\n const startBuildProgress = () => {\n if (buildTimer) return;\n buildDots = 0;\n if (isTTY) {\n buildTimer = setInterval(() => {\n buildDots = (buildDots + 1) % 4;\n process.stdout.write(`\\r\\x1b[K${prefix} compiling${'.'.repeat(buildDots)}${' '.repeat(3 - buildDots)}`);\n }, 300);\n } else {\n process.stdout.write(`${prefix} compiling...\\n`);\n }\n };\n\n const stopBuildProgress = () => {\n if (buildTimer) {\n clearInterval(buildTimer);\n buildTimer = null;\n }\n clearLine();\n };\n\n /** 等待端口真正就绪后输出 ready 并 resolve portReadyPromise */\n const waitForPortReady = () => {\n const { port } = this;\n const maxWait = 15000; // 最多等 15s(覆盖慢速 DB/Redis 初始化)\n const start = Date.now();\n const check = (): void => {\n // 先检查进程是否还活着(避免死循环)\n if (!this.process || this.process.killed) return;\n\n try {\n const socket = require('net').createConnection(port, '127.0.0.1');\n socket.on('connect', () => {\n socket.destroy();\n this.portReady = true; // 端口真正就绪\n // 端口就绪 → 输出 + 通知 startWithAir 继续\n if ((this as unknown as { restartCount: number }).restartCount > 1) {\n console.log(`${prefix} \\x1b[32m✓ reloaded\\x1b[0m`);\n } else {\n console.log(`${prefix} \\x1b[32m✓ ready\\x1b[0m \\x1b[4mhttp://localhost:${port}\\x1b[0m`);\n }\n this.portReadyResolver?.();\n });\n socket.on('error', () => {\n socket.destroy();\n if (Date.now() - start < maxWait) setTimeout(check, 300);\n else {\n // 超时:仍然放行(可能是服务没监听此端口),打印警告\n console.log(\n `${prefix} \\x1b[33m⚠ port :${port} not responding in ${maxWait / 1000}s, proceeding anyway\\x1b[0m`,\n );\n this.portReady = false; // 端口始终未就绪\n this.portReadyResolver?.();\n }\n });\n setTimeout(() => {\n try {\n socket.destroy();\n } catch {\n /* noop */\n }\n }, 2000);\n } catch {\n // net 模块异常,直接放行\n this.portReady = false;\n this.portReadyResolver?.();\n }\n };\n check();\n };\n\n // ---- 核心行处理 ----\n const stripAnsi = (s: string) => s.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '').replace(/\\r/g, '');\n const stripAirPrefix = (s: string) => s.replace(/^[\\s]*[✗✓]?\\s*\\[air\\]\\s*/i, '');\n\n const isAirNoise = (t: string): boolean =>\n /^[_/\\\\ |]+$/.test(t) ||\n /^(watching|!exclude|cleaning|see you)/i.test(t) ||\n t.startsWith('air') ||\n /^v\\d+\\.\\d+/.test(t) ||\n (/^[|/_\\\\-]+/.test(t) && t.length < 40) ||\n /built with Go/i.test(t);\n\n const handlePhaseSignal = (t: string): boolean => {\n if (/^building\\.\\.\\./.test(t)) {\n phase = 'building';\n errorLines = [];\n startBuildProgress();\n return true;\n }\n if (/^running\\.\\.\\./.test(t)) {\n stopBuildProgress();\n phase = 'running';\n waitForPortReady();\n return true;\n }\n const exitMatch = t.match(/Process Exit with Code:\\s*(\\d+)/);\n if (exitMatch) {\n stopBuildProgress();\n const code = exitMatch[1];\n phase = code === '0' ? 'idle' : 'failed';\n if (code !== '0' && errorLines.length > 0) {\n console.log(`\\n${prefix} \\x1b[31m✗ compile error:\\x1b[0m`);\n for (const line of errorLines.slice(0, 10)) {\n console.log(` \\x1b[31m${line}\\x1b[0m`);\n }\n errorLines = [];\n }\n return true;\n }\n return false;\n };\n\n const handleBuildingPhase = (t: string): void => {\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(t)) return;\n if (/\\.go:\\d+:\\d+:/.test(t) || /^#\\s/.test(t)) {\n errorLines.push(t);\n return;\n }\n if (\n /cannot\\s+(find|import)\\s+package|:\\s*undefined:|declared\\s+and\\s+not\\s+used|imported\\s+and\\s+not\\s+used/.test(\n t,\n )\n ) {\n errorLines.push(t);\n }\n };\n\n const handleLine = (raw: string) => {\n const t = stripAirPrefix(stripAnsi(raw)).trim();\n if (!t || isAirNoise(t)) return;\n if (handlePhaseSignal(t)) return;\n if (phase === 'building') handleBuildingPhase(t);\n };\n\n // ---- 绑定流 ----\n let buf = '';\n const drain = (chunk: Buffer) => {\n buf += chunk.toString();\n const lines = buf.split('\\n');\n buf = lines.pop() || '';\n for (const line of lines) handleLine(line);\n };\n this.process.stdout?.on('data', drain);\n this.process.stderr?.on('data', drain);\n\n this.process.on('error', () => {\n stopBuildProgress();\n this.state = 'idle';\n });\n this.process.on('exit', (_code, signal) => {\n stopBuildProgress();\n if (signal !== 'SIGTERM' && signal !== 'SIGINT') {\n logError(`[go] air stopped`);\n }\n this.process = null;\n this.state = 'idle';\n });\n }\n\n /** 自定义命令启动 */\n private async startCustom(): Promise<void> {\n const cmd = this.options.command;\n const args = this.options.args || [];\n\n this.process = spawn(cmd, args, {\n cwd: this.serviceDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n env: this.env,\n windowsHide: true,\n shell: process.platform === 'win32',\n });\n\n this.setupCustomOutputHandler(cmd);\n\n await new Promise<void>((res) => setTimeout(res, 1500));\n\n if (this.process && !this.process.killed) {\n this.state = 'running';\n this.restartCount++;\n logProgress('go', 'ready', `http://localhost:${this.port} (${cmd})`);\n }\n }\n\n private setupCustomOutputHandler(cmd: string): void {\n if (!this.process) return;\n\n const prefix = `\\x1b[36m[go]\\x1b[0m`;\n\n // ---- 绑定 stdout + stderr,复用 handleLine 过滤 ----\n let buf = '';\n const flush = () => {\n if (!buf.trim()) {\n buf = '';\n return;\n }\n for (const line of buf.split('\\n')) {\n const t = line.trim();\n if (!t) continue;\n // 自定义命令:透传关键信息,过滤噪音\n if (/launch\\s+success|listening|serving|started|ready|error|panic|fatal/i.test(t)) {\n console.log(`${prefix} ${t}`);\n }\n }\n buf = '';\n };\n\n this.process.stdout?.on('data', (d: Buffer) => {\n buf += d.toString();\n const nl = buf.lastIndexOf('\\n');\n if (nl !== -1) {\n const chunk = buf.slice(0, nl);\n buf = buf.slice(nl + 1);\n for (const line of chunk.split('\\n')) {\n const t = line.trim();\n if (!t) continue;\n if (/launch\\s+success|listening|serving|started|ready|error|panic|fatal/i.test(t)) {\n console.log(`${prefix} ${t}`);\n }\n }\n }\n });\n\n this.process.stderr?.on('data', (d: Buffer) => {\n const text = d.toString().trim();\n if (text) logError(`[${cmd}] ${text}`);\n });\n\n this.process.on('error', (err) => {\n logError(`[go] ${err.message}`);\n this.state = 'idle';\n });\n\n this.process.on('exit', () => {\n flush();\n this.process = null;\n this.state = 'idle';\n });\n }\n\n private killProcess(): void {\n if (!this.process || this.process.killed) return;\n\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /PID ${this.process.pid} /T /F`, {\n stdio: 'pipe',\n timeout: 5000,\n windowsHide: true,\n });\n } else {\n this.process.kill('SIGTERM');\n // 给进程 1 秒优雅退出\n setTimeout(() => {\n if (this.process && !this.process.killed) {\n this.process.kill('SIGKILL');\n }\n }, 1000);\n }\n } catch {\n // 进程可能已退出,忽略错误\n }\n }\n}\n\n// ============================================================\n// ForgeService 主类\n// ============================================================\n\nexport class ForgeService {\n public onLog?: (level: 'info' | 'warn' | 'error' | 'success', msg: string) => void;\n\n private config: ResolvedConfig;\n private cwd: string;\n private running = false;\n private mode?: string;\n\n private clientHandle: ClientHandle | null = null;\n private goManager: GoProcessManager | null = null;\n\n constructor(config: ResolvedConfig, cwd: string, mode?: string) {\n this.config = config;\n this.cwd = cwd;\n this.mode = mode;\n }\n\n /** 停止所有服务 */\n async stop(): Promise<void> {\n if (this.goManager) {\n await this.goManager.stop();\n this.goManager = null;\n }\n if (this.clientHandle) {\n stopClient(this.clientHandle);\n this.clientHandle = null;\n }\n this.running = false;\n }\n\n /** 获取状态 */\n getStatus(): ForgeServiceStatus {\n return {\n running: this.running,\n config: { clientPort: this.config.client.port, servicePort: this.config.service.port },\n client: this.getClientStatus(),\n service: this.getServiceStatus(),\n };\n }\n\n /** 启动所有服务(Go 后端先启动,前端后启动) */\n async start(options?: { client?: boolean; service?: boolean }): Promise<void> {\n const opt = { client: true, service: true, ...options };\n\n // === 第零步:按 preset 注入自定义环境变量(前端/Go 进程均可读取) ===\n this.injectEnvPreset();\n\n // === 第一步:启动 Go 后端 ===\n if (opt.service) {\n const goPort = this.config.service.port;\n logProgress('go', 'compiling', `http://localhost:${goPort}`);\n await this.startGoService();\n // GoProcessManager.start() 内部已等待端口就绪(最多 15s)\n if (this.goManager?.portReady) {\n logProgress('go', 'ready', `http://localhost:${goPort}`);\n } else if (this.goManager?.isRunning) {\n logWarn(`[go] port :${goPort} 未响应,进程仍存活`);\n } else {\n logWarn(`[go] failed — http://localhost:${goPort} 未就绪`);\n }\n }\n\n // === 第二步:启动前端 ===\n if (opt.client) {\n logProgress('client', 'starting', `http://localhost:${this.config.client.port}`);\n const clientConfig = {\n port: this.config.client.port,\n dir: resolve(this.cwd, this.config.client.dir),\n };\n this.clientHandle = startClient(clientConfig);\n logProgress('client', 'ready', `http://localhost:${this.config.client.port}`);\n }\n\n this.running = true;\n }\n\n /** 按 preset 名称注入环境变量到 process.env */\n private injectEnvPreset(): void {\n const presets = this.config.env || {};\n const presetKeys = Object.keys(presets);\n if (presetKeys.length === 0) return;\n\n // 确定使用哪个 preset\n const name = this.mode || presetKeys[0];\n const vars = presets[name];\n if (!vars) {\n logError(`[specflow] 环境变量分组 \"${name}\" 不存在,可选: ${presetKeys.join(', ')}`);\n return;\n }\n\n for (const [key, value] of Object.entries(vars)) {\n process.env[key] = value;\n }\n logProgress('specflow', 'env', `\"${name}\" (${Object.keys(vars).length} 个变量)`);\n }\n\n private getClientStatus(): ForgeServiceStatus['client'] {\n const { port } = this.config.client;\n const pid = this.clientHandle?.child?.pid;\n return { port, running: pid !== null && pid !== undefined, pid };\n }\n\n private getServiceStatus(): ForgeServiceStatus['service'] {\n const { port } = this.config.service;\n const pid = this.goManager?.pid;\n return { port, running: this.goManager?.isRunning ?? false, pid };\n }\n\n private async waitForPort(port: number, timeoutMs: number): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const socket = require('net').createConnection(port, '127.0.0.1');\n await new Promise<void>((resolve, reject) => {\n socket.on('connect', () => {\n socket.destroy();\n resolve();\n });\n socket.on('error', reject);\n setTimeout(() => {\n socket.destroy();\n reject(new Error('timeout'));\n }, 2000);\n });\n return; // 端口可达\n } catch {\n // 端口未就绪,等 500ms 再试\n await new Promise((r) => setTimeout(r, 500));\n }\n }\n }\n\n // ---- Go 服务启动 ----\n\n private async startGoService(): Promise<void> {\n const serviceDir = resolve(this.cwd, this.config.service.dir);\n\n if (!existsSync(serviceDir)) return;\n\n // 检查 main.go 或 go.mod 是否存在\n const hasMainGo = existsSync(resolve(serviceDir, 'main.go'));\n const hasGoMod = existsSync(resolve(serviceDir, 'go.mod'));\n if (!hasMainGo && !hasGoMod) return;\n\n const goEnv = {\n ...process.env,\n // 不注入 PORT 环境变量 — 让 Go 程序使用自身配置文件(yaml/toml)中的端口设置\n // 强制注入 PORT 会导致 tRPC 等框架的行为与直接运行 air 时不一致\n };\n\n // 解析 dev 配置\n const { dev } = this.config.service;\n\n let devOpts: { command?: string; args?: string[] } | undefined;\n\n if (typeof dev === 'string' && dev !== 'air') {\n // \"make dev\" 或 \"./scripts/dev.sh\"(显式自定义命令)\n // 注意:'air' 是保留字,表示使用内置 air 管理(走 startWithAir 路径)\n devOpts = { command: dev };\n } else if (dev && typeof dev === 'object') {\n // { command: 'make', args: ['dev'] }\n devOpts = dev;\n }\n // dev === 'air' 或 dev 未配置 → 默认使用内置 startWithAir()\n\n this.goManager = new GoProcessManager(serviceDir, this.config.service.port, goEnv, devOpts);\n\n try {\n await this.goManager.start();\n } catch (err) {\n logError(`[go] ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n}\n\n// ============================================================\n// 工厂函数\n// ============================================================\n\nexport async function createForgeService(cwd?: string, configPath?: string, mode?: string): Promise<ForgeService> {\n const root = cwd || process.cwd();\n const config = configPath ? await loadConfigFromPath(configPath, root) : await loadConfig(root);\n return new ForgeService(config, root, mode);\n}\n\nasync function loadConfigFromPath(configPath: string, cwd: string): Promise<ResolvedConfig> {\n const { parseConfig } = await import('confmix');\n const { config: userConfig } = await parseConfig<Partial<ResolvedConfig>>(configPath);\n return {\n client: { ...defaultConfig.client, ...(userConfig.client || {}) },\n service: { ...defaultConfig.service, ...(userConfig.service || {}) },\n env: { ...defaultConfig.env, ...(userConfig.env || {}) },\n configDir: cwd,\n };\n}\n"],"names":["GoProcessManager","process","state","restartCount","portReadyResolver","portReadyPromise","portReady","constructor","serviceDir","port","env","options","isRunning","killed","pid","undefined","start","command","startCustom","startWithAir","stop","killProcess","ensureAirInstalled","ensureAirToml","args","buildAirArgs","spawn","cwd","stdio","windowsHide","shell","platform","setupAirOutputHandler","Promise","resolve","res","setTimeout","tomlPath","binExt","tmpDir","binName","existsSync","existing","require","readFileSync","includes","unlinkSync","tmpFullPath","mkdirSync","recursive","tomlContent","join","writeFileSync","logProgress","push","versionFlags","flag","execSync","timeout","resolvePromise","reject","installProc","on","code","Error","err","logError","message","String","prefix","isTTY","stdout","phase","errorLines","buildDots","buildTimer","clearLine","write","startBuildProgress","setInterval","repeat","stopBuildProgress","clearInterval","waitForPortReady","maxWait","Date","now","check","socket","createConnection","destroy","console","log","stripAnsi","s","replace","stripAirPrefix","isAirNoise","t","test","startsWith","length","handlePhaseSignal","exitMatch","match","line","slice","handleBuildingPhase","handleLine","raw","trim","buf","drain","chunk","toString","lines","split","pop","stderr","_code","signal","cmd","setupCustomOutputHandler","flush","d","nl","lastIndexOf","text","kill","ForgeService","running","clientHandle","goManager","config","mode","stopClient","getStatus","clientPort","client","servicePort","service","getClientStatus","getServiceStatus","opt","injectEnvPreset","goPort","startGoService","logWarn","clientConfig","dir","startClient","presets","presetKeys","Object","keys","name","vars","key","value","entries","child","waitForPort","timeoutMs","r","hasMainGo","hasGoMod","goEnv","dev","devOpts","createForgeService","configPath","root","loadConfigFromPath","loadConfig","parseConfig","userConfig","defaultConfig","configDir"],"mappings":"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"}
|