agentlas 1.0.28 → 1.0.29
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 +33 -0
- package/README.md +5 -2
- package/engine/agentlas-i18n.cjs +4 -4
- package/engine/agentlas-input.cjs +0 -2
- package/engine/agentlas-onboard.cjs +20 -0
- package/engine/agentlas-workforce.cjs +41 -8
- package/engine/agentlas.cjs +7 -1
- package/engine/commands/billing.cjs +3 -0
- package/engine/commands/creds.cjs +49 -1
- package/engine/commands/doctor.cjs +46 -3
- package/engine/commands/graph.cjs +1150 -0
- package/engine/commands/help.cjs +82 -6
- package/engine/commands/hep-cloud.cjs +9 -23
- package/engine/commands/hep-hub.cjs +9 -22
- package/engine/commands/hep-local.cjs +9 -24
- package/engine/commands/hep-network.cjs +9 -35
- package/engine/commands/index.cjs +49 -25
- package/engine/commands/mcp.cjs +6 -2
- package/engine/commands/native.cjs +18 -2
- package/engine/commands/plugin.cjs +22 -0
- package/engine/commands/roles.cjs +202 -0
- package/engine/commands/workforce.cjs +63 -12
- package/engine/graph/ask-model.cjs +131 -0
- package/engine/graph/interview.cjs +875 -0
- package/engine/graph/layout.cjs +137 -0
- package/engine/graph/package.cjs +223 -0
- package/engine/graph/vocabulary.generated.cjs +30 -0
- package/engine/hephaestus/local-core.cjs +159 -0
- package/engine/hephaestus/runtime.cjs +4 -8
- package/engine/runtimes/auth-evidence.cjs +78 -0
- package/engine/sessions/prompt.cjs +16 -0
- package/engine/sessions/session.cjs +9 -0
- package/engine/tools/access-notice.cjs +86 -0
- package/engine/ui/palette.cjs +6 -3
- package/engine/ui/repl.cjs +8 -2
- package/engine/workforce/deps.cjs +13 -0
- package/engine/workforce/local-core-transport.cjs +298 -0
- package/package.json +4 -3
- package/engine/commands/legacy-network.cjs +0 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.29 — 2026-08-05
|
|
4
|
+
|
|
5
|
+
Terminal-wide audit release. Every command was executed for real; what follows fixes what that audit found.
|
|
6
|
+
|
|
7
|
+
- Native federated staffing: `hep-network` / `hep-local` / `hep-cloud` / `hep-hub` now run this
|
|
8
|
+
terminal's own workforce loop with the local Agentlas-OS core federating the declared source scope.
|
|
9
|
+
(They previously passed through to an external CLI stub that always answered exit 3.)
|
|
10
|
+
Honest stop with install guidance when the local core is missing — no silent fallback to the public Hub.
|
|
11
|
+
- `workforce` now declares its sourceScope ("hub") explicitly instead of relying on the server default.
|
|
12
|
+
- `doctor` distinguishes installed from signed-in: local sign-in evidence per runtime, a warning
|
|
13
|
+
(not "all clear") when the active runtime has none.
|
|
14
|
+
- New `roles` command: view and set orchestrator/worker model roles from the terminal
|
|
15
|
+
(`roles set <role> <runtime> [--model id] [--effort lv]`, `roles set worker --inherit`).
|
|
16
|
+
REPL `/model`·`/runtime` now say they are session-scoped and point to `agentlas roles`.
|
|
17
|
+
- Setup wizard prints install/sign-in guidance when the chosen runtime is missing or unauthenticated.
|
|
18
|
+
- `creds list` (names and stores only — values never printed), `mcp list`, Korean `help` body.
|
|
19
|
+
- The graph command group ships in the npm package for the first time (it landed after 1.0.28 was published).
|
|
20
|
+
- New gates: user-scenarios (93 real CLI invocations), local-core transport wire contract,
|
|
21
|
+
doctor auth-evidence, roles round-trip, onboarding guidance, EN/KO help sync.
|
|
22
|
+
- `agentlas <command> --help` reaches the command's own help. It printed a two-line stub
|
|
23
|
+
scraped from the help table, so `graph --help` never showed the eight lines `graph help` has —
|
|
24
|
+
and that is the spelling people try first. (The gate had pinned the stub as correct; its
|
|
25
|
+
contract now asks whether real help was shown.)
|
|
26
|
+
- `graph install --name "<new name>"` works. The documented flag had never worked: its value was
|
|
27
|
+
appended to the file path, so the install died with "no such file".
|
|
28
|
+
- `graph show` stops indenting a straight chain deeper at every step — fourteen steps meant
|
|
29
|
+
twenty-eight columns. Depth now marks real branches (a fork, a failure exit).
|
|
30
|
+
- Building a graph follows your language setting. One Korean character anywhere in the request
|
|
31
|
+
forced the interview to Korean while `graph show`, the list, and errors stayed English.
|
|
32
|
+
- Graph steps written as code declare the pip packages they import, and a verification step is
|
|
33
|
+
now required whenever a step that changes something outside sends out a value an earlier step
|
|
34
|
+
computed — an unattended run must not ship an empty or invented result.
|
|
35
|
+
|
|
3
36
|
## 1.0.26 — 2026-08-03
|
|
4
37
|
|
|
5
38
|
- **`agentlas list` stops letting the terminal cut its own output.** Slugs were
|
package/README.md
CHANGED
|
@@ -94,10 +94,13 @@ agentlas variant resolve --base-release <id> # Preview local variant compatibili
|
|
|
94
94
|
```sh
|
|
95
95
|
agentlas storm "<goal>" # Goal + UltraCode harness: plan -> assign -> execute -> verify [--research]
|
|
96
96
|
agentlas swarm "<goal>" # Emergent multi-agent swarm [--parallel N]
|
|
97
|
-
agentlas workforce "<prompt>" # Route through Agent Workforce Ontology
|
|
97
|
+
agentlas workforce "<prompt>" # Route through Agent Workforce Ontology (public Hub menu)
|
|
98
98
|
agentlas network "<prompt>" # Alias for workforce
|
|
99
99
|
agentlas taskforce "<prompt>" # Alias for workforce
|
|
100
|
-
agentlas
|
|
100
|
+
agentlas hep-network "<prompt>" # Staff across Local + owner Cloud + public Hub (local Core federation)
|
|
101
|
+
agentlas hep-local "<prompt>" # Same staffing, registered Local agents only
|
|
102
|
+
agentlas hep-cloud "<prompt>" # Same staffing, owner Agent Cloud only
|
|
103
|
+
agentlas hep-hub "<prompt>" # Same staffing, public Hub only
|
|
101
104
|
agentlas call "a,b" "<context>" # Call explicitly named Hub or Cloud agents
|
|
102
105
|
agentlas browser [...] # Real browser hardpoint launcher
|
|
103
106
|
agentlas route "<prompt>" [--json] # Preview routing decisions without execution
|
package/engine/agentlas-i18n.cjs
CHANGED
|
@@ -74,7 +74,6 @@ const STRINGS = {
|
|
|
74
74
|
"effortUsage": "usage: /effort low|medium|high|max|off|auto",
|
|
75
75
|
"permSet": "permission → %s",
|
|
76
76
|
"permUsage": "usage: /permission read|write|full",
|
|
77
|
-
"legacyNetworkUsage": "usage: /legacy-network <request>",
|
|
78
77
|
"stormUsage": "usage: /storm <goal> [--research]",
|
|
79
78
|
"buildUsage": "usage: /build <agent-or-team request>",
|
|
80
79
|
"routeUsage": "usage: /route <request> [--json]",
|
|
@@ -190,7 +189,6 @@ const STRINGS = {
|
|
|
190
189
|
"help.search": "discover agents in the Hub",
|
|
191
190
|
"help.install": "install an agent from the Hub by slug",
|
|
192
191
|
"help.network": "let the host LLM staff and execute an ontology-grounded exact-release task force",
|
|
193
|
-
"help.legacyNetwork": "run the explicit compatibility-only Hephaestus network route",
|
|
194
192
|
"help.browser": "real browser execution hardpoint",
|
|
195
193
|
"help.connect": "wire Telegram / platforms to an agent team",
|
|
196
194
|
"help.swarm": "fan out an emergent agent swarm on a goal",
|
|
@@ -251,6 +249,8 @@ const STRINGS = {
|
|
|
251
249
|
"wiz.runtimeAuto": "Use the app's active runtime",
|
|
252
250
|
"wiz.runtimeInstalled": "installed",
|
|
253
251
|
"wiz.runtimeMissing": "not installed",
|
|
252
|
+
"wiz.runtimeInstallHint": "This runtime is not installed yet. Install it first: %s",
|
|
253
|
+
"wiz.runtimeLoginHint": "No local sign-in evidence for this runtime. Sign in before your first run (run: %s, then follow its login flow).",
|
|
254
254
|
"wiz.permQ": "How much should agents be allowed to do by default?",
|
|
255
255
|
"wiz.permRead": "read — inspect only; workspace writes blocked",
|
|
256
256
|
"wiz.permWrite": "write — workspace + runtime temp writes; external MCP off (recommended)",
|
|
@@ -323,7 +323,6 @@ const STRINGS = {
|
|
|
323
323
|
"effortUsage": "사용법: /effort low|medium|high|max|off|auto",
|
|
324
324
|
"permSet": "권한 → %s",
|
|
325
325
|
"permUsage": "사용법: /permission read|write|full",
|
|
326
|
-
"legacyNetworkUsage": "사용법: /legacy-network <요청>",
|
|
327
326
|
"stormUsage": "사용법: /storm <목표> [--research]",
|
|
328
327
|
"buildUsage": "사용법: /build <만들 에이전트·팀 설명>",
|
|
329
328
|
"routeUsage": "사용법: /route <요청> [--json]",
|
|
@@ -438,7 +437,6 @@ const STRINGS = {
|
|
|
438
437
|
"help.search": "Hub에서 에이전트 발견",
|
|
439
438
|
"help.install": "식별자로 Hub 에이전트 설치",
|
|
440
439
|
"help.network": "상위 LLM이 온톨로지에서 정확한 릴리스 TF를 선발·실행",
|
|
441
|
-
"help.legacyNetwork": "호환성 전용 Hephaestus 네트워크 경로를 명시적으로 실행",
|
|
442
440
|
"help.browser": "실제 브라우저 실행 하드포인트",
|
|
443
441
|
"help.connect": "Telegram/플랫폼을 에이전트 팀에 연결",
|
|
444
442
|
"help.swarm": "목표에 창발형 에이전트 스웜 전개",
|
|
@@ -497,6 +495,8 @@ const STRINGS = {
|
|
|
497
495
|
"wiz.runtimeAuto": "앱의 활성 런타임 사용",
|
|
498
496
|
"wiz.runtimeInstalled": "설치됨",
|
|
499
497
|
"wiz.runtimeMissing": "미설치",
|
|
498
|
+
"wiz.runtimeInstallHint": "이 런타임은 아직 설치되어 있지 않습니다. 먼저 설치하세요: %s",
|
|
499
|
+
"wiz.runtimeLoginHint": "이 런타임의 로그인 흔적이 없습니다. 첫 실행 전에 로그인하세요 (%s 실행 후 로그인 절차 진행).",
|
|
500
500
|
"wiz.permQ": "에이전트가 기본적으로 어디까지 할 수 있게 할까요?",
|
|
501
501
|
"wiz.permRead": "read — 조회만; 작업 공간 쓰기 차단",
|
|
502
502
|
"wiz.permWrite": "write — 작업 공간·런타임 임시 경로 쓰기; 외부 MCP 꺼짐 (권장)",
|
|
@@ -300,7 +300,6 @@ const SLASH_COMMAND_META = [
|
|
|
300
300
|
{ command: "/search", description: "Discover agents in the Hub", category: "Hub", usage: "/search <what you need>", detail: "Search the Agentlas Hub + local for an agent that fits the task (hep-search)." },
|
|
301
301
|
{ command: "/install", description: "Install an agent from the Hub by slug", category: "Hub", usage: "/install <slug>", detail: "Install a marketplace agent into this terminal." },
|
|
302
302
|
{ command: "/network", description: "Staff and execute an ontology-grounded task force", category: "Engine", usage: "/network <request> [--benchmark]", detail: "The active top host LLM creates the work order, searches the Hub workforce ontology, selects exact releases, and executes a receipt-backed task force.", aliases: ["/taskforce", "/workforce"] },
|
|
303
|
-
{ command: "/legacy-network", description: "Run the compatibility Hephaestus network route", category: "Engine", usage: "/legacy-network <request>", detail: "Explicit compatibility escape hatch; never used as a fallback by /network." },
|
|
304
303
|
{ command: "/browser", description: "Real browser execution hardpoint", category: "Engine", usage: "/browser [sub]", detail: "Runs the Agentlas browser hardpoint (hep-browser)." },
|
|
305
304
|
{ command: "/connect", description: "Wire Telegram / platforms to an agent team", category: "Hub", usage: "/connect", detail: "Runs Hephaestus hep-connect for platform integration." },
|
|
306
305
|
{ command: "/doctor", description: "Check runtimes and local data", category: "Health", usage: "/doctor", detail: "Run local checks for runtimes, data, credentials, and setup." },
|
|
@@ -351,7 +350,6 @@ const HELP_KEY_BY_COMMAND = {
|
|
|
351
350
|
"/research": "help.research",
|
|
352
351
|
"/search": "help.search",
|
|
353
352
|
"/network": "help.network",
|
|
354
|
-
"/legacy-network": "help.legacyNetwork",
|
|
355
353
|
"/browser": "help.browser",
|
|
356
354
|
"/connect": "help.connect",
|
|
357
355
|
"/doctor": "help.doctor",
|
|
@@ -142,6 +142,26 @@ async function runOnboard({ ui, rl, helpers, persist }) {
|
|
|
142
142
|
rtOpts.forEach((o, i) => optionLine(i + 1, o.label, i === 0));
|
|
143
143
|
const ri = await pickNum(rtOpts.length);
|
|
144
144
|
const runtime = rtOpts[ri - 1].value;
|
|
145
|
+
// missing인 런타임을 골라도 저장은 한다(미리 설정) — 하지만 조용히 저장하면
|
|
146
|
+
// 첫 실행 실패가 배신이 된다(2026-08-05 감사 결함 A). 설치 명령을 그 자리에서
|
|
147
|
+
// 알려주고, 로그인 흔적이 없으면 로그인 절차까지 말한다.
|
|
148
|
+
if (runtime !== "auto") {
|
|
149
|
+
const installHint = {
|
|
150
|
+
"claude-code": "npm i -g @anthropic-ai/claude-code",
|
|
151
|
+
codex: "npm i -g @openai/codex",
|
|
152
|
+
gemini: "npm i -g @google/gemini-cli",
|
|
153
|
+
}[runtime];
|
|
154
|
+
if (!H.which(H.RUNTIME_BIN[runtime])) {
|
|
155
|
+
if (installHint) printIndented(ui.t("wiz.runtimeInstallHint", installHint), c.dim);
|
|
156
|
+
} else {
|
|
157
|
+
try {
|
|
158
|
+
const { runtimeAuthEvidence } = require("./runtimes/auth-evidence.cjs");
|
|
159
|
+
if (runtimeAuthEvidence(runtime).status === "none") {
|
|
160
|
+
printIndented(ui.t("wiz.runtimeLoginHint", H.RUNTIME_BIN[runtime]), c.dim);
|
|
161
|
+
}
|
|
162
|
+
} catch { /* evidence unavailable — say nothing rather than guess */ }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
145
165
|
|
|
146
166
|
// Step 3 — default permission
|
|
147
167
|
ui.line("");
|
|
@@ -1097,10 +1097,15 @@ function validateWorkOrder(value) {
|
|
|
1097
1097
|
function validateCandidateSet(value, workOrder, now = new Date(), options = {}) {
|
|
1098
1098
|
const set = assertObject(value, "candidateSet");
|
|
1099
1099
|
assertNoForbiddenFitSignals(set);
|
|
1100
|
-
|
|
1100
|
+
// projection은 로컬 Core(연합) 응답에만 있는 메뉴 투영 메타데이터다(실측
|
|
1101
|
+
// 2026-08-05, reference-first: fullDossier=false). 원격 서버는 보내지 않는다.
|
|
1102
|
+
// 없애고 되돌려 보내면 Core 쪽 다이제스트 대조가 위험하므로 선택 키로 허용한다.
|
|
1103
|
+
const exactKeys = [
|
|
1101
1104
|
"schemaVersion", "selectionSessionId", "workOrderId", "ontologyVersion",
|
|
1102
1105
|
"candidateSetDigest", "decisionOwner", "historyInfluence", "slots", "issuedAt", "expiresAt",
|
|
1103
|
-
]
|
|
1106
|
+
];
|
|
1107
|
+
if (Object.prototype.hasOwnProperty.call(set, "projection")) exactKeys.push("projection");
|
|
1108
|
+
assertExactKeys(set, exactKeys, "candidateSet", "candidate_set_invalid");
|
|
1104
1109
|
if (set.schemaVersion !== "agentlas.workforce-candidate-set.v1") fail("candidate_set_invalid", "unsupported candidate set schema");
|
|
1105
1110
|
assertId(set.selectionSessionId, "candidateSet.selectionSessionId");
|
|
1106
1111
|
if (set.workOrderId !== workOrder.workOrderId) fail("candidate_set_invalid", "candidate set workOrderId mismatch");
|
|
@@ -1126,11 +1131,15 @@ function validateCandidateSet(value, workOrder, now = new Date(), options = {})
|
|
|
1126
1131
|
const releases = new Set();
|
|
1127
1132
|
for (const candidate of assertArray(slotResult.candidates, `candidateSet.${slotId}.candidates`, 100)) {
|
|
1128
1133
|
assertObject(candidate, "candidate");
|
|
1129
|
-
|
|
1134
|
+
// missingMandatory는 로컬 Core(연합) 응답에만 있는 미충족 필수 표식이다
|
|
1135
|
+
// (실측 2026-08-05, fullDossier=true에도 동봉). 원격 서버는 보내지 않는다.
|
|
1136
|
+
const candidateKeys = [
|
|
1130
1137
|
"agentDefinitionId", "agentReleaseId", "releaseVersion", "packageHash", "contentDigest",
|
|
1131
1138
|
"entityKind", "name", "communities", "fitEvidence", "qualificationEvidence", "optionalGaps",
|
|
1132
1139
|
"semanticSnapshot", "operational",
|
|
1133
|
-
]
|
|
1140
|
+
];
|
|
1141
|
+
if (Object.prototype.hasOwnProperty.call(candidate, "missingMandatory")) candidateKeys.push("missingMandatory");
|
|
1142
|
+
assertExactKeys(candidate, candidateKeys, "candidate", "candidate_set_invalid");
|
|
1134
1143
|
assertId(candidate.agentDefinitionId, "candidate.agentDefinitionId");
|
|
1135
1144
|
const releaseId = assertId(candidate.agentReleaseId, "candidate.agentReleaseId");
|
|
1136
1145
|
if (releases.has(releaseId)) fail("candidate_set_invalid", `duplicate release ${releaseId} in ${slotId}`);
|
|
@@ -1151,10 +1160,19 @@ function validateCandidateSet(value, workOrder, now = new Date(), options = {})
|
|
|
1151
1160
|
if (typeof operational.callable !== "boolean" || typeof operational.installable !== "boolean") fail("candidate_set_invalid", "candidate operational flags are invalid");
|
|
1152
1161
|
assertIds(operational.unavailableReasons || [], "candidate.operational.unavailableReasons");
|
|
1153
1162
|
const semantic = assertObject(candidate.semanticSnapshot, "candidate.semanticSnapshot");
|
|
1154
|
-
|
|
1163
|
+
// knowledge·modalities는 로컬 Core 스냅샷에만 있는 확장 어휘다(실측 2026-08-05).
|
|
1164
|
+
// 원격 서버는 보내지 않는다 — missingMandatory·projection과 같은 규칙으로
|
|
1165
|
+
// "있으면 검증하고 허용", 원격 계약의 exact-keys는 그대로 둔다.
|
|
1166
|
+
const semanticKeys = [
|
|
1155
1167
|
"summaries", "roles", "skills", "toolCapabilities", "consumes", "produces",
|
|
1156
1168
|
"authorities", "runtimes", "languages",
|
|
1157
|
-
]
|
|
1169
|
+
];
|
|
1170
|
+
for (const optional of ["knowledge", "modalities"]) {
|
|
1171
|
+
if (Object.prototype.hasOwnProperty.call(semantic, optional)) semanticKeys.push(optional);
|
|
1172
|
+
}
|
|
1173
|
+
assertExactKeys(semantic, semanticKeys, "candidate.semanticSnapshot", "candidate_set_invalid");
|
|
1174
|
+
if (semantic.knowledge !== undefined) assertIds(semantic.knowledge, "candidate.semanticSnapshot.knowledge");
|
|
1175
|
+
if (semantic.modalities !== undefined) assertStrings(semantic.modalities, "candidate.semanticSnapshot.modalities");
|
|
1158
1176
|
assertStrings(semantic.summaries, "candidate.semanticSnapshot.summaries");
|
|
1159
1177
|
assertIds(semantic.roles, "candidate.semanticSnapshot.roles");
|
|
1160
1178
|
assertLeveledConcepts(semantic.skills, "candidate.semanticSnapshot.skills");
|
|
@@ -2393,6 +2411,16 @@ function create(deps = {}) {
|
|
|
2393
2411
|
|
|
2394
2412
|
async function workforceRun(db, rawTask, ctx = {}) {
|
|
2395
2413
|
const task = assertString(rawTask, "task", 20_000);
|
|
2414
|
+
// 이 표면이 보는 후보 메뉴의 소스 스코프. 원격 MCP 경로의 정직한 기본은 "hub"
|
|
2415
|
+
// (터미널 workforce = 공개 Hub 메뉴 스태핑). 다른 값은 로컬 Core 전송을 가진
|
|
2416
|
+
// 호출자만 넘길 수 있다 — 여기서 조용히 넓히지 않는다.
|
|
2417
|
+
const sourceScope = (() => {
|
|
2418
|
+
const value = ctx.sourceScope === undefined ? "hub" : ctx.sourceScope;
|
|
2419
|
+
if (!["network", "local", "cloud", "hub"].includes(value)) {
|
|
2420
|
+
fail("source_scope_invalid", `sourceScope must be network|local|cloud|hub, got: ${String(value)}`);
|
|
2421
|
+
}
|
|
2422
|
+
return value;
|
|
2423
|
+
})();
|
|
2396
2424
|
const ui = ctx.ui || newUi();
|
|
2397
2425
|
const runtime = ctx.runtime || D.resolveRuntime(db, ctx.runtimeOverride);
|
|
2398
2426
|
const cwd = ctx.cwd || (typeof D.projectCwd === "function" ? D.projectCwd() : process.cwd());
|
|
@@ -2744,7 +2772,9 @@ function create(deps = {}) {
|
|
|
2744
2772
|
};
|
|
2745
2773
|
|
|
2746
2774
|
const supersedeCandidateSearch = (workOrder, refinementNumber, triggerKind) => {
|
|
2747
|
-
|
|
2775
|
+
// hubStage가 저장한 requestDigest와 같은 인자 모양이어야 행을 찾는다 —
|
|
2776
|
+
// search 인자에 sourceScope가 실리므로(2026-08-05) 여기서도 함께 계산한다.
|
|
2777
|
+
const requestDigest = sha256({ workOrder, sourceScope });
|
|
2748
2778
|
for (const row of receipt.hubTools) {
|
|
2749
2779
|
if (row.tool !== "workforce.search_candidates" || row.requestDigest !== requestDigest || row.authoritativeChain !== true) continue;
|
|
2750
2780
|
row.authoritativeChain = false;
|
|
@@ -3065,7 +3095,10 @@ function create(deps = {}) {
|
|
|
3065
3095
|
|
|
3066
3096
|
let refinementsUsed = 0;
|
|
3067
3097
|
const searchCurrentWorkOrder = async () => {
|
|
3068
|
-
|
|
3098
|
+
// sourceScope는 MCP 스키마상 required다. 예전에는 싣지 않아 서버 기본값
|
|
3099
|
+
// ("hub")에 의존했다 — 기본값이 바뀌면 이 표면의 실제 스코프가 조용히
|
|
3100
|
+
// 넓어지거나 좁아진다. 이 표면이 보는 메뉴를 스스로 선언한다.
|
|
3101
|
+
const candidateRaw = await hubStage("workforce.search_candidates", { workOrder, sourceScope });
|
|
3069
3102
|
candidateSet = validateCandidateSet(
|
|
3070
3103
|
candidateRaw,
|
|
3071
3104
|
workOrder,
|
package/engine/agentlas.cjs
CHANGED
|
@@ -107,7 +107,13 @@ function main() {
|
|
|
107
107
|
if (helpRequested && helpCommand) {
|
|
108
108
|
const ctx = buildCtx();
|
|
109
109
|
const command = commands.resolveCommandName(helpCommand);
|
|
110
|
-
|
|
110
|
+
// ★명령이 자기 도움말을 갖고 있으면 그것을 보여준다.
|
|
111
|
+
// 예전에는 무조건 표 한 줄을 긁어(`runForCommand`) "Usage: agentlas graph [options]"
|
|
112
|
+
// 두 줄만 나왔다 — `graph help`에는 8줄짜리 제대로 된 안내가 있는데도 `--help`로는
|
|
113
|
+
// 영원히 닿지 못했다(사용자가 가장 먼저 치는 것이 `--help`다).
|
|
114
|
+
const code = commands.SELF_HELP_COMMANDS.has(command)
|
|
115
|
+
? commands.dispatch(ctx, [command, "help"])
|
|
116
|
+
: require("./commands/help.cjs").runForCommand(ctx, command);
|
|
111
117
|
process.exit(typeof code === "number" ? code : 0);
|
|
112
118
|
}
|
|
113
119
|
// 옵션 정규화: -h/--help/-V/--version 은 하위 명령으로 변환
|
|
@@ -25,6 +25,9 @@ function usage(ko) {
|
|
|
25
25
|
ko
|
|
26
26
|
? " 구독 계좌(A)와 렌트수익 계좌(B) 잔액을 표시합니다."
|
|
27
27
|
: " Shows the subscription account (A) and rental-earnings account (B) balances.",
|
|
28
|
+
ko
|
|
29
|
+
? " 크레딧은 Hub 에이전트 호출(공개 에이전트 3·팀 10, 활성 리스는 0)에 쓰입니다."
|
|
30
|
+
: " Credits pay for Hub agent calls (public agent 3 · team 10; active leases cost 0).",
|
|
28
31
|
ko
|
|
29
32
|
? " 참고: 렌트수익(B) → 구독(A) 전송은 Agentlas Desktop 에서만 가능합니다 (터미널 전송 명령 없음)."
|
|
30
33
|
: " Note: earnings (B) → subscription (A) transfer is Desktop-only (no transfer command in the terminal).",
|
|
@@ -134,12 +134,60 @@ async function cmdCredsFile(ctx, args) {
|
|
|
134
134
|
return 0;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/** KEY=value 파일에서 KEY 이름만 뽑는다 — 값은 절대 읽어 돌려주지 않는다. */
|
|
138
|
+
function envKeyNames(filePath) {
|
|
139
|
+
try {
|
|
140
|
+
return fs.readFileSync(filePath, "utf8")
|
|
141
|
+
.split(/\r?\n/)
|
|
142
|
+
.map((line) => line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/))
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
.map((m) => m[1]);
|
|
145
|
+
} catch {
|
|
146
|
+
return null; // 파일 없음/읽기 불가 — 목록에서 침묵이 아니라 부재로 표시
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* creds list — 어떤 자격증명 "이름"이 어디에 저장돼 있는지.
|
|
152
|
+
* 배경(2026-08-05 감사, 결함 H): save/file만 있어 저장한 것을 확인할 방법이
|
|
153
|
+
* 없었다. 계약은 상단과 동일 — 값은 어떤 경로로도 출력하지 않는다. 키 이름과
|
|
154
|
+
* 저장 위치까지만. (키체인 vault는 standalone에서 keytar를 부르면 무한 대기하는
|
|
155
|
+
* v1 실사고가 있어 조회조차 하지 않는다 — 부재가 아니라 "미조회"로 표시.)
|
|
156
|
+
*/
|
|
157
|
+
function cmdCredsList(ctx) {
|
|
158
|
+
const ko = ctx.lang !== "en";
|
|
159
|
+
const project = activeProjectPath(ctx.db());
|
|
160
|
+
const stores = [];
|
|
161
|
+
if (project) stores.push({ label: (ko ? "프로젝트 .env — " : "project .env — ") + path.join(project, ".env"), keys: envKeyNames(path.join(project, ".env")) });
|
|
162
|
+
stores.push({ label: (ko ? "전역 credentials.env — " : "global credentials.env — ") + path.join(userDataDir(), "credentials.env"), keys: envKeyNames(path.join(userDataDir(), "credentials.env")) });
|
|
163
|
+
stores.push({ label: "~/.agentlas/credentials.env", keys: envKeyNames(path.join(os.homedir(), ".agentlas", "credentials.env")) });
|
|
164
|
+
|
|
165
|
+
let any = false;
|
|
166
|
+
for (const store of stores) {
|
|
167
|
+
if (store.keys === null) continue; // 파일 자체가 없으면 줄을 만들지 않는다
|
|
168
|
+
any = true;
|
|
169
|
+
ctx.out(ctx.ui.bold(store.label));
|
|
170
|
+
if (!store.keys.length) ctx.out(ctx.ui.dim(ko ? " (비어 있음)" : " (empty)"));
|
|
171
|
+
for (const key of store.keys) ctx.out(` ${key}${ctx.ui.dim("=•••")}`);
|
|
172
|
+
}
|
|
173
|
+
if (isElectronRuntime()) {
|
|
174
|
+
ctx.out(ctx.ui.dim(ko ? "키체인 vault: 데스크탑에서 확인" : "keychain vault: inspect in Desktop"));
|
|
175
|
+
} else {
|
|
176
|
+
ctx.out(ctx.ui.dim(ko
|
|
177
|
+
? "키체인 vault는 standalone 터미널에서 조회하지 않습니다(서명 없는 keytar 호출은 키체인에 막혀 멈춥니다)."
|
|
178
|
+
: "The keychain vault is not queried from the standalone terminal (unsigned keytar calls hang on the OS keychain)."));
|
|
179
|
+
}
|
|
180
|
+
if (!any) ctx.out(ko ? "저장된 자격증명 파일이 없습니다. agentlas creds save 로 추가하세요." : "No credential files yet. Add one with: agentlas creds save");
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
137
184
|
async function run(ctx, args) {
|
|
138
185
|
const fail = (msg) => { ctx.err(msg); return 1; };
|
|
139
186
|
const sub = args[0];
|
|
140
187
|
if (sub === "file") return cmdCredsFile(ctx, args.slice(1));
|
|
188
|
+
if (sub === "list" || sub === "ls") return cmdCredsList(ctx);
|
|
141
189
|
if (sub !== "save") {
|
|
142
|
-
return fail('usage: agentlas creds save --provider <name> --key <ENV_NAME> --value <value> [--project <path>] OR agentlas creds file --source <path> [--env <ENV_NAME>]');
|
|
190
|
+
return fail('usage: agentlas creds save --provider <name> --key <ENV_NAME> --value <value> [--project <path>] OR agentlas creds file --source <path> [--env <ENV_NAME>] OR agentlas creds list');
|
|
143
191
|
}
|
|
144
192
|
const f = parseCredFlags(args.slice(1));
|
|
145
193
|
const key = typeof f.key === "string" ? f.key.trim() : "";
|
|
@@ -9,6 +9,7 @@ const fs = require("node:fs");
|
|
|
9
9
|
const path = require("node:path");
|
|
10
10
|
const { dbPath, userDataDir } = require("../core/paths.cjs");
|
|
11
11
|
const { listAvailableCliRuntimes, activeRuntimeRow } = require("../runtimes/detect.cjs");
|
|
12
|
+
const { runtimeAuthEvidence } = require("../runtimes/auth-evidence.cjs");
|
|
12
13
|
const { resolvedModelRole } = require("../runtimes/roles.cjs");
|
|
13
14
|
|
|
14
15
|
function roleDetail(selection, role, en) {
|
|
@@ -26,8 +27,10 @@ function roleDetail(selection, role, en) {
|
|
|
26
27
|
function run(ctx) {
|
|
27
28
|
const en = ctx.lang === "en";
|
|
28
29
|
let failures = 0;
|
|
30
|
+
let warnings = 0;
|
|
29
31
|
const ok = (label, detail) => ctx.out(` ${ctx.ui.green("✓")} ${label}${detail ? ctx.ui.dim(" — " + detail) : ""}`);
|
|
30
32
|
const bad = (label, detail) => { failures += 1; ctx.out(` ${ctx.ui.red("✗")} ${label}${detail ? ctx.ui.dim(" — " + detail) : ""}`); };
|
|
33
|
+
const warn = (label, detail) => { warnings += 1; ctx.out(` ${ctx.ui.yellow ? ctx.ui.yellow("!") : "!"} ${label}${detail ? ctx.ui.dim(" — " + detail) : ""}`); };
|
|
31
34
|
|
|
32
35
|
// 1) 데이터
|
|
33
36
|
const p = dbPath();
|
|
@@ -43,10 +46,28 @@ function run(ctx) {
|
|
|
43
46
|
bad(en ? "database" : "데이터베이스", (en ? "missing: " : "없음: ") + p);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
// 2) 런타임
|
|
49
|
+
// 2) 런타임 — 설치 여부와 인증 흔적은 다른 축이다. which()만 보면 로그아웃
|
|
50
|
+
// 상태에서도 all clear가 나간다(2026-08-05 실측). 각 CLI가 로그인 시 남기는
|
|
51
|
+
// 로컬 산출물을 증거로 관측하고, 증거≠증명이므로 문구도 단정하지 않는다.
|
|
47
52
|
const clis = listAvailableCliRuntimes();
|
|
48
53
|
if (clis.length) {
|
|
49
|
-
|
|
54
|
+
const evidences = clis.map((c) => ({ kind: c.kind, evidence: runtimeAuthEvidence(c.kind) }));
|
|
55
|
+
const annotated = evidences.map(({ kind, evidence }) => {
|
|
56
|
+
if (evidence.status === "none") return `${kind}(${en ? "no sign-in evidence" : "로그인 흔적 없음"})`;
|
|
57
|
+
return kind; // evidence 또는 unknown — 검사법이 없는 런타임을 미로그인으로 표시하지 않는다
|
|
58
|
+
});
|
|
59
|
+
ok(en ? "runtimes" : "런타임", annotated.join(", "));
|
|
60
|
+
// 신규 설치 상태: 검사 가능한 런타임 전부가 흔적 없음이면 첫 실행이 거의
|
|
61
|
+
// 확실히 실패한다 — 활성 런타임 행이 아직 없어도 여기서 경고한다.
|
|
62
|
+
const checkable = evidences.filter(({ evidence }) => evidence.status !== "unknown");
|
|
63
|
+
if (checkable.length && checkable.every(({ evidence }) => evidence.status === "none")) {
|
|
64
|
+
warn(
|
|
65
|
+
en ? "runtime sign-in" : "런타임 로그인",
|
|
66
|
+
en
|
|
67
|
+
? "no detected runtime has local sign-in evidence — sign in to one before running (e.g. claude / codex login / gemini)"
|
|
68
|
+
: "감지된 어떤 런타임에도 로그인 흔적이 없습니다 — 실행 전에 하나는 로그인하세요 (예: claude / codex login / gemini)",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
50
71
|
} else {
|
|
51
72
|
bad(en ? "runtimes" : "런타임", en
|
|
52
73
|
? "no agent CLI on PATH (claude / codex / gemini / kimi / grok / cursor-agent)"
|
|
@@ -57,7 +78,23 @@ function run(ctx) {
|
|
|
57
78
|
try {
|
|
58
79
|
const db = ctx.db();
|
|
59
80
|
const active = activeRuntimeRow(db);
|
|
60
|
-
if (active)
|
|
81
|
+
if (active) {
|
|
82
|
+
const detail = `${active.kind}${active.model ? ` (${active.model})` : ""}`;
|
|
83
|
+
// 활성 런타임은 모든 실행이 지나는 문이다 — 로그인 흔적이 없으면 all clear
|
|
84
|
+
// 가 아니라 경고다. 흔적 없음 = 미로그인 "가능성"이므로 단정하지 않는다.
|
|
85
|
+
const evidence = runtimeAuthEvidence(active.kind);
|
|
86
|
+
if (evidence.status === "none") {
|
|
87
|
+
const bin = { "claude-code": "claude", codex: "codex", gemini: "gemini" }[active.kind] || active.kind;
|
|
88
|
+
warn(
|
|
89
|
+
en ? "active runtime" : "활성 런타임",
|
|
90
|
+
en
|
|
91
|
+
? `${detail} — no local sign-in evidence; runs may fail until you sign in (try: ${bin} login)`
|
|
92
|
+
: `${detail} — 로그인 흔적이 없습니다. 로그인 전에는 실행이 실패할 수 있습니다 (시도: ${bin} login)`,
|
|
93
|
+
);
|
|
94
|
+
} else {
|
|
95
|
+
ok(en ? "active runtime" : "활성 런타임", detail);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
61
98
|
const orchestrator = resolvedModelRole(db, "orchestrator");
|
|
62
99
|
const worker = resolvedModelRole(db, "worker");
|
|
63
100
|
if (orchestrator && worker) {
|
|
@@ -83,6 +120,12 @@ function run(ctx) {
|
|
|
83
120
|
ctx.out(en ? `doctor: ${failures} problem(s) found` : `doctor: 문제 ${failures}건`);
|
|
84
121
|
return 1;
|
|
85
122
|
}
|
|
123
|
+
if (warnings) {
|
|
124
|
+
// 경고가 있으면 "이상 없음"이라고 말하지 않는다 — 그 문구가 첫 실행 실패를
|
|
125
|
+
// 배신으로 만든다. 경고는 실행을 막지 않으므로 exit 0.
|
|
126
|
+
ctx.out(en ? `doctor: ${warnings} warning(s) — runnable, but check the lines above` : `doctor: 경고 ${warnings}건 — 실행은 가능하나 위 항목을 확인하세요`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
86
129
|
ctx.out(en ? "doctor: all clear" : "doctor: 이상 없음");
|
|
87
130
|
return 0;
|
|
88
131
|
}
|