@nowcrew/daemon 0.5.26 → 0.5.28
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/package.json +1 -1
- package/dist/attachments.js +0 -196
- package/dist/bound-im-decision.js +0 -22
- package/dist/completion-retransmitter.js +0 -77
- package/dist/computer-cli.js +0 -274
- package/dist/computer-profile-lock.js +0 -395
- package/dist/computer-profile.js +0 -364
- package/dist/computer-service.js +0 -358
- package/dist/config.js +0 -82
- package/dist/console-collapse.js +0 -13
- package/dist/console-formatter.js +0 -77
- package/dist/console-payload.js +0 -73
- package/dist/console.js +0 -329
- package/dist/daemon-startup-error.js +0 -30
- package/dist/execution-backend.js +0 -44
- package/dist/execution-event-limit.js +0 -64
- package/dist/execution-journal-lock.js +0 -421
- package/dist/execution-journal.js +0 -716
- package/dist/execution-protocol.js +0 -342
- package/dist/execution-recovery.js +0 -95
- package/dist/execution-runner.js +0 -659
- package/dist/execution-supervisor-child.js +0 -236
- package/dist/execution-supervisor.js +0 -302
- package/dist/execution-telemetry-journal.js +0 -71
- package/dist/external-output.js +0 -114
- package/dist/i18n.js +0 -64
- package/dist/json-result.js +0 -27
- package/dist/list-models.js +0 -92
- package/dist/local-executor.js +0 -439
- package/dist/log-format.js +0 -10
- package/dist/machine-info.js +0 -124
- package/dist/main.js +0 -118
- package/dist/normalize.js +0 -170
- package/dist/origin-decision.js +0 -44
- package/dist/platform.js +0 -8
- package/dist/prompt.js +0 -307
- package/dist/provider-env.js +0 -90
- package/dist/runner.js +0 -234
- package/dist/runtime-cancellation.js +0 -74
- package/dist/runtime-capabilities.js +0 -43
- package/dist/runtime-path.js +0 -60
- package/dist/runtimes/claude.js +0 -51
- package/dist/runtimes/codex-app-server-runner.js +0 -340
- package/dist/runtimes/codex-deepseek-catalog.js +0 -7
- package/dist/runtimes/codex-deepseek-config.js +0 -50
- package/dist/runtimes/codex.js +0 -53
- package/dist/runtimes/kimi-acp-runner.js +0 -364
- package/dist/runtimes/kimi.js +0 -45
- package/dist/runtimes/progress-watchdog.js +0 -26
- package/dist/scheduled-report.js +0 -51
- package/dist/scheduled-run-report.js +0 -57
- package/dist/serve-lifecycle.js +0 -82
- package/dist/serve.js +0 -868
- package/dist/session.js +0 -82
- package/dist/shared-execution-slots.js +0 -68
- package/dist/shutdown-deadline.js +0 -32
- package/dist/skill-preview.js +0 -21
- package/dist/skills.js +0 -56
- package/dist/slog.js +0 -228
- package/dist/supervised-runtime.js +0 -104
- package/dist/token.js +0 -24
- package/dist/unified-diff.js +0 -84
- package/dist/websocket-shutdown.js +0 -53
- package/dist/win32-job-object.js +0 -193
- package/dist/workspace-fs.js +0 -80
- package/dist/workspace-import.js +0 -127
- package/dist/workspace.js +0 -148
package/dist/external-output.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
export const EXTERNAL_ANSWER_OPEN = "<nowwork_external_answer>";
|
|
2
|
-
export const EXTERNAL_ANSWER_CLOSE = "</nowwork_external_answer>";
|
|
3
|
-
function retainedMarkerPrefix(value, marker) {
|
|
4
|
-
const maximum = Math.min(value.length, marker.length - 1);
|
|
5
|
-
for (let length = maximum; length > 0; length -= 1) {
|
|
6
|
-
if (marker.startsWith(value.slice(-length)))
|
|
7
|
-
return length;
|
|
8
|
-
}
|
|
9
|
-
return 0;
|
|
10
|
-
}
|
|
11
|
-
export class ExternalAnswerDecoder {
|
|
12
|
-
state = "outside";
|
|
13
|
-
pending = "";
|
|
14
|
-
push(text) {
|
|
15
|
-
if (!text)
|
|
16
|
-
return [];
|
|
17
|
-
const output = [];
|
|
18
|
-
this.consume(this.pending + text, output);
|
|
19
|
-
return output;
|
|
20
|
-
}
|
|
21
|
-
finish() {
|
|
22
|
-
this.pending = "";
|
|
23
|
-
this.state = "outside";
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
consume(value, output) {
|
|
27
|
-
this.pending = "";
|
|
28
|
-
const marker = this.state === "outside" ? EXTERNAL_ANSWER_OPEN : EXTERNAL_ANSWER_CLOSE;
|
|
29
|
-
const markerAt = value.indexOf(marker);
|
|
30
|
-
if (markerAt >= 0) {
|
|
31
|
-
if (this.state === "inside" && markerAt > 0)
|
|
32
|
-
output.push(value.slice(0, markerAt));
|
|
33
|
-
this.state = this.state === "outside" ? "inside" : "outside";
|
|
34
|
-
const remaining = value.slice(markerAt + marker.length);
|
|
35
|
-
if (remaining)
|
|
36
|
-
this.consume(remaining, output);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const retained = retainedMarkerPrefix(value, marker);
|
|
40
|
-
const safe = retained > 0 ? value.slice(0, -retained) : value;
|
|
41
|
-
this.pending = retained > 0 ? value.slice(-retained) : "";
|
|
42
|
-
if (this.state === "inside" && safe)
|
|
43
|
-
output.push(safe);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export function decodeExternalOutputEvent(runtime, event, decoder) {
|
|
47
|
-
if (runtime === "claude") {
|
|
48
|
-
const candidate = (event ?? {});
|
|
49
|
-
const delta = candidate.event?.delta;
|
|
50
|
-
if (candidate.type !== "stream_event"
|
|
51
|
-
|| candidate.event?.type !== "content_block_delta"
|
|
52
|
-
|| delta?.type !== "text_delta"
|
|
53
|
-
|| typeof delta.text !== "string")
|
|
54
|
-
return [];
|
|
55
|
-
return decoder.push(delta.text);
|
|
56
|
-
}
|
|
57
|
-
const candidate = (event ?? {});
|
|
58
|
-
if (runtime === "codex") {
|
|
59
|
-
return candidate.type === "item.completed"
|
|
60
|
-
&& candidate.item?.type === "agent_message"
|
|
61
|
-
&& typeof candidate.item.text === "string"
|
|
62
|
-
? decoder.push(candidate.item.text)
|
|
63
|
-
: [];
|
|
64
|
-
}
|
|
65
|
-
return candidate.type === undefined
|
|
66
|
-
&& candidate.role === "assistant"
|
|
67
|
-
&& typeof candidate.content === "string"
|
|
68
|
-
? decoder.push(candidate.content)
|
|
69
|
-
: [];
|
|
70
|
-
}
|
|
71
|
-
export function stripExternalAnswerMarkers(value) {
|
|
72
|
-
const sections = [];
|
|
73
|
-
let cursor = 0;
|
|
74
|
-
while (cursor < value.length) {
|
|
75
|
-
const openAt = value.indexOf(EXTERNAL_ANSWER_OPEN, cursor);
|
|
76
|
-
if (openAt < 0)
|
|
77
|
-
break;
|
|
78
|
-
const contentAt = openAt + EXTERNAL_ANSWER_OPEN.length;
|
|
79
|
-
const closeAt = value.indexOf(EXTERNAL_ANSWER_CLOSE, contentAt);
|
|
80
|
-
if (closeAt < 0)
|
|
81
|
-
return value.slice(contentAt).trim();
|
|
82
|
-
sections.push(value.slice(contentAt, closeAt));
|
|
83
|
-
cursor = closeAt + EXTERNAL_ANSWER_CLOSE.length;
|
|
84
|
-
}
|
|
85
|
-
return (sections.length > 0 ? sections.join("") : value).trim();
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* 提取 marker 内容:有 marker 返回拼接内容(trim,空→null),无 marker 返回 null。
|
|
89
|
-
* 与 stripExternalAnswerMarkers 的区别:strip 在无 marker 时回退整段原文(finalText 展示用),
|
|
90
|
-
* 本函数用于判定"agent 是否给出了频道直接回复"——必须能区分有无 marker。
|
|
91
|
-
*/
|
|
92
|
-
export function extractExternalAnswer(value) {
|
|
93
|
-
const parts = [];
|
|
94
|
-
let found = false;
|
|
95
|
-
let rest = value;
|
|
96
|
-
for (;;) {
|
|
97
|
-
const open = rest.indexOf(EXTERNAL_ANSWER_OPEN);
|
|
98
|
-
if (open < 0)
|
|
99
|
-
break;
|
|
100
|
-
found = true;
|
|
101
|
-
const afterOpen = rest.slice(open + EXTERNAL_ANSWER_OPEN.length);
|
|
102
|
-
const close = afterOpen.indexOf(EXTERNAL_ANSWER_CLOSE);
|
|
103
|
-
if (close < 0) {
|
|
104
|
-
parts.push(afterOpen);
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
parts.push(afterOpen.slice(0, close));
|
|
108
|
-
rest = afterOpen.slice(close + EXTERNAL_ANSWER_CLOSE.length);
|
|
109
|
-
}
|
|
110
|
-
if (!found)
|
|
111
|
-
return null;
|
|
112
|
-
const answer = parts.join("").trim();
|
|
113
|
-
return answer.length > 0 ? answer : null;
|
|
114
|
-
}
|
package/dist/i18n.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
const normalizeLang = (value) => {
|
|
2
|
-
if (!value)
|
|
3
|
-
return null;
|
|
4
|
-
const lowered = value.toLowerCase();
|
|
5
|
-
if (lowered.startsWith("zh"))
|
|
6
|
-
return "zh";
|
|
7
|
-
if (lowered.startsWith("en"))
|
|
8
|
-
return "en";
|
|
9
|
-
return null;
|
|
10
|
-
};
|
|
11
|
-
export function detectDaemonLang(env = process.env) {
|
|
12
|
-
return normalizeLang(env.CREW_LANG)
|
|
13
|
-
?? normalizeLang(env.LC_ALL)
|
|
14
|
-
?? normalizeLang(env.LC_MESSAGES)
|
|
15
|
-
?? normalizeLang(env.LANG)
|
|
16
|
-
?? "en";
|
|
17
|
-
}
|
|
18
|
-
const zh = {
|
|
19
|
-
"Claude session started": "Claude 会话启动",
|
|
20
|
-
"Codex session started": "Codex 会话启动",
|
|
21
|
-
"Files changed": "文件变更",
|
|
22
|
-
"Run failed": "运行出错",
|
|
23
|
-
"Run finished": "本轮结束",
|
|
24
|
-
"Missing CREW_MACHINE_TOKEN (sk_machine_*, printed by seed)": "缺少 CREW_MACHINE_TOKEN(sk_machine_*,由 seed 打印)",
|
|
25
|
-
"Usage:": "用法:",
|
|
26
|
-
"connect and stay resident": "连接并常驻",
|
|
27
|
-
"run once manually": "手动运行一次",
|
|
28
|
-
"resident, connecting to": "常驻,连接到",
|
|
29
|
-
"control plane": "控制面",
|
|
30
|
-
"Waking agent": "唤醒 agent",
|
|
31
|
-
"for channel": "处理频道",
|
|
32
|
-
"agent exited": "agent 退出",
|
|
33
|
-
"activities": "活动数",
|
|
34
|
-
"Computer lifecycle:": "电脑生命周期:",
|
|
35
|
-
"profile save reads the machine token from CREW_MACHINE_TOKEN, or stdin with --token-stdin.": "profile save 从 CREW_MACHINE_TOKEN 或 --token-stdin 的标准输入读取机器令牌。",
|
|
36
|
-
"Missing {{name}}": "缺少 {{name}}",
|
|
37
|
-
"Service '{{id}}' is still installed; uninstall it before removing the profile": "服务 '{{id}}' 仍已安装;请先卸载服务再删除配置",
|
|
38
|
-
"Removed profile '{{name}}'.": "已删除配置 '{{name}}'。",
|
|
39
|
-
"Expected profile save, list, show, or remove": "profile 子命令应为 save、list、show 或 remove",
|
|
40
|
-
"Choose one token source: CREW_MACHINE_TOKEN or --token-stdin": "CREW_MACHINE_TOKEN 与 --token-stdin 只能选择一种令牌来源",
|
|
41
|
-
"Missing machine token; use CREW_MACHINE_TOKEN or --token-stdin": "缺少机器令牌;请使用 CREW_MACHINE_TOKEN 或 --token-stdin",
|
|
42
|
-
"Saved profile '{{name}}' with private credentials.": "已保存配置 '{{name}}',凭证仅私有可读。",
|
|
43
|
-
"Service '{{id}}' is not installed": "服务 '{{id}}' 尚未安装",
|
|
44
|
-
"Upgraded daemon and restart request accepted for '{{name}}'. Verify with status.": "daemon 已升级,并已请求重启 '{{name}}';请用 status 确认。",
|
|
45
|
-
"Upgraded daemon but skipped restart for '{{name}}': {{reason}}": "daemon 已升级,但已跳过 '{{name}}' 的重启:{{reason}}",
|
|
46
|
-
"Upgraded daemon. Installed services were not restarted; pass --profile to restart one.": "daemon 已升级;已安装服务尚未重启,可传入 --profile 重启指定服务。",
|
|
47
|
-
"Service lifecycle requires the built daemon entry (.js), not a TypeScript development entry": "服务生命周期必须使用已构建的 daemon 入口(.js),不能使用 TypeScript 开发入口",
|
|
48
|
-
"Installed '{{id}}'. Use status to confirm runtime state.": "已安装 '{{id}}';请用 status 确认运行状态。",
|
|
49
|
-
"Uninstalled '{{id}}'.": "已卸载 '{{id}}'。",
|
|
50
|
-
"{{action}} request accepted for '{{id}}'. Verify with status.": "已接受对 '{{id}}' 的 {{action}} 请求;请用 status 确认。",
|
|
51
|
-
"--token-stdin requires a token on standard input": "--token-stdin 需要从标准输入读取令牌",
|
|
52
|
-
"Service lifecycle requires a global @nowcrew/daemon install; run npm install --global @nowcrew/daemon@latest": "服务生命周期需要全局安装 @nowcrew/daemon;请运行 npm install --global @nowcrew/daemon@latest",
|
|
53
|
-
"Profile '{{profile}}' conflicts with profile '{{conflict}}': both resolve to agents root '{{agentsRoot}}'. Save it with a unique root, for example: {{command}}": "配置 '{{profile}}' 与配置 '{{conflict}}' 解析到了同一个 agents root '{{agentsRoot}}'。请保存为唯一目录,例如:{{command}}",
|
|
54
|
-
"Daemon is already running (PID={{ownerPid}}) for agents root '{{agentsRoot}}'. Journal: '{{journalPath}}'. Do not delete the active journal lock. Stop the existing daemon before retrying. If this profile is managed as a service, use 'crew-daemon stop --profile {{profileName}}' or 'crew-daemon restart --profile {{profileName}}'.": "daemon 已在运行(PID={{ownerPid}}),agents root 为 '{{agentsRoot}}'。日志目录:'{{journalPath}}'。不要删除活跃进程持有的 journal 锁。请先停止现有 daemon 再重试;如果此 profile 由系统服务管理,请使用 'crew-daemon stop --profile {{profileName}}' 或 'crew-daemon restart --profile {{profileName}}'。",
|
|
55
|
-
"Daemon is already running (PID={{ownerPid}}) for agents root '{{agentsRoot}}'. Journal: '{{journalPath}}'. Do not delete the active journal lock. Stop the existing daemon before retrying. If it is managed as a service, use 'crew-daemon stop --profile <name>' or 'crew-daemon restart --profile <name>'.": "daemon 已在运行(PID={{ownerPid}}),agents root 为 '{{agentsRoot}}'。日志目录:'{{journalPath}}'。不要删除活跃进程持有的 journal 锁。请先停止现有 daemon 再重试;如果它由系统服务管理,请使用 'crew-daemon stop --profile <name>' 或 'crew-daemon restart --profile <name>'。",
|
|
56
|
-
};
|
|
57
|
-
export function translateDaemon(lang, message) {
|
|
58
|
-
if (lang === "zh")
|
|
59
|
-
return zh[message] ?? message;
|
|
60
|
-
return message;
|
|
61
|
-
}
|
|
62
|
-
export function formatDaemonText(lang, message, values = {}) {
|
|
63
|
-
return translateDaemon(lang, message).replace(/\{\{([^{}]+)\}\}/g, (token, key) => Object.hasOwn(values, key) ? String(values[key]) : token);
|
|
64
|
-
}
|
package/dist/json-result.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/** 命令 JSON 输出识别与有界 pretty-print;供 AgentConsole 语法高亮,不引入重量级 highlighter。 */
|
|
2
|
-
const MAX_JSON_CHARS = 12_000;
|
|
3
|
-
const TASK_COMMAND = /\bcrew\s+task\s+(create|update|claim|assign|close)\b/i;
|
|
4
|
-
const MESSAGE_COMMAND = /\bcrew\s+message\s+(send|read)\b/i;
|
|
5
|
-
export function buildJsonResult(command, output) {
|
|
6
|
-
const trimmed = output.trim();
|
|
7
|
-
if (!trimmed || (trimmed[0] !== "{" && trimmed[0] !== "["))
|
|
8
|
-
return null;
|
|
9
|
-
try {
|
|
10
|
-
const value = JSON.parse(trimmed);
|
|
11
|
-
const pretty = JSON.stringify(value, null, 2);
|
|
12
|
-
const commandType = command.match(TASK_COMMAND)?.[1]?.toUpperCase().replace("CLOSE", "UPDATE")
|
|
13
|
-
?? command.match(MESSAGE_COMMAND)?.[1]?.toUpperCase()
|
|
14
|
-
?? "RESULT";
|
|
15
|
-
const entity = TASK_COMMAND.test(command) ? "TASK" : MESSAGE_COMMAND.test(command) ? "MESSAGE" : "JSON";
|
|
16
|
-
const label = `${entity} ${commandType}`;
|
|
17
|
-
return {
|
|
18
|
-
kind: "json_result",
|
|
19
|
-
label,
|
|
20
|
-
json: pretty.length > MAX_JSON_CHARS ? `${pretty.slice(0, MAX_JSON_CHARS)}\n…` : pretty,
|
|
21
|
-
truncated: pretty.length > MAX_JSON_CHARS,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
package/dist/list-models.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
import { isWin } from "./platform.js";
|
|
4
|
-
const execFileRaw = promisify(execFile);
|
|
5
|
-
const MODEL_PROBE_TIMEOUT_MS = 8_000;
|
|
6
|
-
const MODEL_PROBE_MAX_BUFFER_BYTES = 1024 * 1024;
|
|
7
|
-
// win32 上 npm CLI 是 .cmd shim,execFile 需 shell 才能执行;参数全是固定字面量,无注入面。
|
|
8
|
-
const execFileP = (bin, args) => execFileRaw(bin, args, {
|
|
9
|
-
shell: isWin(),
|
|
10
|
-
timeout: MODEL_PROBE_TIMEOUT_MS,
|
|
11
|
-
killSignal: "SIGKILL",
|
|
12
|
-
maxBuffer: MODEL_PROBE_MAX_BUFFER_BYTES,
|
|
13
|
-
});
|
|
14
|
-
export async function listRuntimeModels(runtime) {
|
|
15
|
-
switch (runtime) {
|
|
16
|
-
case "codex":
|
|
17
|
-
return parseCodexModels((await execFileP("codex", ["debug", "models"])).stdout);
|
|
18
|
-
case "cursor":
|
|
19
|
-
return parseCursorModels((await execFileP("cursor-agent", ["--list-models"])).stdout);
|
|
20
|
-
case "opencode":
|
|
21
|
-
return parseOpencodeModels((await execFileP("opencode", ["models"])).stdout);
|
|
22
|
-
case "pi":
|
|
23
|
-
return parsePiModels((await execFileP("pi", ["--list-models"])).stdout);
|
|
24
|
-
default:
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function parseCodexModels(stdout) {
|
|
29
|
-
try {
|
|
30
|
-
const parsed = JSON.parse(stdout);
|
|
31
|
-
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.models)) {
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
34
|
-
return normalizeCodexModels(parsed.models);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function normalizeCodexModels(rows) {
|
|
41
|
-
const normalized = rows.flatMap((value) => {
|
|
42
|
-
if (typeof value !== "object" || value === null)
|
|
43
|
-
return [];
|
|
44
|
-
const row = value;
|
|
45
|
-
if (row.visibility != null && row.visibility !== "list")
|
|
46
|
-
return [];
|
|
47
|
-
const id = nonEmptyString(row.slug) ?? nonEmptyString(row.id);
|
|
48
|
-
if (id === null)
|
|
49
|
-
return [];
|
|
50
|
-
return [{
|
|
51
|
-
id,
|
|
52
|
-
label: nonEmptyString(row.display_name) ?? nonEmptyString(row.name) ?? id,
|
|
53
|
-
explicitDefault: row.default === true,
|
|
54
|
-
}];
|
|
55
|
-
});
|
|
56
|
-
const hasExplicitDefault = normalized.some((model) => model.explicitDefault);
|
|
57
|
-
return normalized.map((model, index) => ({
|
|
58
|
-
id: model.id,
|
|
59
|
-
label: model.label,
|
|
60
|
-
...((model.explicitDefault || (!hasExplicitDefault && index === 0)) ? { default: true } : {}),
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
function nonEmptyString(value) {
|
|
64
|
-
if (typeof value !== "string")
|
|
65
|
-
return null;
|
|
66
|
-
const trimmed = value.trim();
|
|
67
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
68
|
-
}
|
|
69
|
-
function parseCursorModels(stdout) {
|
|
70
|
-
return stdout
|
|
71
|
-
.split(/\r?\n/)
|
|
72
|
-
.map((line) => line.trim())
|
|
73
|
-
.filter((line) => line.includes(" - "))
|
|
74
|
-
.map((line, index) => {
|
|
75
|
-
const [id, label] = line.split(/\s+-\s+/, 2);
|
|
76
|
-
return { id: id.trim(), label: (label || id).trim(), ...(index === 0 ? { default: true } : {}) };
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
function parseOpencodeModels(stdout) {
|
|
80
|
-
return stdout
|
|
81
|
-
.split(/\r?\n/)
|
|
82
|
-
.map((line) => line.trim())
|
|
83
|
-
.filter((line) => line.length > 0 && !line.startsWith("warning:"))
|
|
84
|
-
.map((line, index) => ({ id: line, label: line, ...(index === 0 ? { default: true } : {}) }));
|
|
85
|
-
}
|
|
86
|
-
function parsePiModels(stdout) {
|
|
87
|
-
return stdout
|
|
88
|
-
.split(/\r?\n/)
|
|
89
|
-
.map((line) => line.trim())
|
|
90
|
-
.filter((line) => line.length > 0 && !line.toLowerCase().startsWith("warning"))
|
|
91
|
-
.map((line, index) => ({ id: line, label: line, ...(index === 0 ? { default: true } : {}) }));
|
|
92
|
-
}
|