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
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* hephaestus/runtime — Hephaestus(Agentlas OS Core) 네이티브 패스스루 클러스터.
|
|
4
|
+
*
|
|
5
|
+
* v1 출처: engine/agentlas-parity.cjs (git tag legacy-v1-engine-snapshot)
|
|
6
|
+
* - hephaestusBin / careerGraphRuntime / spawnHephaestus / captureHephaestus
|
|
7
|
+
* - runHephaestusRoute / runHephaestusResearch (사람용 렌더러)
|
|
8
|
+
* - runHephaestusInteractive (stdio inherit 패스스루 공용 러너)
|
|
9
|
+
* - HEP_USAGE / cmdHep
|
|
10
|
+
*
|
|
11
|
+
* v2 이식 규칙:
|
|
12
|
+
* - D.out → ctx.out, D.prefsLang() → ctx.lang.
|
|
13
|
+
* - D.runCwd — v1 검증 결과: AGENTLAS_RUN_CWD 같은 env는 v1에 존재하지 않았다.
|
|
14
|
+
* v1 runCwd()의 실제 동작 = userDataDir()/agent-cwd 샌드박스(mkdir, 실패 시
|
|
15
|
+
* homedir 폴백). 그 동작을 그대로 보존해 여기 로컬로 이식했다. (프로젝트
|
|
16
|
+
* 실행 위치가 필요한 곳은 v1과 동일하게 projectCwd()를 쓴다.)
|
|
17
|
+
* - 런타임 발견은 engine/agentlas-core-harness.cjs(복원본)를 소비만 한다.
|
|
18
|
+
* - 런타임 부재 = 정직 정지(exit 1) + v1 설치 안내 문구 그대로. 폴백 금지.
|
|
19
|
+
*
|
|
20
|
+
* ⚠ v1 결함 제거: careerGraphRuntime()의 root 후보에 있던
|
|
21
|
+
* path.resolve(__dirname, "..", "..", "agentlas_desktop", "Hephaestus")
|
|
22
|
+
* — 개발 머신에서만 존재하는 형제 저장소 상대경로 — 는 결함으로 판정되어
|
|
23
|
+
* v2에서 삭제했다. 설치 런처/env/앱 번들 후보만 남긴다.
|
|
24
|
+
*/
|
|
25
|
+
const os = require("node:os");
|
|
26
|
+
const path = require("node:path");
|
|
27
|
+
const fs = require("node:fs");
|
|
28
|
+
const { spawn } = require("node:child_process");
|
|
29
|
+
const { Ui } = require("../agentlas-ui.cjs");
|
|
30
|
+
const { truncateWidth, visWidth, wrapWidth } = require("../agentlas-composer.cjs");
|
|
31
|
+
const coreHarness = require("../agentlas-core-harness.cjs");
|
|
32
|
+
const { userDataDir } = require("../core/paths.cjs");
|
|
33
|
+
|
|
34
|
+
const { CONTEXT_MAP_MIN_CORE_VERSION } = coreHarness;
|
|
35
|
+
|
|
36
|
+
// ── 명령 usage 문자열 (v1 TOP_LEVEL_COMMAND_USAGE에서 hephaestus 클러스터만 발췌) ──
|
|
37
|
+
const USAGE = Object.freeze({
|
|
38
|
+
build: 'usage: agentlas build "<request>"',
|
|
39
|
+
browser: "usage: agentlas browser <url-or-query|subcommand>",
|
|
40
|
+
call: 'usage: agentlas call "<agent-slugs>" "<context>"',
|
|
41
|
+
connect: "usage: agentlas connect [status|telegram|help]",
|
|
42
|
+
hep: "usage: agentlas hep <subcommand> [args]",
|
|
43
|
+
hephaestus: "usage: agentlas hephaestus <subcommand> [args]",
|
|
44
|
+
journal: "usage: agentlas journal <status|verify|repair|gate> --run-id <id> | --journal <path>",
|
|
45
|
+
"legacy-network": 'usage: agentlas legacy-network "<request>"',
|
|
46
|
+
netadmin: "usage: agentlas netadmin <init|status|reindex|bench|add-source> [args]",
|
|
47
|
+
research: "usage: agentlas research <status|gather|search|read|plan> [args]",
|
|
48
|
+
route: 'usage: agentlas route "<request>" [--json]',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// v1 localizedTopLevelUsage와 동일한 한국어 변환.
|
|
52
|
+
function usageFor(cmd, lang) {
|
|
53
|
+
const usage = USAGE[cmd];
|
|
54
|
+
if (!usage) return null;
|
|
55
|
+
if (lang !== "ko") return usage;
|
|
56
|
+
return usage
|
|
57
|
+
.replace(/^usage:/, "사용법:")
|
|
58
|
+
.replace(/\[args\]/g, "[인자]")
|
|
59
|
+
.replace(/<subcommand>/g, "<하위-명령>");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isHelpToken(value) {
|
|
63
|
+
return value === "help" || value === "--help" || value === "-h";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── 라우팅/리서치 미리보기 모델 (v1 module-level 순수 함수, 그대로) ──
|
|
67
|
+
function routePreviewModel(value) {
|
|
68
|
+
const json = value && typeof value === "object" ? value : {};
|
|
69
|
+
const execution = json.execution && typeof json.execution === "object" ? json.execution : {};
|
|
70
|
+
const hubResults = Array.isArray(json.hub?.results) ? json.hub.results : [];
|
|
71
|
+
const idOf = (candidate) => {
|
|
72
|
+
if (typeof candidate === "string") return candidate;
|
|
73
|
+
return candidate && (candidate.slug || candidate.id || candidate.agent || candidate.name) || null;
|
|
74
|
+
};
|
|
75
|
+
const lookup = (candidate) => {
|
|
76
|
+
const id = idOf(candidate);
|
|
77
|
+
if (!id) return null;
|
|
78
|
+
const match = hubResults.find((item) => idOf(item) === id);
|
|
79
|
+
const source = match || (candidate && typeof candidate === "object" ? candidate : {});
|
|
80
|
+
return {
|
|
81
|
+
id,
|
|
82
|
+
name: source.name || source.nameEn || source.title || id,
|
|
83
|
+
nameEn: source.nameEn || source.name_en || source.name || source.title || id,
|
|
84
|
+
kind: source.kind || source.entityKind || "",
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const selected = lookup(json.selected);
|
|
88
|
+
const primary = lookup(execution.primary_agent || execution.recommended_agents?.[0]);
|
|
89
|
+
const candidates = [];
|
|
90
|
+
const add = (candidate) => {
|
|
91
|
+
const normalized = lookup(candidate);
|
|
92
|
+
if (!normalized || candidates.some((item) => item.id === normalized.id)) return;
|
|
93
|
+
candidates.push(normalized);
|
|
94
|
+
};
|
|
95
|
+
add(selected);
|
|
96
|
+
add(primary);
|
|
97
|
+
for (const candidate of hubResults) add(candidate);
|
|
98
|
+
for (const candidate of execution.recommended_agents || []) add(candidate);
|
|
99
|
+
for (const candidate of execution.alternatives || []) add(candidate);
|
|
100
|
+
return {
|
|
101
|
+
selected,
|
|
102
|
+
primary,
|
|
103
|
+
candidates: candidates.slice(0, 6),
|
|
104
|
+
scope: json.scope || json.hub?.scope || null,
|
|
105
|
+
receiptId: json.receipt_id || null,
|
|
106
|
+
needsRouter: !selected && json.router_agent?.mode === "escalate_to_router_agent",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function researchPreviewModel(value) {
|
|
111
|
+
const json = value && typeof value === "object" ? value : {};
|
|
112
|
+
const request = json.request && typeof json.request === "object" ? json.request : {};
|
|
113
|
+
const receipt = json.receipt && typeof json.receipt === "object" ? json.receipt : {};
|
|
114
|
+
const policy = receipt.policy && typeof receipt.policy === "object"
|
|
115
|
+
? receipt.policy
|
|
116
|
+
: (json.policy && typeof json.policy === "object" ? json.policy : {});
|
|
117
|
+
const capability = json.capability_summary && typeof json.capability_summary === "object"
|
|
118
|
+
? json.capability_summary
|
|
119
|
+
: {};
|
|
120
|
+
const quality = policy.evidence_quality && typeof policy.evidence_quality === "object"
|
|
121
|
+
? policy.evidence_quality
|
|
122
|
+
: {};
|
|
123
|
+
const coverage = policy.evidence_coverage && typeof policy.evidence_coverage === "object"
|
|
124
|
+
? policy.evidence_coverage
|
|
125
|
+
: {};
|
|
126
|
+
const attempts = Array.isArray(receipt.attempts) ? receipt.attempts : [];
|
|
127
|
+
const results = Array.isArray(json.results) ? json.results : [];
|
|
128
|
+
const summary = json.summary && typeof json.summary === "object" ? json.summary : {};
|
|
129
|
+
const modules = [
|
|
130
|
+
...(Array.isArray(receipt.module_chain) ? receipt.module_chain : []),
|
|
131
|
+
...(Array.isArray(json.ready_mounted_modules) ? json.ready_mounted_modules : []),
|
|
132
|
+
...(Array.isArray(json.mounted_modules) ? json.mounted_modules : []),
|
|
133
|
+
].filter((item, index, array) => item && array.indexOf(item) === index);
|
|
134
|
+
const intent = request.intent || (
|
|
135
|
+
String(json.schema || "").includes(".status.") ? "status"
|
|
136
|
+
: String(json.schema || "").includes(".plan.") ? "plan"
|
|
137
|
+
: "research"
|
|
138
|
+
);
|
|
139
|
+
const maxRequests = request.max_cost?.requests
|
|
140
|
+
?? policy.request_budget?.max_requests
|
|
141
|
+
?? policy.max_cost_requests
|
|
142
|
+
?? null;
|
|
143
|
+
const totalEvidenceFailure = ["search", "read", "gather"].includes(intent)
|
|
144
|
+
&& results.length === 0
|
|
145
|
+
&& (
|
|
146
|
+
capability.status === "missing_evidence"
|
|
147
|
+
|| capability.trust?.can_use_for_build_context === false
|
|
148
|
+
|| quality.status === "none"
|
|
149
|
+
|| coverage.status === "missing"
|
|
150
|
+
);
|
|
151
|
+
return {
|
|
152
|
+
schema: json.schema || "",
|
|
153
|
+
status: json.status || "unknown",
|
|
154
|
+
intent,
|
|
155
|
+
query: request.query || "",
|
|
156
|
+
loadout: request.loadout || policy.loadout?.name || "",
|
|
157
|
+
depth: request.depth || "",
|
|
158
|
+
maxRequests,
|
|
159
|
+
networkWillRun: json.network_will_run ?? policy.network_will_run,
|
|
160
|
+
receiptWillBeWritten: policy.receipt_will_be_written,
|
|
161
|
+
privateHostsBlocked: policy.private_hosts_blocked,
|
|
162
|
+
browserMounted: policy.browser_modules_mounted,
|
|
163
|
+
goalReady: json.goal_ready,
|
|
164
|
+
coreReady: summary.core_engine_ok,
|
|
165
|
+
publicFallbackReady: summary.public_social_fallbacks_ok,
|
|
166
|
+
browserReady: summary.browser_hardpoint_ok,
|
|
167
|
+
socialReady: summary.credentialed_social_ok,
|
|
168
|
+
incompleteCount: summary.incomplete_count,
|
|
169
|
+
missingProofs: Array.isArray(summary.missing_or_unready_proofs) ? summary.missing_or_unready_proofs : [],
|
|
170
|
+
modules,
|
|
171
|
+
attempts,
|
|
172
|
+
results,
|
|
173
|
+
qualityStatus: quality.status || "",
|
|
174
|
+
qualityScore: quality.score,
|
|
175
|
+
coverageStatus: coverage.status || "",
|
|
176
|
+
warnings: Array.isArray(capability.trust?.warnings) ? capability.trust.warnings : [],
|
|
177
|
+
canUseForBuildContext: capability.trust?.can_use_for_build_context,
|
|
178
|
+
receiptId: receipt.receipt_id || json.receipt_id || "",
|
|
179
|
+
totalEvidenceFailure,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* create(ctx, deps?) — v2 ctx(DI)로 바인딩된 러너 묶음을 만든다.
|
|
185
|
+
* ctx: { lang, out, err } (엔진 ctx 그대로 전달해도 된다)
|
|
186
|
+
* deps: 테스트 전용 주입 이음새. 기본값은 실제 core-harness.
|
|
187
|
+
* (v1도 create(deps) 주입 팩토리였다 — 같은 구조를 유지한다.)
|
|
188
|
+
*/
|
|
189
|
+
function create(ctx, deps = {}) {
|
|
190
|
+
const resolveCoreRuntimeRoot = deps.resolveCoreRuntimeRoot || coreHarness.resolveCoreRuntimeRoot;
|
|
191
|
+
const spawnCoreModule = deps.spawnCoreModule || coreHarness.spawnCoreModule;
|
|
192
|
+
const lang = () => (ctx && ctx.lang) || "en";
|
|
193
|
+
|
|
194
|
+
function newUi(uiLang) {
|
|
195
|
+
return new Ui({ lang: uiLang || lang() });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// v1 agentlas.cjs runCwd() 그대로: 에이전트 전용 안전 샌드박스 폴더.
|
|
199
|
+
// (검증: v1에 AGENTLAS_RUN_CWD env는 없었다 — process.cwd() 매핑이 아니라
|
|
200
|
+
// 이 샌드박스가 원래 동작이므로 그대로 보존한다.)
|
|
201
|
+
function runCwd() {
|
|
202
|
+
const dir = path.join(userDataDir(), "agent-cwd");
|
|
203
|
+
try {
|
|
204
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
205
|
+
return dir;
|
|
206
|
+
} catch {
|
|
207
|
+
return os.homedir();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// v1: 에이전트가 실제로 실행될 작업 폴더 = 사용자가 명령을 친 현재 디렉터리.
|
|
212
|
+
// 단, home/userData/agent-cwd 같은 "프로젝트 아님" 위치면 샌드박스로 폴백.
|
|
213
|
+
function projectCwd() {
|
|
214
|
+
try {
|
|
215
|
+
const cwd = process.cwd();
|
|
216
|
+
if (!cwd || cwd === os.homedir() || cwd === userDataDir() || cwd === runCwd()) return runCwd();
|
|
217
|
+
return cwd;
|
|
218
|
+
} catch {
|
|
219
|
+
return runCwd();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ── Hephaestus 런타임 해석 (설치 런처 우선, 앱 번들 폴백) ──
|
|
224
|
+
// v1 후보 순서 그대로: HEPHAESTUS_BIN → ~/.agentlas/runtime/current/bin/hephaestus
|
|
225
|
+
// → Core python 모듈(resolveCoreRuntimeRoot). win32는 bin 후보를 건너뛴다(v1 동일).
|
|
226
|
+
function hephaestusBin() {
|
|
227
|
+
const candidates = [
|
|
228
|
+
process.env.HEPHAESTUS_BIN,
|
|
229
|
+
path.join(os.homedir(), ".agentlas", "runtime", "current", "bin", "hephaestus"),
|
|
230
|
+
];
|
|
231
|
+
for (const c of process.platform === "win32" ? [] : candidates) {
|
|
232
|
+
try {
|
|
233
|
+
if (c && fs.existsSync(c)) {
|
|
234
|
+
fs.accessSync(c, fs.constants.X_OK);
|
|
235
|
+
return { kind: "bin", exec: c };
|
|
236
|
+
}
|
|
237
|
+
} catch { /* 다음 후보 */ }
|
|
238
|
+
}
|
|
239
|
+
const root = resolveCoreRuntimeRoot();
|
|
240
|
+
if (root) return { kind: "python", root };
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function careerGraphRuntime() {
|
|
245
|
+
const binCandidates = [
|
|
246
|
+
process.env.HEPHAESTUS_CAREER_GRAPH_BIN,
|
|
247
|
+
process.env.HEPHAESTUS_BIN ? path.join(path.dirname(process.env.HEPHAESTUS_BIN), "career-graph") : null,
|
|
248
|
+
path.join(os.homedir(), ".agentlas", "runtime", "current", "bin", "career-graph"),
|
|
249
|
+
];
|
|
250
|
+
for (const c of binCandidates) {
|
|
251
|
+
try {
|
|
252
|
+
if (c && fs.existsSync(c)) {
|
|
253
|
+
fs.accessSync(c, fs.constants.X_OK);
|
|
254
|
+
return { kind: "bin", exec: c };
|
|
255
|
+
}
|
|
256
|
+
} catch { /* 다음 후보 */ }
|
|
257
|
+
}
|
|
258
|
+
const roots = [
|
|
259
|
+
process.env.HEPHAESTUS_RUNTIME_ROOT,
|
|
260
|
+
path.join(os.homedir(), ".agentlas", "runtime", "current"),
|
|
261
|
+
];
|
|
262
|
+
if (process.resourcesPath) roots.push(path.join(process.resourcesPath, "Hephaestus"));
|
|
263
|
+
if (process.platform === "darwin") roots.push("/Applications/Agentlas.app/Contents/Resources/Hephaestus");
|
|
264
|
+
// v1에는 여기 형제 저장소 상대경로(../../agentlas_desktop/Hephaestus)가 있었다.
|
|
265
|
+
// 개발 머신에서만 성립하는 결함으로 확정되어 v2에서 제거했다 (파일 상단 참조).
|
|
266
|
+
for (const root of roots) {
|
|
267
|
+
try {
|
|
268
|
+
if (root && fs.existsSync(path.join(root, "career_graph", "__main__.py"))) return { kind: "python", root };
|
|
269
|
+
} catch { /* 다음 후보 */ }
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function spawnHephaestus(args, opts) {
|
|
275
|
+
const found = hephaestusBin();
|
|
276
|
+
if (!found) return null;
|
|
277
|
+
if (found.kind === "bin") return spawn(found.exec, args, opts);
|
|
278
|
+
return spawnCoreModule("agentlas_cloud", args, opts, found.root);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function captureHephaestus(args, opts = {}) {
|
|
282
|
+
const cwd = opts.cwd || runCwd();
|
|
283
|
+
const maxBytes = 2 * 1024 * 1024;
|
|
284
|
+
const timeoutMs = Number(opts.timeoutMs) > 0 ? Number(opts.timeoutMs) : 30_000;
|
|
285
|
+
return new Promise((resolve) => {
|
|
286
|
+
const child = spawnHephaestus(args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
287
|
+
if (!child) return resolve({ code: 1, stdout: "", stderr: "Hephaestus runtime unavailable.", unavailable: true });
|
|
288
|
+
const stdout = [];
|
|
289
|
+
const stderr = [];
|
|
290
|
+
let bytes = 0;
|
|
291
|
+
let overflow = false;
|
|
292
|
+
let timedOut = false;
|
|
293
|
+
let settled = false;
|
|
294
|
+
const append = (target, chunk) => {
|
|
295
|
+
if (overflow) return;
|
|
296
|
+
bytes += chunk.length;
|
|
297
|
+
if (bytes > maxBytes) {
|
|
298
|
+
overflow = true;
|
|
299
|
+
child.kill("SIGTERM");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
target.push(chunk);
|
|
303
|
+
};
|
|
304
|
+
child.stdout.on("data", (chunk) => append(stdout, Buffer.from(chunk)));
|
|
305
|
+
child.stderr.on("data", (chunk) => append(stderr, Buffer.from(chunk)));
|
|
306
|
+
const onInterrupt = () => child.kill("SIGINT");
|
|
307
|
+
process.once("SIGINT", onInterrupt);
|
|
308
|
+
const timer = setTimeout(() => {
|
|
309
|
+
timedOut = true;
|
|
310
|
+
child.kill("SIGTERM");
|
|
311
|
+
}, timeoutMs);
|
|
312
|
+
if (timer.unref) timer.unref();
|
|
313
|
+
const done = (code, error) => {
|
|
314
|
+
if (settled) return;
|
|
315
|
+
settled = true;
|
|
316
|
+
clearTimeout(timer);
|
|
317
|
+
process.removeListener("SIGINT", onInterrupt);
|
|
318
|
+
resolve({
|
|
319
|
+
code: overflow || timedOut || error ? 1 : (code ?? 0),
|
|
320
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
321
|
+
stderr: error ? String(error.message || error) : Buffer.concat(stderr).toString("utf8"),
|
|
322
|
+
overflow,
|
|
323
|
+
timedOut,
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
child.on("error", (error) => done(1, error));
|
|
327
|
+
child.on("close", (code) => done(code));
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function renderRoutePreview(json, ui) {
|
|
332
|
+
const model = routePreviewModel(json);
|
|
333
|
+
const ko = ui.lang === "ko";
|
|
334
|
+
const room = Math.max(28, (ui.out.columns || 80) - 2);
|
|
335
|
+
const emit = (prefix, value, paint = (text) => ui.c.text(text)) => {
|
|
336
|
+
const label = String(prefix || "");
|
|
337
|
+
const continuation = " ".repeat(visWidth(label));
|
|
338
|
+
const lines = wrapWidth(String(value || ""), Math.max(2, room - visWidth(label)));
|
|
339
|
+
lines.forEach((line, index) => {
|
|
340
|
+
ui.line(" " + (index === 0 ? ui.c.faint(label) : continuation) + paint(line));
|
|
341
|
+
});
|
|
342
|
+
};
|
|
343
|
+
const scope = model.scope === "cloud"
|
|
344
|
+
? (ko ? "내 Agent Cloud 보관함" : "my Agent Cloud library")
|
|
345
|
+
: model.scope === "network"
|
|
346
|
+
? (ko ? "공개 Agentlas Hub" : "public Agentlas Hub")
|
|
347
|
+
: (model.scope || (ko ? "자동" : "automatic"));
|
|
348
|
+
ui.line("");
|
|
349
|
+
ui.rule(ko ? "라우팅 미리보기" : "Route preview");
|
|
350
|
+
emit(ko ? "범위: " : "Scope: ", scope);
|
|
351
|
+
if (model.selected) {
|
|
352
|
+
emit(ko ? "선택: " : "Selected: ", `${model.selected.name} (${model.selected.id})`, (text) => ui.c.emerald(text));
|
|
353
|
+
} else {
|
|
354
|
+
emit(
|
|
355
|
+
ko ? "결정: " : "Decision: ",
|
|
356
|
+
ko ? "아직 자동 선택하지 않음" : "no automatic selection yet",
|
|
357
|
+
(text) => ui.c.amber(text),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
if (model.candidates.length) {
|
|
361
|
+
emit(ko ? "후보: " : "Candidates: ", ko ? `${model.candidates.length}개` : String(model.candidates.length));
|
|
362
|
+
model.candidates.forEach((candidate, index) => {
|
|
363
|
+
const primary = model.primary && candidate.id === model.primary.id
|
|
364
|
+
? (ko ? " · 우선 후보" : " · primary candidate")
|
|
365
|
+
: "";
|
|
366
|
+
const name = ko ? candidate.name : candidate.nameEn;
|
|
367
|
+
emit(`${index + 1}. `, `${name} (${candidate.id})${primary}`);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
if (model.needsRouter) {
|
|
371
|
+
emit(
|
|
372
|
+
ko ? "이유: " : "Why: ",
|
|
373
|
+
ko
|
|
374
|
+
? "관련 후보는 찾았지만 자동 선택 확신이 부족합니다."
|
|
375
|
+
: "Candidates matched, but confidence was too low for an automatic choice.",
|
|
376
|
+
);
|
|
377
|
+
} else if (!model.candidates.length) {
|
|
378
|
+
emit(
|
|
379
|
+
ko ? "이유: " : "Why: ",
|
|
380
|
+
ko ? "일치하는 후보를 찾지 못했습니다." : "No matching candidate was found.",
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (!model.selected && model.primary) {
|
|
384
|
+
emit(
|
|
385
|
+
ko ? "다음: " : "Next: ",
|
|
386
|
+
`agentlas call "${model.primary.id}" "<request>"`,
|
|
387
|
+
(text) => ui.c.emerald(text),
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
if (model.receiptId) emit(ko ? "영수증: " : "Receipt: ", model.receiptId);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function runHephaestusRoute(args, opts = {}) {
|
|
394
|
+
const raw = args.includes("--json");
|
|
395
|
+
const cleanArgs = args.filter((arg) => arg !== "--json");
|
|
396
|
+
if (raw) return runHephaestusInteractive(cleanArgs, { ...opts, human: false });
|
|
397
|
+
if (!hephaestusBin()) return runHephaestusInteractive(cleanArgs, { ...opts, human: false });
|
|
398
|
+
const ui = opts.ui || newUi();
|
|
399
|
+
const result = await captureHephaestus(cleanArgs, opts);
|
|
400
|
+
let json = null;
|
|
401
|
+
try {
|
|
402
|
+
const start = result.stdout.indexOf("{");
|
|
403
|
+
const end = result.stdout.lastIndexOf("}");
|
|
404
|
+
if (start >= 0 && end > start) json = JSON.parse(result.stdout.slice(start, end + 1));
|
|
405
|
+
} catch { /* handled below */ }
|
|
406
|
+
const renderError = (message) => {
|
|
407
|
+
const room = Math.max(28, (ui.out.columns || 80) - 2);
|
|
408
|
+
wrapWidth(message, Math.max(2, room - 2)).forEach((line, index) => {
|
|
409
|
+
ui.line(" " + (index === 0 ? ui.c.paw("✗ ") : " ") + ui.c.text(line));
|
|
410
|
+
});
|
|
411
|
+
};
|
|
412
|
+
if (json) renderRoutePreview(json, ui);
|
|
413
|
+
else {
|
|
414
|
+
const detail = (result.stderr || result.stdout || "").trim();
|
|
415
|
+
const message = detail || (ui.lang === "ko" ? "라우팅 결과를 읽지 못했습니다." : "Could not read the route result.");
|
|
416
|
+
renderError(message);
|
|
417
|
+
}
|
|
418
|
+
if (result.overflow) renderError(ui.lang === "ko" ? "라우팅 출력이 2MB 제한을 넘었습니다." : "Route output exceeded the 2 MB limit.");
|
|
419
|
+
if (result.timedOut) renderError(ui.lang === "ko" ? "라우팅이 30초 제한을 넘었습니다." : "Route timed out after 30 seconds.");
|
|
420
|
+
return result.code;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function renderResearchPreview(json, ui) {
|
|
424
|
+
const model = researchPreviewModel(json);
|
|
425
|
+
const ko = ui.lang === "ko";
|
|
426
|
+
const room = Math.max(28, (ui.out.columns || 80) - 2);
|
|
427
|
+
const emit = (prefix, value, paint = (text) => ui.c.text(text)) => {
|
|
428
|
+
const label = String(prefix || "");
|
|
429
|
+
const continuation = " ".repeat(visWidth(label));
|
|
430
|
+
const lines = wrapWidth(String(value ?? ""), Math.max(2, room - visWidth(label)));
|
|
431
|
+
lines.forEach((line, index) => {
|
|
432
|
+
ui.line(" " + (index === 0 ? ui.c.faint(label) : continuation) + paint(line));
|
|
433
|
+
});
|
|
434
|
+
};
|
|
435
|
+
const ready = (value) => value === true
|
|
436
|
+
? (ko ? "준비됨" : "ready")
|
|
437
|
+
: value === false
|
|
438
|
+
? (ko ? "준비 안 됨" : "not ready")
|
|
439
|
+
: (ko ? "확인 안 됨" : "unknown");
|
|
440
|
+
const status = model.status === "ok"
|
|
441
|
+
? (ko ? "성공" : "ok")
|
|
442
|
+
: model.status === "partial"
|
|
443
|
+
? (ko ? "일부만 완료" : "partial")
|
|
444
|
+
: model.status;
|
|
445
|
+
const title = model.intent === "status"
|
|
446
|
+
? (ko ? "리서치 준비 상태" : "Research status")
|
|
447
|
+
: model.intent === "plan"
|
|
448
|
+
? (ko ? "리서치 실행 계획" : "Research plan")
|
|
449
|
+
: model.intent === "read"
|
|
450
|
+
? (ko ? "리서치 읽기 결과" : "Research read")
|
|
451
|
+
: model.intent === "gather"
|
|
452
|
+
? (ko ? "리서치 수집 결과" : "Research gather")
|
|
453
|
+
: (ko ? "리서치 검색 결과" : "Research search");
|
|
454
|
+
ui.line("");
|
|
455
|
+
ui.rule(title);
|
|
456
|
+
emit(ko ? "상태: " : "Status: ", status, model.status === "ok" ? (text) => ui.c.emerald(text) : (text) => ui.c.amber(text));
|
|
457
|
+
if (model.query) emit(ko ? "질문: " : "Query: ", model.query);
|
|
458
|
+
|
|
459
|
+
if (model.intent === "status") {
|
|
460
|
+
emit(ko ? "전체: " : "Overall: ", ready(model.goalReady));
|
|
461
|
+
emit(ko ? "코어: " : "Core: ", ready(model.coreReady));
|
|
462
|
+
emit(ko ? "공개 소스: " : "Public sources: ", ready(model.publicFallbackReady));
|
|
463
|
+
emit(ko ? "브라우저: " : "Browser proof: ", ready(model.browserReady));
|
|
464
|
+
emit(ko ? "소셜: " : "Social proof: ", ready(model.socialReady));
|
|
465
|
+
if (model.incompleteCount != null) emit(ko ? "누락: " : "Missing: ", String(model.incompleteCount));
|
|
466
|
+
const proofNames = {
|
|
467
|
+
reddit_oauth_live_check: ko ? "Reddit OAuth 실시간 증거" : "Reddit OAuth live proof",
|
|
468
|
+
threads_live_graph_check: ko ? "Threads Graph 실시간 증거" : "Threads Graph live proof",
|
|
469
|
+
browser_hardpoint_live_check: ko ? "브라우저 hardpoint 실시간 증거" : "browser hardpoint live proof",
|
|
470
|
+
};
|
|
471
|
+
for (const proof of model.missingProofs.slice(0, 5)) emit("• ", proofNames[proof] || proof);
|
|
472
|
+
emit(ko ? "네트워크: " : "Network: ", model.networkWillRun ? (ko ? "실행함" : "will run") : (ko ? "실행 안 함" : "will not run"));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (model.intent === "plan") {
|
|
477
|
+
if (model.loadout) emit(ko ? "구성: " : "Loadout: ", model.loadout);
|
|
478
|
+
if (model.depth) emit(ko ? "깊이: " : "Depth: ", model.depth);
|
|
479
|
+
if (model.maxRequests != null) emit(ko ? "요청 한도: " : "Request limit: ", String(model.maxRequests));
|
|
480
|
+
emit(ko ? "네트워크: " : "Network: ", model.networkWillRun ? (ko ? "실행함" : "will run") : (ko ? "계획 단계에서는 실행 안 함" : "not run during planning"));
|
|
481
|
+
if (model.receiptWillBeWritten != null) {
|
|
482
|
+
emit(ko ? "영수증: " : "Receipt: ", model.receiptWillBeWritten ? (ko ? "기록함" : "will be written") : (ko ? "계획 단계에서는 기록 안 함" : "not written during planning"));
|
|
483
|
+
}
|
|
484
|
+
if (model.privateHostsBlocked != null) emit(ko ? "사설 주소: " : "Private hosts: ", model.privateHostsBlocked ? (ko ? "차단" : "blocked") : (ko ? "허용" : "allowed"));
|
|
485
|
+
if (model.browserMounted != null) emit(ko ? "브라우저: " : "Browser: ", model.browserMounted ? (ko ? "포함" : "mounted") : (ko ? "포함 안 함" : "not mounted"));
|
|
486
|
+
if (model.modules.length) emit(ko ? "모듈: " : "Modules: ", model.modules.join(", "));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (model.loadout) emit(ko ? "구성: " : "Loadout: ", model.loadout);
|
|
491
|
+
if (model.maxRequests != null) {
|
|
492
|
+
emit(
|
|
493
|
+
ko ? "요청: " : "Requests: ",
|
|
494
|
+
`${model.attempts.length}/${model.maxRequests}`,
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
if (model.coverageStatus) {
|
|
498
|
+
const coverage = {
|
|
499
|
+
search_only: ko ? "검색 스니펫만" : "search snippets only",
|
|
500
|
+
direct_read: ko ? "본문 직접 읽음" : "direct read",
|
|
501
|
+
missing: ko ? "증거 없음" : "missing",
|
|
502
|
+
};
|
|
503
|
+
emit(ko ? "증거: " : "Evidence: ", coverage[model.coverageStatus] || model.coverageStatus);
|
|
504
|
+
}
|
|
505
|
+
if (model.qualityStatus) {
|
|
506
|
+
const score = model.qualityScore == null ? "" : ` · ${model.qualityScore}/100`;
|
|
507
|
+
emit(ko ? "품질: " : "Quality: ", `${model.qualityStatus}${score}`);
|
|
508
|
+
}
|
|
509
|
+
if (model.modules.length) emit(ko ? "모듈: " : "Modules: ", model.modules.join(", "));
|
|
510
|
+
emit(ko ? "결과: " : "Results: ", String(model.results.length));
|
|
511
|
+
model.results.slice(0, 5).forEach((result, index) => {
|
|
512
|
+
emit(`${index + 1}. `, result.title || result.name || (ko ? "제목 없음" : "Untitled"), (text) => ui.c.emerald(text));
|
|
513
|
+
if (result.url) {
|
|
514
|
+
const urlLabel = ko ? "주소: " : "URL: ";
|
|
515
|
+
emit(urlLabel, truncateWidth(result.url, Math.max(8, room - visWidth(urlLabel))));
|
|
516
|
+
}
|
|
517
|
+
const meta = [result.confidence, result.freshness].filter(Boolean).join(" · ");
|
|
518
|
+
if (meta) emit(ko ? "근거: " : "Evidence: ", meta);
|
|
519
|
+
});
|
|
520
|
+
for (const warning of model.warnings.slice(0, 3)) {
|
|
521
|
+
const text = warning === "search_snippets_need_followup"
|
|
522
|
+
? (ko ? "검색 스니펫은 원문 확인이 더 필요합니다." : "Search snippets still need a direct read.")
|
|
523
|
+
: warning;
|
|
524
|
+
emit(ko ? "주의: " : "Warning: ", text, (line) => ui.c.amber(line));
|
|
525
|
+
}
|
|
526
|
+
if (model.totalEvidenceFailure) {
|
|
527
|
+
emit(
|
|
528
|
+
ko ? "실패: " : "Failure: ",
|
|
529
|
+
ko ? "사용할 수 있는 증거를 얻지 못했습니다." : "No usable evidence was obtained.",
|
|
530
|
+
(text) => ui.c.paw(text),
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
if (model.receiptId) emit(ko ? "영수증: " : "Receipt: ", model.receiptId);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function runHephaestusResearch(args, opts = {}) {
|
|
537
|
+
if (args.some((arg) => arg === "--help" || arg === "-h" || arg === "help")) {
|
|
538
|
+
return runHephaestusInteractive(args, { ...opts, human: false });
|
|
539
|
+
}
|
|
540
|
+
const raw = args.includes("--json");
|
|
541
|
+
const cleanArgs = args.filter((arg) => arg !== "--json");
|
|
542
|
+
if (!hephaestusBin()) return runHephaestusInteractive(cleanArgs, { ...opts, human: false });
|
|
543
|
+
const ui = opts.ui || newUi();
|
|
544
|
+
if (!raw) ui.startSpinner(ui.lang === "ko" ? "리서치 실행 중…" : "Running research…");
|
|
545
|
+
const result = await captureHephaestus(cleanArgs, {
|
|
546
|
+
...opts,
|
|
547
|
+
timeoutMs: Number(opts.timeoutMs) > 0 ? Number(opts.timeoutMs) : 60_000,
|
|
548
|
+
});
|
|
549
|
+
if (!raw) ui.stopSpinner();
|
|
550
|
+
let json = null;
|
|
551
|
+
try {
|
|
552
|
+
const start = result.stdout.indexOf("{");
|
|
553
|
+
const end = result.stdout.lastIndexOf("}");
|
|
554
|
+
if (start >= 0 && end > start) json = JSON.parse(result.stdout.slice(start, end + 1));
|
|
555
|
+
} catch { /* handled below */ }
|
|
556
|
+
const preview = json ? researchPreviewModel(json) : null;
|
|
557
|
+
const code = result.code !== 0 || preview?.totalEvidenceFailure ? 1 : 0;
|
|
558
|
+
if (raw) {
|
|
559
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
560
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
561
|
+
return code;
|
|
562
|
+
}
|
|
563
|
+
const renderError = (message) => {
|
|
564
|
+
const room = Math.max(28, (ui.out.columns || 80) - 2);
|
|
565
|
+
wrapWidth(message, Math.max(2, room - 2)).forEach((line, index) => {
|
|
566
|
+
ui.line(" " + (index === 0 ? ui.c.paw("✗ ") : " ") + ui.c.text(line));
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
if (json) {
|
|
570
|
+
renderResearchPreview(json, ui);
|
|
571
|
+
} else {
|
|
572
|
+
const message = (result.stderr || result.stdout || "").trim()
|
|
573
|
+
|| (ui.lang === "ko" ? "리서치 결과를 읽지 못했습니다." : "Could not read the research result.");
|
|
574
|
+
renderError(message);
|
|
575
|
+
}
|
|
576
|
+
if (result.overflow) renderError(ui.lang === "ko" ? "리서치 출력이 2MB 제한을 넘었습니다." : "Research output exceeded the 2 MB limit.");
|
|
577
|
+
if (result.timedOut) renderError(ui.lang === "ko" ? "리서치가 60초 제한을 넘었습니다." : "Research timed out after 60 seconds.");
|
|
578
|
+
return code;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// ── Hephaestus 네이티브 패스스루 — 엔진 전 기능을 터미널 1급으로 노출 ──
|
|
582
|
+
// stdio inherit 로 돌려 색/프롬프트/스트리밍이 네이티브 그대로 나온다.
|
|
583
|
+
//
|
|
584
|
+
// 주의: `context` 분기는 공용 러너에 남겨 두지만(다른 소유자의 `agentlas
|
|
585
|
+
// context` 명령이 이 러너를 호출할 수 있다), 이 클러스터의 COMMANDS에는
|
|
586
|
+
// context 명령을 노출하지 않는다 — context.cjs는 다른 에이전트 소유다.
|
|
587
|
+
function runHephaestusInteractive(args, opts = {}) {
|
|
588
|
+
if (args[0] === "route" && opts.human !== false) return runHephaestusRoute(args, opts);
|
|
589
|
+
if (args[0] === "research" && opts.human !== false) return runHephaestusResearch(args, opts);
|
|
590
|
+
const isCareerGraph = args[0] === "career-graph" || args[0] === "career_graph";
|
|
591
|
+
// `context` is a versioned Core capability, not a natural-language router
|
|
592
|
+
// request. Older launcher scripts route unknown words through Hub search,
|
|
593
|
+
// which silently turns `context verify` into a completely different action.
|
|
594
|
+
// Bypass that wrapper and invoke only a Core root that actually contains
|
|
595
|
+
// the canonical context-map implementation.
|
|
596
|
+
const isContextMap = args[0] === "context";
|
|
597
|
+
const contextRoot = isContextMap
|
|
598
|
+
? resolveCoreRuntimeRoot(
|
|
599
|
+
null,
|
|
600
|
+
[["agentlas_cloud", "context_map.py"]],
|
|
601
|
+
{ minVersion: CONTEXT_MAP_MIN_CORE_VERSION },
|
|
602
|
+
)
|
|
603
|
+
: null;
|
|
604
|
+
const contextCapable = Boolean(
|
|
605
|
+
contextRoot && fs.existsSync(path.join(contextRoot, "agentlas_cloud", "context_map.py")),
|
|
606
|
+
);
|
|
607
|
+
const found = isContextMap
|
|
608
|
+
? (contextCapable ? { kind: "python", root: contextRoot } : null)
|
|
609
|
+
: (isCareerGraph ? careerGraphRuntime() : hephaestusBin());
|
|
610
|
+
if (!found) {
|
|
611
|
+
// 런타임 부재 = 정직 정지 (v1 문구 그대로). 폴백 실행 금지.
|
|
612
|
+
process.stderr.write(
|
|
613
|
+
isContextMap
|
|
614
|
+
? "Agentlas Core context-map capability is unavailable — update Agentlas OS / Hephaestus before using `agentlas context`.\n"
|
|
615
|
+
: isCareerGraph
|
|
616
|
+
? "Career Graph 런타임이 없습니다 — 최신 Agentlas OS / Hephaestus 설치 후 다시 시도하세요.\n"
|
|
617
|
+
: "Hephaestus 런타임이 없습니다 — 데스크탑 앱 설치 또는 Hephaestus 인스톨러 실행 후 다시 시도하세요.\n",
|
|
618
|
+
);
|
|
619
|
+
if (!isContextMap) {
|
|
620
|
+
process.stderr.write(
|
|
621
|
+
isCareerGraph
|
|
622
|
+
? "설치 또는 지정: HEPHAESTUS_CAREER_GRAPH_BIN=<경로> 또는 HEPHAESTUS_RUNTIME_ROOT=<경로>\n"
|
|
623
|
+
: "설치: https://agentlas.cloud · 또는 HEPHAESTUS_BIN=<경로> 지정\n",
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
return Promise.resolve(1);
|
|
627
|
+
}
|
|
628
|
+
const helpOnly = args.some((arg) => arg === "--help" || arg === "-h" || arg === "help");
|
|
629
|
+
const cwd = opts.cwd || (helpOnly ? process.cwd() : runCwd());
|
|
630
|
+
const moduleName = found.kind === "python" && isCareerGraph ? "career_graph" : "agentlas_cloud";
|
|
631
|
+
const moduleArgs = moduleName === "career_graph" ? args.slice(1) : args;
|
|
632
|
+
const child =
|
|
633
|
+
found.kind === "bin"
|
|
634
|
+
? spawn(found.exec, isCareerGraph ? args.slice(1) : args, { cwd, stdio: "inherit" })
|
|
635
|
+
: spawnCoreModule(moduleName, moduleArgs, { cwd, stdio: "inherit" }, found.root);
|
|
636
|
+
if (!child) {
|
|
637
|
+
process.stderr.write("Hephaestus failed: Python 3.9+ was not found.\n");
|
|
638
|
+
return Promise.resolve(1);
|
|
639
|
+
}
|
|
640
|
+
return new Promise((resolve) => {
|
|
641
|
+
child.on("error", (e) => {
|
|
642
|
+
process.stderr.write(`Hephaestus failed: ${e.message}\n`);
|
|
643
|
+
resolve(1);
|
|
644
|
+
});
|
|
645
|
+
child.on("close", (code) => resolve(code == null ? 0 : code));
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const HEP_USAGE = [
|
|
650
|
+
"agentlas hep <hephaestus 서브커맨드…> — 엔진 전 기능 네이티브 패스스루",
|
|
651
|
+
"",
|
|
652
|
+
" 1급 별칭:",
|
|
653
|
+
" agentlas build \"<요청>\" 에이전트/팀 빌드·수리·패키징 (hep-build)",
|
|
654
|
+
" agentlas route \"<요청>\" 라우팅 미리보기 — 어떤 에이전트가 잡히는지",
|
|
655
|
+
" agentlas research <sub…> Research Engine (status|gather|search|read|plan…)",
|
|
656
|
+
" agentlas network <sub…> 로컬 에이전트 네트워크 (init|status|reindex|add-source…)",
|
|
657
|
+
" agentlas journal <sub…> Stormbreaker 런 저널 (status|verify|repair|gate)",
|
|
658
|
+
" agentlas call \"a,b\" \"<컨텍스트>\" 지정한 Hub/Cloud 에이전트 준비 (hep-call)",
|
|
659
|
+
"",
|
|
660
|
+
" 전체 서브커맨드: agentlas hep --help (wizard·security·package·publish·cards·ao·plugins·meta-agent…)",
|
|
661
|
+
].join("\n");
|
|
662
|
+
|
|
663
|
+
// v1 cmdHep는 process.exitCode를 세팅했다. v2 명령 계약은 exit code 반환이다.
|
|
664
|
+
async function cmdHep(args) {
|
|
665
|
+
if (!args.length || args[0] === "help") {
|
|
666
|
+
ctx.out(HEP_USAGE);
|
|
667
|
+
return 0;
|
|
668
|
+
}
|
|
669
|
+
return runHephaestusInteractive(args);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
hephaestusBin,
|
|
674
|
+
careerGraphRuntime,
|
|
675
|
+
spawnHephaestus,
|
|
676
|
+
captureHephaestus,
|
|
677
|
+
runCwd,
|
|
678
|
+
projectCwd,
|
|
679
|
+
renderRoutePreview,
|
|
680
|
+
renderResearchPreview,
|
|
681
|
+
runHephaestusRoute,
|
|
682
|
+
runHephaestusResearch,
|
|
683
|
+
runHephaestusInteractive,
|
|
684
|
+
HEP_USAGE,
|
|
685
|
+
cmdHep,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
module.exports = {
|
|
690
|
+
create,
|
|
691
|
+
routePreviewModel,
|
|
692
|
+
researchPreviewModel,
|
|
693
|
+
USAGE,
|
|
694
|
+
usageFor,
|
|
695
|
+
isHelpToken,
|
|
696
|
+
CONTEXT_MAP_MIN_CORE_VERSION,
|
|
697
|
+
};
|