helloloop 0.8.6 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +230 -498
- package/hosts/claude/marketplace/plugins/helloloop/.claude-plugin/plugin.json +1 -1
- package/hosts/gemini/extension/gemini-extension.json +1 -1
- package/native/windows-hidden-shell-proxy/HelloLoopHiddenShellProxy.csproj +11 -0
- package/native/windows-hidden-shell-proxy/Program.cs +498 -0
- package/package.json +4 -2
- package/src/activity_projection.mjs +294 -0
- package/src/analyze_confirmation.mjs +3 -1
- package/src/analyzer.mjs +2 -1
- package/src/auto_execution_options.mjs +13 -0
- package/src/background_launch.mjs +73 -0
- package/src/cli.mjs +51 -1
- package/src/cli_analyze_command.mjs +12 -14
- package/src/cli_args.mjs +106 -32
- package/src/cli_command_handlers.mjs +73 -25
- package/src/cli_support.mjs +2 -0
- package/src/common.mjs +11 -0
- package/src/dashboard_command.mjs +371 -0
- package/src/dashboard_tui.mjs +289 -0
- package/src/dashboard_web.mjs +351 -0
- package/src/dashboard_web_client.mjs +167 -0
- package/src/dashboard_web_page.mjs +49 -0
- package/src/engine_event_parser_codex.mjs +167 -0
- package/src/engine_process_support.mjs +7 -2
- package/src/engine_selection.mjs +24 -0
- package/src/engine_selection_probe.mjs +10 -6
- package/src/engine_selection_settings.mjs +53 -44
- package/src/execution_interactivity.mjs +12 -0
- package/src/host_continuation.mjs +305 -0
- package/src/install_codex.mjs +20 -30
- package/src/install_shared.mjs +9 -0
- package/src/node_process_launch.mjs +28 -0
- package/src/process.mjs +2 -0
- package/src/runner_execute_task.mjs +15 -1
- package/src/runner_execution_support.mjs +69 -3
- package/src/runner_once.mjs +5 -0
- package/src/runner_status.mjs +72 -4
- package/src/runtime_engine_support.mjs +52 -5
- package/src/runtime_engine_task.mjs +7 -0
- package/src/runtime_settings.mjs +105 -0
- package/src/runtime_settings_loader.mjs +19 -0
- package/src/shell_invocation.mjs +227 -9
- package/src/supervisor_cli_support.mjs +49 -0
- package/src/supervisor_guardian.mjs +307 -0
- package/src/supervisor_runtime.mjs +142 -83
- package/src/supervisor_state.mjs +64 -0
- package/src/supervisor_watch.mjs +364 -0
- package/src/terminal_session_limits.mjs +1 -21
- package/src/windows_hidden_shell_proxy.mjs +405 -0
- package/src/workspace_registry.mjs +155 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
function normalizeWhitespace(value) {
|
|
2
|
+
return String(value || "").replace(/\s+/gu, " ").trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function stripMarkdown(value) {
|
|
6
|
+
return normalizeWhitespace(String(value || "")
|
|
7
|
+
.replace(/\*\*(.*?)\*\*/gu, "$1")
|
|
8
|
+
.replace(/`([^`]+)`/gu, "$1")
|
|
9
|
+
.replace(/^#+\s*/gmu, ""));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function shorten(value, maxLength = 160) {
|
|
13
|
+
const text = normalizeWhitespace(value);
|
|
14
|
+
if (!text) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
return text.length <= maxLength ? text : `${text.slice(0, Math.max(0, maxLength - 1))}…`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function summarizeText(value, maxLength = 160) {
|
|
21
|
+
const sections = String(value || "")
|
|
22
|
+
.replace(/\r\n/gu, "\n")
|
|
23
|
+
.split(/\n\s*\n/gu)
|
|
24
|
+
.map((item) => stripMarkdown(item))
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
return shorten(sections[0] || stripMarkdown(value), maxLength);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeExitCode(value) {
|
|
30
|
+
const code = Number(value);
|
|
31
|
+
return Number.isFinite(code) ? code : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeTodoItems(items) {
|
|
35
|
+
const normalized = Array.isArray(items)
|
|
36
|
+
? items.map((item) => ({
|
|
37
|
+
text: shorten(item?.text || "", 140),
|
|
38
|
+
completed: item?.completed === true,
|
|
39
|
+
}))
|
|
40
|
+
: [];
|
|
41
|
+
const completed = normalized.filter((item) => item.completed).length;
|
|
42
|
+
return {
|
|
43
|
+
items: normalized,
|
|
44
|
+
total: normalized.length,
|
|
45
|
+
completed,
|
|
46
|
+
pending: Math.max(0, normalized.length - completed),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeChanges(changes) {
|
|
51
|
+
return Array.isArray(changes)
|
|
52
|
+
? changes.map((change) => ({
|
|
53
|
+
path: String(change?.path || "").trim(),
|
|
54
|
+
kind: String(change?.kind || "update").trim() || "update",
|
|
55
|
+
}))
|
|
56
|
+
: [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildFileChangeLabel(changes) {
|
|
60
|
+
if (!changes.length) {
|
|
61
|
+
return "文件变更";
|
|
62
|
+
}
|
|
63
|
+
if (changes.length === 1) {
|
|
64
|
+
const change = changes[0];
|
|
65
|
+
return `${change.kind} ${shorten(change.path, 100)}`;
|
|
66
|
+
}
|
|
67
|
+
return `${changes.length} 个文件变更`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function parseCodexJsonlEventLine(line) {
|
|
71
|
+
const payload = JSON.parse(line);
|
|
72
|
+
const type = String(payload?.type || "").trim();
|
|
73
|
+
|
|
74
|
+
if (type === "thread.started") {
|
|
75
|
+
return {
|
|
76
|
+
kind: "thread",
|
|
77
|
+
status: "started",
|
|
78
|
+
label: "线程已启动",
|
|
79
|
+
threadId: String(payload.thread_id || "").trim(),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (type === "turn.started") {
|
|
84
|
+
return {
|
|
85
|
+
kind: "turn",
|
|
86
|
+
status: "started",
|
|
87
|
+
label: "轮次开始",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (type === "turn.completed") {
|
|
92
|
+
return {
|
|
93
|
+
kind: "turn",
|
|
94
|
+
status: "completed",
|
|
95
|
+
label: "轮次完成",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!type.startsWith("item.")) {
|
|
100
|
+
return {
|
|
101
|
+
kind: "event",
|
|
102
|
+
status: "info",
|
|
103
|
+
label: shorten(type, 120) || "事件更新",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const item = payload.item || {};
|
|
108
|
+
const itemId = String(item.id || "").trim();
|
|
109
|
+
const itemStatus = type === "item.started"
|
|
110
|
+
? "in_progress"
|
|
111
|
+
: (String(item.status || "").trim() || "completed");
|
|
112
|
+
|
|
113
|
+
if (item.type === "reasoning") {
|
|
114
|
+
const summary = summarizeText(item.text || "", 180);
|
|
115
|
+
return {
|
|
116
|
+
kind: "reasoning",
|
|
117
|
+
itemId,
|
|
118
|
+
status: itemStatus,
|
|
119
|
+
label: summary || "推理更新",
|
|
120
|
+
summary,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (item.type === "todo_list") {
|
|
125
|
+
const todo = normalizeTodoItems(item.items);
|
|
126
|
+
return {
|
|
127
|
+
kind: "todo",
|
|
128
|
+
itemId,
|
|
129
|
+
status: itemStatus,
|
|
130
|
+
label: todo.total > 0
|
|
131
|
+
? `待办 ${todo.completed}/${todo.total}`
|
|
132
|
+
: "待办清单更新",
|
|
133
|
+
todo,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (item.type === "command_execution") {
|
|
138
|
+
const command = shorten(item.command || "", 220);
|
|
139
|
+
return {
|
|
140
|
+
kind: "command",
|
|
141
|
+
itemId,
|
|
142
|
+
status: itemStatus,
|
|
143
|
+
label: command || "命令执行",
|
|
144
|
+
command,
|
|
145
|
+
exitCode: normalizeExitCode(item.exit_code),
|
|
146
|
+
outputSummary: summarizeText(item.aggregated_output || "", 160),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (item.type === "file_change") {
|
|
151
|
+
const changes = normalizeChanges(item.changes);
|
|
152
|
+
return {
|
|
153
|
+
kind: "file_change",
|
|
154
|
+
itemId,
|
|
155
|
+
status: itemStatus,
|
|
156
|
+
label: buildFileChangeLabel(changes),
|
|
157
|
+
changes,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
kind: String(item.type || "item").trim() || "item",
|
|
163
|
+
itemId,
|
|
164
|
+
status: itemStatus,
|
|
165
|
+
label: summarizeText(item.text || item.command || item.type || type, 160) || "活动更新",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -26,6 +26,7 @@ export function runChild(command, args, options = {}) {
|
|
|
26
26
|
},
|
|
27
27
|
stdio: ["pipe", "pipe", "pipe"],
|
|
28
28
|
shell: Boolean(options.shell),
|
|
29
|
+
windowsHide: process.platform === "win32",
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
let stdout = "";
|
|
@@ -121,17 +122,21 @@ export function runChild(command, args, options = {}) {
|
|
|
121
122
|
emitHeartbeat("running");
|
|
122
123
|
|
|
123
124
|
child.stdout.on("data", (chunk) => {
|
|
124
|
-
|
|
125
|
+
const text = chunk.toString();
|
|
126
|
+
stdout += text;
|
|
125
127
|
stdoutBytes += chunk.length;
|
|
126
128
|
lastOutputAt = Date.now();
|
|
127
129
|
stallWarned = false;
|
|
130
|
+
options.onStdout?.(text);
|
|
128
131
|
emitHeartbeat("running");
|
|
129
132
|
});
|
|
130
133
|
child.stderr.on("data", (chunk) => {
|
|
131
|
-
|
|
134
|
+
const text = chunk.toString();
|
|
135
|
+
stderr += text;
|
|
132
136
|
stderrBytes += chunk.length;
|
|
133
137
|
lastOutputAt = Date.now();
|
|
134
138
|
stallWarned = false;
|
|
139
|
+
options.onStderr?.(text);
|
|
135
140
|
emitHeartbeat("running");
|
|
136
141
|
});
|
|
137
142
|
|
package/src/engine_selection.mjs
CHANGED
|
@@ -273,6 +273,30 @@ export async function resolveEngineSelection({
|
|
|
273
273
|
};
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
const recommendedEngine = recommendEngine({
|
|
277
|
+
hostContext,
|
|
278
|
+
availableEngines,
|
|
279
|
+
projectConfig,
|
|
280
|
+
userSettings,
|
|
281
|
+
});
|
|
282
|
+
if (!interactive && recommendedEngine) {
|
|
283
|
+
return buildResolution({
|
|
284
|
+
engine: recommendedEngine,
|
|
285
|
+
source: "remembered_default",
|
|
286
|
+
basis: [
|
|
287
|
+
buildRecommendationBasis({
|
|
288
|
+
hostContext,
|
|
289
|
+
projectConfig,
|
|
290
|
+
userSettings,
|
|
291
|
+
recommendedEngine,
|
|
292
|
+
}) || `继续沿用已确认过的默认执行引擎:${getEngineDisplayName(recommendedEngine)}。`,
|
|
293
|
+
"当前为无人值守续跑场景,HelloLoop 直接复用之前确认过的可用引擎。",
|
|
294
|
+
],
|
|
295
|
+
hostContext,
|
|
296
|
+
probes,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
276
300
|
if (!interactive) {
|
|
277
301
|
return {
|
|
278
302
|
ok: false,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
|
|
3
3
|
import { getEngineDisplayName, getEngineMetadata, listKnownEngines, normalizeEngineName } from "./engine_metadata.mjs";
|
|
4
|
-
import { resolveCliInvocation } from "./shell_invocation.mjs";
|
|
4
|
+
import { resolveCliInvocation, resolveCodexInvocation } from "./shell_invocation.mjs";
|
|
5
5
|
|
|
6
6
|
function resolveExecutableOverride(policy = {}, engine) {
|
|
7
7
|
const envExecutable = String(process.env[`HELLOLOOP_${String(engine || "").toUpperCase()}_EXECUTABLE`] || "").trim();
|
|
@@ -13,11 +13,14 @@ function resolveExecutableOverride(policy = {}, engine) {
|
|
|
13
13
|
|
|
14
14
|
function probeEngineAvailability(engine, policy = {}) {
|
|
15
15
|
const meta = getEngineMetadata(engine);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const explicitExecutable = resolveExecutableOverride(policy, engine);
|
|
17
|
+
const invocation = engine === "codex"
|
|
18
|
+
? resolveCodexInvocation({ explicitExecutable })
|
|
19
|
+
: resolveCliInvocation({
|
|
20
|
+
commandName: meta.commandName,
|
|
21
|
+
toolDisplayName: meta.displayName,
|
|
22
|
+
explicitExecutable,
|
|
23
|
+
});
|
|
21
24
|
|
|
22
25
|
if (invocation.error) {
|
|
23
26
|
return {
|
|
@@ -30,6 +33,7 @@ function probeEngineAvailability(engine, policy = {}) {
|
|
|
30
33
|
const result = spawnSync(invocation.command, [...invocation.argsPrefix, "--version"], {
|
|
31
34
|
encoding: "utf8",
|
|
32
35
|
shell: invocation.shell,
|
|
36
|
+
windowsHide: process.platform === "win32",
|
|
33
37
|
});
|
|
34
38
|
const ok = result.status === 0;
|
|
35
39
|
return {
|
|
@@ -3,6 +3,14 @@ import path from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { fileExists, readJson, readText, timestampForFile, writeJson, writeText } from "./common.mjs";
|
|
5
5
|
import { normalizeEngineName } from "./engine_metadata.mjs";
|
|
6
|
+
import {
|
|
7
|
+
defaultObserverRetrySettings,
|
|
8
|
+
defaultSupervisorKeepAliveSettings,
|
|
9
|
+
defaultTerminalConcurrencySettings,
|
|
10
|
+
normalizeObserverRetrySettings,
|
|
11
|
+
normalizeSupervisorKeepAliveSettings,
|
|
12
|
+
normalizeTerminalConcurrencySettings,
|
|
13
|
+
} from "./runtime_settings.mjs";
|
|
6
14
|
|
|
7
15
|
function defaultEmailNotificationSettings() {
|
|
8
16
|
return {
|
|
@@ -24,15 +32,6 @@ function defaultEmailNotificationSettings() {
|
|
|
24
32
|
};
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
function defaultTerminalConcurrencySettings() {
|
|
28
|
-
return {
|
|
29
|
-
enabled: true,
|
|
30
|
-
visibleMax: 8,
|
|
31
|
-
backgroundMax: 8,
|
|
32
|
-
totalMax: 8,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
35
|
function defaultUserSettings() {
|
|
37
36
|
return {
|
|
38
37
|
defaultEngine: "",
|
|
@@ -42,29 +41,30 @@ function defaultUserSettings() {
|
|
|
42
41
|
},
|
|
43
42
|
runtime: {
|
|
44
43
|
terminalConcurrency: defaultTerminalConcurrencySettings(),
|
|
44
|
+
observerRetry: defaultObserverRetrySettings(),
|
|
45
|
+
supervisorKeepAlive: defaultSupervisorKeepAliveSettings(),
|
|
45
46
|
},
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
function cloneJsonValue(value) {
|
|
50
|
-
return JSON.parse(JSON.stringify(value));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
50
|
function isPlainObject(value) {
|
|
54
51
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
54
|
+
function normalizeString(value, fallback = "") {
|
|
55
|
+
return typeof value === "string" ? value.trim() : fallback;
|
|
56
|
+
}
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
function normalizeBoolean(value, fallback = false) {
|
|
59
|
+
return typeof value === "boolean" ? value : fallback;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizePositiveInteger(value, fallback, minimum = 1) {
|
|
63
|
+
const numericValue = Number(value);
|
|
64
|
+
if (!Number.isInteger(numericValue) || numericValue < minimum) {
|
|
65
|
+
return fallback;
|
|
66
66
|
}
|
|
67
|
-
return
|
|
67
|
+
return numericValue;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function mergeValueBySchema(schemaValue, baseValue, patchValue) {
|
|
@@ -87,16 +87,6 @@ function mergeValueBySchema(schemaValue, baseValue, patchValue) {
|
|
|
87
87
|
return next;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
export function syncUserSettingsShape(settings = {}) {
|
|
91
|
-
return syncValueBySchema(defaultUserSettings(), settings);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function readRawUserSettingsDocument(options = {}) {
|
|
95
|
-
const settingsFile = resolveUserSettingsFile(options.userSettingsFile);
|
|
96
|
-
const settings = fileExists(settingsFile) ? readJson(settingsFile) : {};
|
|
97
|
-
return syncUserSettingsShape(settings);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
90
|
export function resolveUserSettingsHome() {
|
|
101
91
|
return String(process.env.HELLOLOOP_HOME || "").trim()
|
|
102
92
|
|| path.join(os.homedir(), ".helloloop");
|
|
@@ -113,32 +103,51 @@ function normalizeEmailNotificationSettings(emailSettings = {}) {
|
|
|
113
103
|
const smtp = emailSettings?.smtp || {};
|
|
114
104
|
|
|
115
105
|
return {
|
|
116
|
-
|
|
117
|
-
...emailSettings,
|
|
106
|
+
enabled: normalizeBoolean(emailSettings?.enabled, defaults.enabled),
|
|
118
107
|
to: Array.isArray(emailSettings?.to)
|
|
119
108
|
? emailSettings.to.map((item) => String(item || "").trim()).filter(Boolean)
|
|
120
|
-
:
|
|
109
|
+
: defaults.to,
|
|
110
|
+
from: normalizeString(emailSettings?.from, defaults.from),
|
|
121
111
|
smtp: {
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
host: normalizeString(smtp?.host, defaults.smtp.host),
|
|
113
|
+
port: normalizePositiveInteger(smtp?.port, defaults.smtp.port),
|
|
114
|
+
secure: normalizeBoolean(smtp?.secure, defaults.smtp.secure),
|
|
115
|
+
starttls: normalizeBoolean(smtp?.starttls, defaults.smtp.starttls),
|
|
116
|
+
username: normalizeString(smtp?.username, defaults.smtp.username),
|
|
117
|
+
usernameEnv: normalizeString(smtp?.usernameEnv, defaults.smtp.usernameEnv),
|
|
118
|
+
password: typeof smtp?.password === "string" ? smtp.password : defaults.smtp.password,
|
|
119
|
+
passwordEnv: normalizeString(smtp?.passwordEnv, defaults.smtp.passwordEnv),
|
|
120
|
+
timeoutSeconds: normalizePositiveInteger(smtp?.timeoutSeconds, defaults.smtp.timeoutSeconds),
|
|
121
|
+
rejectUnauthorized: normalizeBoolean(smtp?.rejectUnauthorized, defaults.smtp.rejectUnauthorized),
|
|
124
122
|
},
|
|
125
123
|
};
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
export function
|
|
129
|
-
const settings = readRawUserSettingsDocument(options);
|
|
130
|
-
|
|
126
|
+
export function syncUserSettingsShape(settings = {}) {
|
|
131
127
|
return {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
lastSelectedEngine: normalizeEngineName(settings?.lastSelectedEngine),
|
|
128
|
+
defaultEngine: normalizeEngineName(settings?.defaultEngine) || "",
|
|
129
|
+
lastSelectedEngine: normalizeEngineName(settings?.lastSelectedEngine) || "",
|
|
135
130
|
notifications: {
|
|
136
|
-
...(settings?.notifications || {}),
|
|
137
131
|
email: normalizeEmailNotificationSettings(settings?.notifications?.email || {}),
|
|
138
132
|
},
|
|
133
|
+
runtime: {
|
|
134
|
+
terminalConcurrency: normalizeTerminalConcurrencySettings(settings?.runtime?.terminalConcurrency || {}),
|
|
135
|
+
observerRetry: normalizeObserverRetrySettings(settings?.runtime?.observerRetry || {}),
|
|
136
|
+
supervisorKeepAlive: normalizeSupervisorKeepAliveSettings(settings?.runtime?.supervisorKeepAlive || {}),
|
|
137
|
+
},
|
|
139
138
|
};
|
|
140
139
|
}
|
|
141
140
|
|
|
141
|
+
function readRawUserSettingsDocument(options = {}) {
|
|
142
|
+
const settingsFile = resolveUserSettingsFile(options.userSettingsFile);
|
|
143
|
+
const settings = fileExists(settingsFile) ? readJson(settingsFile) : {};
|
|
144
|
+
return syncUserSettingsShape(settings);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function loadUserSettingsDocument(options = {}) {
|
|
148
|
+
return readRawUserSettingsDocument(options);
|
|
149
|
+
}
|
|
150
|
+
|
|
142
151
|
export function loadUserSettings(options = {}) {
|
|
143
152
|
const settings = loadUserSettingsDocument(options);
|
|
144
153
|
return {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function shouldPromptForEngineSelection(options = {}) {
|
|
2
|
+
if (options.yes) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (options.detach) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (process.env.HELLOLOOP_SUPERVISOR_ACTIVE === "1") {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return Boolean(process.stdout?.isTTY);
|
|
12
|
+
}
|