agentlas 0.9.10 → 1.0.2
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/CHANGELOG.md +106 -0
- package/README.md +319 -341
- package/bin/agentlas.cjs +32 -9
- package/engine/agentlas-banner.cjs +24 -3
- package/engine/agentlas-composer.cjs +239 -31
- package/engine/agentlas-config.cjs +103 -7
- package/engine/agentlas-core-harness.cjs +48 -4
- package/engine/agentlas-evolution.cjs +3 -4
- package/engine/agentlas-i18n.cjs +88 -32
- package/engine/agentlas-input.cjs +234 -18
- package/engine/agentlas-memory-governance.cjs +3 -5
- package/engine/agentlas-memory-import.cjs +3 -3
- package/engine/agentlas-native-host.cjs +200 -9
- package/engine/agentlas-onboard.cjs +112 -20
- package/engine/agentlas-permissions.cjs +14 -7
- package/engine/agentlas-sqlite-policy.cjs +34 -0
- package/engine/agentlas-ui.cjs +93 -42
- package/engine/agentlas-workforce.cjs +472 -58
- package/engine/agentlas-workload-routing.cjs +8 -3
- package/engine/agentlas.cjs +140 -12367
- package/engine/agents/files.cjs +61 -0
- package/engine/agents/import-local.cjs +237 -0
- package/engine/agents/registry.cjs +158 -0
- package/engine/agents/router.cjs +565 -0
- package/engine/agents/routes.cjs +43 -0
- package/engine/architecture.data.json +2 -2
- package/engine/automation/daemon.cjs +335 -0
- package/engine/automation/schedule.cjs +181 -0
- package/engine/automation/store.cjs +209 -0
- package/engine/cloud/auth.cjs +279 -0
- package/engine/cloud/hub-client.cjs +239 -0
- package/engine/cloud-assets/cargo.cjs +49 -0
- package/engine/cloud-assets/cas.cjs +235 -0
- package/engine/cloud-assets/commands.cjs +273 -0
- package/engine/cloud-assets/package.cjs +936 -0
- package/engine/cloud-assets/restore.cjs +172 -0
- package/engine/cloud-assets/state.cjs +268 -0
- package/engine/commands/automation.cjs +195 -0
- package/engine/commands/billing.cjs +96 -0
- package/engine/commands/browser.cjs +20 -0
- package/engine/commands/build.cjs +33 -0
- package/engine/commands/call.cjs +24 -0
- package/engine/commands/career-graph.cjs +51 -0
- package/engine/commands/cd.cjs +22 -0
- package/engine/commands/chat.cjs +12 -0
- package/engine/commands/chats.cjs +28 -0
- package/engine/commands/cloud.cjs +17 -0
- package/engine/commands/connect.cjs +20 -0
- package/engine/commands/context.cjs +66 -0
- package/engine/commands/creds.cjs +203 -0
- package/engine/commands/doctor.cjs +68 -0
- package/engine/commands/env.cjs +33 -0
- package/engine/commands/evolve.cjs +23 -0
- package/engine/commands/experience.cjs +34 -0
- package/engine/commands/film.cjs +8 -0
- package/engine/commands/firm.cjs +115 -0
- package/engine/commands/help.cjs +65 -0
- package/engine/commands/hep.cjs +23 -0
- package/engine/commands/import.cjs +35 -0
- package/engine/commands/index.cjs +136 -0
- package/engine/commands/install.cjs +27 -0
- package/engine/commands/journal.cjs +31 -0
- package/engine/commands/legacy-network.cjs +29 -0
- package/engine/commands/list.cjs +49 -0
- package/engine/commands/login.cjs +68 -0
- package/engine/commands/logout.cjs +28 -0
- package/engine/commands/mcp.cjs +91 -0
- package/engine/commands/memory.cjs +21 -0
- package/engine/commands/multimodal.cjs +91 -0
- package/engine/commands/native.cjs +34 -0
- package/engine/commands/netadmin.cjs +31 -0
- package/engine/commands/oberon.cjs +70 -0
- package/engine/commands/ontology.cjs +24 -0
- package/engine/commands/open.cjs +48 -0
- package/engine/commands/plugin.cjs +101 -0
- package/engine/commands/project.cjs +47 -0
- package/engine/commands/research.cjs +36 -0
- package/engine/commands/route.cjs +37 -0
- package/engine/commands/run.cjs +149 -0
- package/engine/commands/search.cjs +55 -0
- package/engine/commands/setup.cjs +45 -0
- package/engine/commands/storm.cjs +75 -0
- package/engine/commands/swarm.cjs +75 -0
- package/engine/commands/telegram.cjs +32 -0
- package/engine/commands/uninstall.cjs +68 -0
- package/engine/commands/update.cjs +54 -0
- package/engine/commands/upload.cjs +18 -0
- package/engine/commands/usage.cjs +35 -0
- package/engine/commands/variant.cjs +25 -0
- package/engine/commands/version.cjs +9 -0
- package/engine/commands/whoami.cjs +38 -0
- package/engine/commands/workforce.cjs +103 -0
- package/engine/core/db.cjs +160 -0
- package/engine/core/paths.cjs +35 -0
- package/engine/experience/build.cjs +181 -0
- package/engine/experience/intents.cjs +492 -0
- package/engine/experience/runtime.cjs +242 -0
- package/engine/experience/variant.cjs +196 -0
- package/engine/firms/orchestrate.cjs +333 -0
- package/engine/hephaestus/runtime.cjs +697 -0
- package/engine/hub/install.cjs +872 -0
- package/engine/hub/plugins.cjs +213 -0
- package/engine/mcp/consent.cjs +289 -0
- package/engine/mcp/contract.cjs +202 -0
- package/engine/mcp/index.cjs +43 -0
- package/engine/mcp/inventory.cjs +322 -0
- package/engine/mcp/plan.cjs +286 -0
- package/engine/mcp/probe.cjs +151 -0
- package/engine/memory-cli/curate.cjs +163 -0
- package/engine/oberon/common.cjs +69 -0
- package/engine/oberon/manifest.cjs +164 -0
- package/engine/oberon/outputs.cjs +70 -0
- package/engine/oberon/render.cjs +164 -0
- package/engine/project/career-graph.cjs +249 -0
- package/engine/project/credentials.cjs +262 -0
- package/engine/project/env-file.cjs +46 -0
- package/engine/project/index.cjs +27 -0
- package/engine/project/memory-context.cjs +453 -0
- package/engine/project/ontology.cjs +467 -0
- package/engine/project/paths.cjs +39 -0
- package/engine/project/seed.cjs +200 -0
- package/engine/project/state.cjs +403 -0
- package/engine/project/super-ontology-seed.json +3288 -0
- package/engine/runtimes/detect.cjs +54 -0
- package/engine/runtimes/overrides.cjs +139 -0
- package/engine/runtimes/resolve.cjs +64 -0
- package/engine/sessions/apply-fences.cjs +188 -0
- package/engine/sessions/fences.cjs +362 -0
- package/engine/sessions/orchestrator.cjs +170 -0
- package/engine/sessions/prompt.cjs +212 -0
- package/engine/sessions/session.cjs +245 -0
- package/engine/sessions/sink.cjs +54 -0
- package/engine/sessions/store.cjs +79 -0
- package/engine/storm/deps.cjs +88 -0
- package/engine/storm/storm.cjs +218 -0
- package/engine/storm/swarm.cjs +422 -0
- package/engine/ui/palette.cjs +105 -0
- package/engine/ui/renderer.cjs +85 -0
- package/engine/ui/repl.cjs +444 -0
- package/engine/workforce/capture.cjs +701 -0
- package/engine/workforce/deps.cjs +472 -0
- package/package.json +2 -6
- package/engine/agentlas-experience-mcp.cjs +0 -1709
- package/engine/agentlas-parity.cjs +0 -1499
- package/engine/agentlas-repl.cjs +0 -1780
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* 스키마는 실측으로 확인됨 (cli/agentlas.cjs 상단 주석 참고).
|
|
14
14
|
*/
|
|
15
|
-
const { spawn } = require("node:child_process");
|
|
15
|
+
const { execFileSync, spawn } = require("node:child_process");
|
|
16
16
|
const crypto = require("node:crypto");
|
|
17
17
|
const fs = require("node:fs");
|
|
18
18
|
const os = require("node:os");
|
|
@@ -821,6 +821,7 @@ function runNativeTurn(req) {
|
|
|
821
821
|
let child;
|
|
822
822
|
try {
|
|
823
823
|
const spawnImpl = req.spawn || spawn;
|
|
824
|
+
const groupedChild = process.platform !== "win32" && spawnImpl === spawn;
|
|
824
825
|
const childEnv = launchReq.prepareRuntimeEnv === false
|
|
825
826
|
? (launchReq.env || process.env)
|
|
826
827
|
: runtimeEnvForKind(kind, launchReq.env || process.env, launchReq);
|
|
@@ -828,7 +829,16 @@ function runNativeTurn(req) {
|
|
|
828
829
|
cwd,
|
|
829
830
|
stdio: ["ignore", "pipe", "pipe"],
|
|
830
831
|
env: childEnv,
|
|
832
|
+
detached: groupedChild,
|
|
833
|
+
windowsHide: true,
|
|
831
834
|
});
|
|
835
|
+
child.__agentlasNativeChild = spawnImpl === spawn;
|
|
836
|
+
child.__agentlasGroupedChild = groupedChild;
|
|
837
|
+
// 세션 계층(오르카)이 실행 중 중단(kill)을 하려면 child 핸들이 필요하다.
|
|
838
|
+
// 커스텀 spawn 주입 방식은 위 트리킬 플래그를 깨므로 훅으로 노출한다.
|
|
839
|
+
if (typeof req.onSpawn === "function") {
|
|
840
|
+
try { req.onSpawn(child); } catch { /* observer 실패가 턴을 죽이면 안 됨 */ }
|
|
841
|
+
}
|
|
832
842
|
} catch (e) {
|
|
833
843
|
ui.error(uiText(ui, "runtime.failed", kind, e.message));
|
|
834
844
|
return resolve({ text: "", session: st.session, error: e.message });
|
|
@@ -840,6 +850,7 @@ function runNativeTurn(req) {
|
|
|
840
850
|
let totalTimer = null;
|
|
841
851
|
let killTimer = null;
|
|
842
852
|
let forceTimer = null;
|
|
853
|
+
let verifyTimer = null;
|
|
843
854
|
let removeLineReader = () => {};
|
|
844
855
|
let stderrBuf = "";
|
|
845
856
|
const clearWatchdogs = () => {
|
|
@@ -847,7 +858,8 @@ function runNativeTurn(req) {
|
|
|
847
858
|
if (totalTimer) clearTimeout(totalTimer);
|
|
848
859
|
if (killTimer) clearTimeout(killTimer);
|
|
849
860
|
if (forceTimer) clearTimeout(forceTimer);
|
|
850
|
-
|
|
861
|
+
if (verifyTimer) clearTimeout(verifyTimer);
|
|
862
|
+
idleTimer = totalTimer = killTimer = forceTimer = verifyTimer = null;
|
|
851
863
|
};
|
|
852
864
|
const cleanup = () => {
|
|
853
865
|
clearWatchdogs();
|
|
@@ -866,7 +878,29 @@ function runNativeTurn(req) {
|
|
|
866
878
|
ui.stopSpinner();
|
|
867
879
|
const text = (st.finalText || st.text || "").trim();
|
|
868
880
|
if (st.usage) ui.cost(st.usage);
|
|
869
|
-
finish({
|
|
881
|
+
finish({
|
|
882
|
+
text,
|
|
883
|
+
session: st.session,
|
|
884
|
+
usage: st.usage,
|
|
885
|
+
error: termination ? termination.message : "native runtime stopped",
|
|
886
|
+
terminationVerified: Boolean(termination?.verified),
|
|
887
|
+
});
|
|
888
|
+
};
|
|
889
|
+
const verifyTermination = () => {
|
|
890
|
+
if (settled || !termination) return;
|
|
891
|
+
if (verifyTimer) return;
|
|
892
|
+
if (child.__agentlasNativeChild === true && !(termination.pids || []).length) return;
|
|
893
|
+
const alive = activeNativeProcessIds(termination.pids || []);
|
|
894
|
+
if (Array.isArray(alive) && !alive.length) {
|
|
895
|
+
termination.verified = true;
|
|
896
|
+
terminationResult();
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
for (const pid of alive || termination.pids || []) signalNativePid(pid, "SIGKILL");
|
|
900
|
+
verifyTimer = setTimeout(() => {
|
|
901
|
+
verifyTimer = null;
|
|
902
|
+
verifyTermination();
|
|
903
|
+
}, 25);
|
|
870
904
|
};
|
|
871
905
|
const requestStop = (reason) => {
|
|
872
906
|
if (termination || settled) return;
|
|
@@ -878,11 +912,17 @@ function runNativeTurn(req) {
|
|
|
878
912
|
if (idleTimer) clearTimeout(idleTimer);
|
|
879
913
|
if (totalTimer) clearTimeout(totalTimer);
|
|
880
914
|
idleTimer = totalTimer = null;
|
|
881
|
-
|
|
915
|
+
const forcedTree = forceStopNativeProcessTree(child);
|
|
916
|
+
if (forcedTree.length) {
|
|
917
|
+
termination.pids = forcedTree;
|
|
918
|
+
verifyTermination();
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
terminateNativeChild(child, "SIGTERM");
|
|
882
922
|
if (settled) return;
|
|
883
923
|
killTimer = setTimeout(() => {
|
|
884
924
|
if (settled) return;
|
|
885
|
-
|
|
925
|
+
terminateNativeChild(child, "SIGKILL");
|
|
886
926
|
if (settled) return;
|
|
887
927
|
forceTimer = setTimeout(terminationResult, Math.max(250, Math.min(1_000, timeout.killGraceMs)));
|
|
888
928
|
}, timeout.killGraceMs);
|
|
@@ -920,7 +960,7 @@ function runNativeTurn(req) {
|
|
|
920
960
|
child.on("error", (err) => {
|
|
921
961
|
if (settled) return;
|
|
922
962
|
if (termination) {
|
|
923
|
-
|
|
963
|
+
verifyTermination();
|
|
924
964
|
return;
|
|
925
965
|
}
|
|
926
966
|
ui.stopSpinner();
|
|
@@ -930,7 +970,7 @@ function runNativeTurn(req) {
|
|
|
930
970
|
child.on("close", (code) => {
|
|
931
971
|
if (settled) return;
|
|
932
972
|
if (termination) {
|
|
933
|
-
|
|
973
|
+
verifyTermination();
|
|
934
974
|
return;
|
|
935
975
|
}
|
|
936
976
|
ui.streamEnd();
|
|
@@ -940,7 +980,7 @@ function runNativeTurn(req) {
|
|
|
940
980
|
if (st.error && !st.errorShown) {
|
|
941
981
|
// claude `result` is_error 등 — 이전에 표시되지 않은 에러를 노출
|
|
942
982
|
ui.error(String(st.error));
|
|
943
|
-
} else if (code !== 0
|
|
983
|
+
} else if (code !== 0) {
|
|
944
984
|
// Runtime Doctor — 아는 시스템 원인(미인증 OAuth MCP 플러그인 등)이면 즉시 수리하고
|
|
945
985
|
// 1회 자동 재시도한다(2026-07-08 notion@openai-curated가 codex 전멸시킨 사고).
|
|
946
986
|
if (!req._doctorRetried) {
|
|
@@ -966,7 +1006,10 @@ function runNativeTurn(req) {
|
|
|
966
1006
|
ui.warn(uiText(ui, "runtime.noOutput", kind) + (errTail ? ` (${errTail.slice(-200)})` : ""));
|
|
967
1007
|
}
|
|
968
1008
|
if (st.usage) ui.cost(st.usage);
|
|
969
|
-
|
|
1009
|
+
const exitError = code === 0
|
|
1010
|
+
? st.error
|
|
1011
|
+
: st.error || `${kind} exited with code ${code == null ? "unknown" : code}`;
|
|
1012
|
+
finish({ text, session: st.session, usage: st.usage, error: exitError });
|
|
970
1013
|
});
|
|
971
1014
|
|
|
972
1015
|
armIdle();
|
|
@@ -978,6 +1021,150 @@ function runNativeTurn(req) {
|
|
|
978
1021
|
});
|
|
979
1022
|
}
|
|
980
1023
|
|
|
1024
|
+
function nativeProcessRows() {
|
|
1025
|
+
if (process.platform === "win32") return [];
|
|
1026
|
+
try {
|
|
1027
|
+
const output = execFileSync("/bin/ps", ["-axo", "pid=,ppid=,stat="], {
|
|
1028
|
+
encoding: "utf8",
|
|
1029
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
1030
|
+
timeout: 1_000,
|
|
1031
|
+
});
|
|
1032
|
+
return String(output || "").split(/\r?\n/).map((line) => {
|
|
1033
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(\S+)/);
|
|
1034
|
+
return match
|
|
1035
|
+
? { pid: Number(match[1]), ppid: Number(match[2]), stat: match[3] }
|
|
1036
|
+
: null;
|
|
1037
|
+
}).filter(Boolean);
|
|
1038
|
+
} catch {
|
|
1039
|
+
return null;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function nativeProcessTreeIds(rootPid, rows = nativeProcessRows()) {
|
|
1044
|
+
if (!Number.isInteger(rootPid) || rootPid <= 1 || !Array.isArray(rows)) return [];
|
|
1045
|
+
const children = new Map();
|
|
1046
|
+
for (const row of rows) {
|
|
1047
|
+
if (!children.has(row.ppid)) children.set(row.ppid, []);
|
|
1048
|
+
children.get(row.ppid).push(row.pid);
|
|
1049
|
+
}
|
|
1050
|
+
const ordered = [];
|
|
1051
|
+
const seen = new Set();
|
|
1052
|
+
const visit = (pid) => {
|
|
1053
|
+
if (seen.has(pid)) return;
|
|
1054
|
+
seen.add(pid);
|
|
1055
|
+
for (const childPid of children.get(pid) || []) visit(childPid);
|
|
1056
|
+
ordered.push(pid);
|
|
1057
|
+
};
|
|
1058
|
+
visit(rootPid);
|
|
1059
|
+
return ordered;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function activeNativeProcessIds(pids, rows = nativeProcessRows()) {
|
|
1063
|
+
if (!Array.isArray(pids) || !pids.length) return [];
|
|
1064
|
+
if (!Array.isArray(rows)) return null;
|
|
1065
|
+
const wanted = new Set(pids);
|
|
1066
|
+
return rows
|
|
1067
|
+
.filter((row) => wanted.has(row.pid) && !String(row.stat || "").startsWith("Z"))
|
|
1068
|
+
.map((row) => row.pid);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
function signalNativePid(pid, signal) {
|
|
1072
|
+
if (!Number.isInteger(pid) || pid <= 1) return false;
|
|
1073
|
+
try {
|
|
1074
|
+
process.kill(pid, signal);
|
|
1075
|
+
return true;
|
|
1076
|
+
} catch {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function forceStopNativeProcessTree(child) {
|
|
1082
|
+
if (
|
|
1083
|
+
process.platform === "win32" &&
|
|
1084
|
+
child?.__agentlasNativeChild === true &&
|
|
1085
|
+
Number.isInteger(child.pid) &&
|
|
1086
|
+
child.pid > 1
|
|
1087
|
+
) {
|
|
1088
|
+
try {
|
|
1089
|
+
execFileSync("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
1090
|
+
stdio: "ignore",
|
|
1091
|
+
timeout: 5_000,
|
|
1092
|
+
windowsHide: true,
|
|
1093
|
+
});
|
|
1094
|
+
return [child.pid];
|
|
1095
|
+
} catch {
|
|
1096
|
+
return [];
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (
|
|
1100
|
+
!child ||
|
|
1101
|
+
child.__agentlasGroupedChild !== true ||
|
|
1102
|
+
!Number.isInteger(child.pid) ||
|
|
1103
|
+
child.pid <= 1
|
|
1104
|
+
) return [];
|
|
1105
|
+
|
|
1106
|
+
// Native model CLIs may place tool shells in their own process groups. A
|
|
1107
|
+
// group-only kill can therefore close the model process while a Bash tool
|
|
1108
|
+
// keeps writing in the background. Freeze the whole observed ancestry first
|
|
1109
|
+
// so no descendant can advance, take one more snapshot for races, then kill
|
|
1110
|
+
// every owned process. The caller does not report cancellation until all
|
|
1111
|
+
// captured non-zombie PIDs are gone.
|
|
1112
|
+
const firstRows = nativeProcessRows();
|
|
1113
|
+
if (!Array.isArray(firstRows)) return [];
|
|
1114
|
+
const first = nativeProcessTreeIds(child.pid, firstRows);
|
|
1115
|
+
for (const pid of first) signalNativePid(pid, "SIGSTOP");
|
|
1116
|
+
const secondRows = nativeProcessRows();
|
|
1117
|
+
const second = Array.isArray(secondRows) ? nativeProcessTreeIds(child.pid, secondRows) : [];
|
|
1118
|
+
const all = [...new Set([...first, ...second])];
|
|
1119
|
+
for (const pid of second) signalNativePid(pid, "SIGSTOP");
|
|
1120
|
+
for (const pid of all) signalNativePid(pid, "SIGKILL");
|
|
1121
|
+
return all;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function terminateNativeChild(child, signal = "SIGTERM") {
|
|
1125
|
+
if (!child) return false;
|
|
1126
|
+
let signalled = false;
|
|
1127
|
+
if (
|
|
1128
|
+
process.platform !== "win32" &&
|
|
1129
|
+
child.__agentlasGroupedChild === true &&
|
|
1130
|
+
Number.isInteger(child.pid) &&
|
|
1131
|
+
child.pid > 1
|
|
1132
|
+
) {
|
|
1133
|
+
try {
|
|
1134
|
+
process.kill(-child.pid, signal);
|
|
1135
|
+
signalled = true;
|
|
1136
|
+
} catch {
|
|
1137
|
+
// The group may already be gone while the direct child is still
|
|
1138
|
+
// observable. Fall through to the direct-child signal.
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (!signalled) {
|
|
1142
|
+
try {
|
|
1143
|
+
signalled = child.kill(signal);
|
|
1144
|
+
} catch {
|
|
1145
|
+
signalled = false;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (
|
|
1149
|
+
process.platform === "win32" &&
|
|
1150
|
+
signal === "SIGKILL" &&
|
|
1151
|
+
Number.isInteger(child.pid) &&
|
|
1152
|
+
child.pid > 1
|
|
1153
|
+
) {
|
|
1154
|
+
try {
|
|
1155
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
1156
|
+
stdio: "ignore",
|
|
1157
|
+
windowsHide: true,
|
|
1158
|
+
});
|
|
1159
|
+
killer.unref();
|
|
1160
|
+
signalled = true;
|
|
1161
|
+
} catch {
|
|
1162
|
+
// Direct child.kill above remains the best available fallback.
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return signalled;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
981
1168
|
module.exports = {
|
|
982
1169
|
runNativeTurn,
|
|
983
1170
|
summarizeToolInput,
|
|
@@ -997,4 +1184,8 @@ module.exports = {
|
|
|
997
1184
|
codexMcpArgs,
|
|
998
1185
|
nativeTimeoutConfig,
|
|
999
1186
|
directNativeTimeoutConfig,
|
|
1187
|
+
nativeProcessTreeIds,
|
|
1188
|
+
activeNativeProcessIds,
|
|
1189
|
+
forceStopNativeProcessTree,
|
|
1190
|
+
terminateNativeChild,
|
|
1000
1191
|
};
|
|
@@ -1,71 +1,163 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* First-run onboarding wizard (openclaw-style): language → default runtime → default permission.
|
|
3
|
+
* First-run onboarding wizard (openclaw-style): language → default runtime → safe default permission.
|
|
4
4
|
* Runs once; result is saved to cli-prefs.json. Re-run anytime with `agentlas setup`.
|
|
5
5
|
*/
|
|
6
6
|
const i18n = require("./agentlas-i18n.cjs");
|
|
7
7
|
const banner = require("./agentlas-banner.cjs");
|
|
8
|
+
const { visWidth, wrapWidth } = require("./agentlas-composer.cjs");
|
|
8
9
|
|
|
9
10
|
// req = { ui, rl, helpers } → Promise<{ onboarded, lang, runtime, permission }>
|
|
10
11
|
async function runOnboard({ ui, rl, helpers }) {
|
|
11
12
|
const H = helpers;
|
|
12
13
|
const c = ui.c;
|
|
13
|
-
const
|
|
14
|
+
const contentWidth = () => Math.max(20, (ui.out.columns || 80) - 5);
|
|
15
|
+
const printIndented = (value, paint = (text) => text, indent = " ") => {
|
|
16
|
+
const room = Math.max(2, (ui.out.columns || 80) - visWidth(indent));
|
|
17
|
+
for (const line of wrapWidth(value, room)) ui.line(indent + paint(line));
|
|
18
|
+
};
|
|
19
|
+
const printSaved = (value) => {
|
|
20
|
+
const lines = wrapWidth(value, Math.max(2, (ui.out.columns || 80) - 6));
|
|
21
|
+
ui.ok(lines[0] || "");
|
|
22
|
+
for (const line of lines.slice(1)) ui.line(" " + c.text(line));
|
|
23
|
+
};
|
|
24
|
+
const optionLine = (index, label, isDefault = false) => {
|
|
25
|
+
const suffix = isDefault ? ` ${ui.t("wiz.default")}` : "";
|
|
26
|
+
const prefix = `${index} `;
|
|
27
|
+
const continuation = " ".repeat(visWidth(prefix));
|
|
28
|
+
const lines = wrapWidth(`${label}${suffix}`, Math.max(2, contentWidth() - visWidth(prefix)));
|
|
29
|
+
lines.forEach((line, lineIndex) => {
|
|
30
|
+
ui.line(" " + c.text((lineIndex === 0 ? prefix : continuation) + line));
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const pendingLines = [];
|
|
34
|
+
let lineWaiter = null;
|
|
35
|
+
const onLine = (line) => {
|
|
36
|
+
if (lineWaiter) {
|
|
37
|
+
const resolve = lineWaiter;
|
|
38
|
+
lineWaiter = null;
|
|
39
|
+
resolve((line || "").trim());
|
|
40
|
+
} else {
|
|
41
|
+
// A pasted multi-line setup response can arrive in one terminal data
|
|
42
|
+
// burst before the next prompt is installed. Preserve those later lines.
|
|
43
|
+
pendingLines.push((line || "").trim());
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
rl.on("line", onLine);
|
|
47
|
+
const ask = (q) => {
|
|
48
|
+
if (pendingLines.length) return Promise.resolve(pendingLines.shift());
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
lineWaiter = resolve;
|
|
51
|
+
rl.setPrompt(q);
|
|
52
|
+
rl.prompt();
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
let localSigintArmedUntil = 0;
|
|
56
|
+
const ownsSigint = rl.listenerCount("SIGINT") === 0;
|
|
57
|
+
const onSigint = () => {
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
if (now < localSigintArmedUntil) {
|
|
60
|
+
ui.line(ui.c.dim(ui.t("bye")));
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
localSigintArmedUntil = now + 3_000;
|
|
64
|
+
ui.warn(ui.t("ctrlcAgain"));
|
|
65
|
+
};
|
|
66
|
+
if (ownsSigint) rl.on("SIGINT", onSigint);
|
|
14
67
|
const pickNum = async (n) => {
|
|
15
|
-
//
|
|
68
|
+
// Empty keeps the explicitly labelled first option. In a TTY, Up/Down
|
|
69
|
+
// places a real numeric choice into the readline field so arrows and
|
|
70
|
+
// number entry share the same validation path.
|
|
71
|
+
let selected = 1;
|
|
72
|
+
printIndented(ui.t("wiz.nav"), c.dim);
|
|
16
73
|
for (;;) {
|
|
74
|
+
let settled = false;
|
|
75
|
+
let arrowWriteScheduled = false;
|
|
76
|
+
const flushArrowWrite = () => {
|
|
77
|
+
arrowWriteScheduled = false;
|
|
78
|
+
if (settled || rl.closed) return;
|
|
79
|
+
rl.write(null, { ctrl: true, name: "u" });
|
|
80
|
+
rl.write(String(selected));
|
|
81
|
+
};
|
|
82
|
+
const onKeypress = (_str, key = {}) => {
|
|
83
|
+
if (key.name === "return" || key.name === "enter") {
|
|
84
|
+
// readline may emit `line` before the coalesced setImmediate write.
|
|
85
|
+
// Commit the final arrow target synchronously before Enter reaches
|
|
86
|
+
// readline's own key handler.
|
|
87
|
+
if (arrowWriteScheduled) flushArrowWrite();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (key.name !== "up" && key.name !== "down") return;
|
|
91
|
+
selected = key.name === "up"
|
|
92
|
+
? (selected - 2 + n) % n + 1
|
|
93
|
+
: selected % n + 1;
|
|
94
|
+
if (arrowWriteScheduled) return;
|
|
95
|
+
arrowWriteScheduled = true;
|
|
96
|
+
setImmediate(() => {
|
|
97
|
+
if (arrowWriteScheduled) flushArrowWrite();
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
if (rl.terminal && rl.input) rl.input.prependListener("keypress", onKeypress);
|
|
17
101
|
const a = await ask(" " + c.emerald(ui.t("wiz.pick")));
|
|
18
|
-
|
|
102
|
+
settled = true;
|
|
103
|
+
if (rl.input) rl.input.removeListener("keypress", onKeypress);
|
|
104
|
+
if (a === "") return selected;
|
|
19
105
|
if (/^\d+$/.test(a)) {
|
|
20
106
|
const i = parseInt(a, 10);
|
|
21
107
|
if (i >= 1 && i <= n) return i;
|
|
22
108
|
}
|
|
109
|
+
ui.warn(ui.t("wiz.invalid", String(n)));
|
|
23
110
|
}
|
|
24
111
|
};
|
|
25
112
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
113
|
+
try {
|
|
114
|
+
// small mascot + header
|
|
115
|
+
ui.line("");
|
|
116
|
+
banner.renderMascot(ui);
|
|
117
|
+
ui.line(" " + c.bold(c.emerald("Agentlas")) + c.dim(" · setup"));
|
|
118
|
+
printIndented(ui.t("wiz.welcome"), c.dim);
|
|
31
119
|
|
|
32
120
|
// Step 1 — language
|
|
33
121
|
ui.line("");
|
|
34
|
-
|
|
35
|
-
i18n.LANGS.forEach((l, i) =>
|
|
122
|
+
printIndented(ui.t("wiz.langQ"), c.bold);
|
|
123
|
+
i18n.LANGS.forEach((l, i) => optionLine(i + 1, l.label, i === 0));
|
|
36
124
|
const li = await pickNum(i18n.LANGS.length);
|
|
37
125
|
const lang = i18n.LANGS[li - 1].code;
|
|
38
126
|
ui.lang = lang; // localize the rest of the wizard
|
|
39
127
|
|
|
40
128
|
// Step 2 — default runtime
|
|
41
129
|
ui.line("");
|
|
42
|
-
|
|
130
|
+
printIndented(ui.t("wiz.runtimeQ"), c.bold);
|
|
43
131
|
const cliKinds = ["claude-code", "codex", "gemini"];
|
|
44
132
|
const rtOpts = [{ value: "auto", label: ui.t("wiz.runtimeAuto") }];
|
|
45
133
|
for (const k of cliKinds) {
|
|
46
134
|
const has = !!H.which(H.RUNTIME_BIN[k]);
|
|
47
135
|
rtOpts.push({ value: k, label: `${k} (${has ? ui.t("wiz.runtimeInstalled") : ui.t("wiz.runtimeMissing")})` });
|
|
48
136
|
}
|
|
49
|
-
rtOpts.forEach((o, i) =>
|
|
137
|
+
rtOpts.forEach((o, i) => optionLine(i + 1, o.label, i === 0));
|
|
50
138
|
const ri = await pickNum(rtOpts.length);
|
|
51
139
|
const runtime = rtOpts[ri - 1].value;
|
|
52
140
|
|
|
53
141
|
// Step 3 — default permission
|
|
54
142
|
ui.line("");
|
|
55
|
-
|
|
143
|
+
printIndented(ui.t("wiz.permQ"), c.bold);
|
|
56
144
|
const permOpts = [
|
|
57
145
|
{ v: "read", l: ui.t("wiz.permRead") },
|
|
58
146
|
{ v: "write", l: ui.t("wiz.permWrite") },
|
|
59
|
-
{ v: "full", l: ui.t("wiz.permFull") },
|
|
60
147
|
];
|
|
61
|
-
permOpts.forEach((o, i) =>
|
|
148
|
+
permOpts.forEach((o, i) => optionLine(i + 1, o.l, i === 0));
|
|
62
149
|
const pi = await pickNum(permOpts.length);
|
|
63
150
|
const permission = permOpts[pi - 1].v;
|
|
64
151
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
152
|
+
ui.line("");
|
|
153
|
+
printSaved(ui.t("wiz.saved"));
|
|
154
|
+
printIndented(ui.t("wiz.changeLang"), c.faint);
|
|
155
|
+
return { onboarded: true, lang, runtime, permission };
|
|
156
|
+
} finally {
|
|
157
|
+
rl.removeListener("line", onLine);
|
|
158
|
+
if (ownsSigint) rl.removeListener("SIGINT", onSigint);
|
|
159
|
+
lineWaiter = null;
|
|
160
|
+
}
|
|
69
161
|
}
|
|
70
162
|
|
|
71
163
|
module.exports = { runOnboard };
|
|
@@ -14,34 +14,34 @@ const COPY = {
|
|
|
14
14
|
read: {
|
|
15
15
|
label: "read only",
|
|
16
16
|
short: "inspect only",
|
|
17
|
-
description: "inspect
|
|
17
|
+
description: "inspect only; workspace writes blocked",
|
|
18
18
|
},
|
|
19
19
|
write: {
|
|
20
20
|
label: "workspace write",
|
|
21
21
|
short: "edit workspace",
|
|
22
|
-
description: "
|
|
22
|
+
description: "workspace + runtime temp writes; external MCP off",
|
|
23
23
|
},
|
|
24
24
|
full: {
|
|
25
25
|
label: "unrestricted",
|
|
26
26
|
short: "unrestricted",
|
|
27
|
-
description: "bypass
|
|
27
|
+
description: "bypass approvals and sandbox; trusted use only",
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
ko: {
|
|
31
31
|
read: {
|
|
32
32
|
label: "읽기 전용",
|
|
33
33
|
short: "조회만",
|
|
34
|
-
description: "
|
|
34
|
+
description: "조회만; 작업 공간 쓰기 차단",
|
|
35
35
|
},
|
|
36
36
|
write: {
|
|
37
37
|
label: "작업 공간 쓰기",
|
|
38
38
|
short: "작업 공간 편집",
|
|
39
|
-
description: "
|
|
39
|
+
description: "작업 공간·런타임 임시 경로 쓰기; 외부 MCP 꺼짐",
|
|
40
40
|
},
|
|
41
41
|
full: {
|
|
42
42
|
label: "무제한 권한",
|
|
43
43
|
short: "무제한",
|
|
44
|
-
description: "
|
|
44
|
+
description: "승인과 샌드박스를 우회; 신뢰 환경 전용",
|
|
45
45
|
},
|
|
46
46
|
},
|
|
47
47
|
};
|
|
@@ -52,6 +52,13 @@ function normalize(value, fallback = "read") {
|
|
|
52
52
|
return LEVELS.includes(fallback) ? fallback : "read";
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function persistent(value, fallback = "write") {
|
|
56
|
+
const level = normalize(value, fallback);
|
|
57
|
+
if (level !== "full") return level;
|
|
58
|
+
const safeFallback = normalize(fallback, "write");
|
|
59
|
+
return safeFallback === "full" ? "write" : safeFallback;
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
function next(value) {
|
|
56
63
|
const current = normalize(value);
|
|
57
64
|
return LEVELS[(LEVELS.indexOf(current) + 1) % LEVELS.length];
|
|
@@ -87,4 +94,4 @@ function createCycleController(options = {}) {
|
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
module.exports = { LEVELS, normalize, next, copy, createCycleController };
|
|
97
|
+
module.exports = { LEVELS, normalize, persistent, next, copy, createCycleController };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Terminal processes may share the Desktop application's SQLite file. Keep
|
|
4
|
+
// lock waits bounded, and acquire writer authority before a transaction reads
|
|
5
|
+
// state that it may subsequently update. A deferred read-to-write upgrade can
|
|
6
|
+
// fail immediately with SQLITE_BUSY even when busy_timeout is configured.
|
|
7
|
+
const SQLITE_BUSY_TIMEOUT_MS = 15_000;
|
|
8
|
+
|
|
9
|
+
function configureSqliteConnection(db) {
|
|
10
|
+
if (!db) throw new TypeError("SQLite connection is required");
|
|
11
|
+
if (typeof db.pragma === "function") {
|
|
12
|
+
db.pragma(`busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`);
|
|
13
|
+
} else if (typeof db.exec === "function") {
|
|
14
|
+
db.exec(`PRAGMA busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`);
|
|
15
|
+
} else {
|
|
16
|
+
throw new TypeError("SQLite connection must expose pragma() or exec()");
|
|
17
|
+
}
|
|
18
|
+
return db;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function runWriteTransaction(db, fn, ...args) {
|
|
22
|
+
if (!db || typeof db.transaction !== "function") return fn(...args);
|
|
23
|
+
const transaction = db.transaction(fn);
|
|
24
|
+
if (typeof transaction.immediate === "function") {
|
|
25
|
+
return transaction.immediate(...args);
|
|
26
|
+
}
|
|
27
|
+
return transaction(...args);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
SQLITE_BUSY_TIMEOUT_MS,
|
|
32
|
+
configureSqliteConnection,
|
|
33
|
+
runWriteTransaction,
|
|
34
|
+
};
|