agentlas 1.0.65 → 1.0.66
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 +78 -0
- package/README.md +6 -4
- package/bin/agentlas.cjs +341 -16
- package/engine/acp/server.cjs +27 -1
- package/engine/agentlas-api-agent.cjs +151 -120
- package/engine/agentlas-banner.cjs +6 -2
- package/engine/agentlas-capabilities.cjs +17 -10
- package/engine/agentlas-cloud-runtime.cjs +4 -5
- package/engine/agentlas-config.cjs +19 -54
- package/engine/agentlas-core-harness.cjs +59 -5
- package/engine/agentlas-desktop-loadout.cjs +17 -3
- package/engine/agentlas-evolution.cjs +75 -18
- package/engine/agentlas-experience-exchange.cjs +145 -14
- package/engine/agentlas-experience-intake.cjs +2 -2
- package/engine/agentlas-i18n.cjs +0 -2
- package/engine/agentlas-input.cjs +273 -20
- package/engine/agentlas-judgment.cjs +0 -0
- package/engine/agentlas-mcp-env.cjs +84 -23
- package/engine/agentlas-mcp-wrapper.cjs +36 -4
- package/engine/agentlas-memory-governance.cjs +2 -1
- package/engine/agentlas-memory-import.cjs +49 -12
- package/engine/agentlas-native-host.cjs +279 -44
- package/engine/agentlas-onboard.cjs +23 -1
- package/engine/agentlas-permissions.cjs +5 -1
- package/engine/agentlas-sqlite-policy.cjs +40 -2
- package/engine/agentlas-tasks.cjs +7 -0
- package/engine/agentlas-tools.cjs +37 -10
- package/engine/agentlas-ui.cjs +26 -4
- package/engine/agentlas-workforce.cjs +29 -5
- package/engine/agentlas-workload-routing.cjs +83 -10
- package/engine/agentlas.cjs +10 -3
- package/engine/agents/builder.cjs +35 -10
- package/engine/agents/files.cjs +81 -10
- package/engine/agents/import-local.cjs +10 -2
- package/engine/agents/registry.cjs +10 -3
- package/engine/agents/router.cjs +7 -7
- package/engine/agents/routes.cjs +31 -9
- package/engine/automation/daemon.cjs +154 -40
- package/engine/automation/launchd.cjs +395 -16
- package/engine/automation/schedule.cjs +90 -3
- package/engine/automation/store.cjs +163 -59
- package/engine/browser/cdp.cjs +190 -17
- package/engine/browser/vault.cjs +1 -0
- package/engine/cli-output.cjs +80 -28
- package/engine/cloud/auth.cjs +131 -13
- package/engine/cloud/hub-client.cjs +41 -16
- package/engine/cloud-assets/cas.cjs +18 -9
- package/engine/cloud-assets/commands.cjs +108 -30
- package/engine/cloud-assets/package.cjs +66 -3
- package/engine/cloud-assets/restore.cjs +12 -9
- package/engine/cloud-assets/state.cjs +308 -113
- package/engine/commands/acp.cjs +13 -2
- package/engine/commands/automation.cjs +75 -24
- package/engine/commands/billing.cjs +6 -1
- package/engine/commands/browser.cjs +61 -29
- package/engine/commands/build.cjs +42 -12
- package/engine/commands/call.cjs +1 -1
- package/engine/commands/cd.cjs +1 -1
- package/engine/commands/cloud.cjs +2 -1
- package/engine/commands/connect.cjs +38 -13
- package/engine/commands/creds.cjs +74 -29
- package/engine/commands/doctor.cjs +17 -1
- package/engine/commands/document.cjs +38 -10
- package/engine/commands/env.cjs +6 -1
- package/engine/commands/firm.cjs +42 -12
- package/engine/commands/graph.cjs +36 -32
- package/engine/commands/help.cjs +11 -1
- package/engine/commands/hep.cjs +1 -1
- package/engine/commands/import.cjs +5 -4
- package/engine/commands/index.cjs +1 -1
- package/engine/commands/install.cjs +6 -4
- package/engine/commands/list.cjs +56 -16
- package/engine/commands/login.cjs +5 -0
- package/engine/commands/logout.cjs +6 -1
- package/engine/commands/mcp.cjs +8 -2
- package/engine/commands/memory.cjs +3 -2
- package/engine/commands/multimodal.cjs +12 -0
- package/engine/commands/native.cjs +5 -1
- package/engine/commands/one.cjs +78 -33
- package/engine/commands/plugin.cjs +13 -2
- package/engine/commands/project.cjs +15 -12
- package/engine/commands/research.cjs +18 -7
- package/engine/commands/roles.cjs +104 -28
- package/engine/commands/route.cjs +2 -2
- package/engine/commands/run.cjs +42 -9
- package/engine/commands/search.cjs +23 -2
- package/engine/commands/setup.cjs +6 -1
- package/engine/commands/storm.cjs +44 -6
- package/engine/commands/swarm.cjs +47 -9
- package/engine/commands/telegram.cjs +6 -1
- package/engine/commands/uninstall.cjs +12 -6
- package/engine/commands/update.cjs +5 -0
- package/engine/commands/upload.cjs +2 -1
- package/engine/commands/usage.cjs +6 -1
- package/engine/commands/version.cjs +6 -1
- package/engine/commands/whoami.cjs +6 -1
- package/engine/commands/workforce.cjs +60 -16
- package/engine/core/capability-grants.cjs +7 -2
- package/engine/core/daemon-client.cjs +36 -7
- package/engine/core/db.cjs +10 -2
- package/engine/core/desktop-core-fetch.cjs +477 -77
- package/engine/core/schema-ensure.cjs +23 -11
- package/engine/experience/build.cjs +5 -1
- package/engine/experience/intents.cjs +29 -82
- package/engine/experience/runtime.cjs +7 -1
- package/engine/experience/variant.cjs +58 -4
- package/engine/firms/orchestrate.cjs +9 -6
- package/engine/graph/ask-model.cjs +22 -11
- package/engine/graph/interview.cjs +74 -23
- package/engine/graph/package.cjs +63 -8
- package/engine/hephaestus/local-core.cjs +18 -5
- package/engine/hephaestus/runtime.cjs +48 -43
- package/engine/hub/install.cjs +230 -61
- package/engine/hub/plugins.cjs +391 -29
- package/engine/mcp/consent.cjs +25 -17
- package/engine/mcp/contract.cjs +229 -24
- package/engine/mcp/inventory.cjs +15 -5
- package/engine/mcp/plan.cjs +5 -1
- package/engine/mcp/probe.cjs +24 -9
- package/engine/memory-cli/curate.cjs +74 -16
- package/engine/oberon/common.cjs +37 -12
- package/engine/oberon/manifest.cjs +25 -2
- package/engine/oberon/outputs.cjs +49 -19
- package/engine/oberon/render.cjs +665 -40
- package/engine/project/career-graph.cjs +15 -2
- package/engine/project/controller.cjs +133 -15
- package/engine/project/credentials.cjs +212 -34
- package/engine/project/env-file.cjs +93 -8
- package/engine/project/memory-context.cjs +11 -57
- package/engine/project/ontology.cjs +66 -25
- package/engine/project/paths.cjs +42 -8
- package/engine/project/seed.cjs +88 -51
- package/engine/project/state.cjs +41 -16
- package/engine/project/team.cjs +26 -1
- package/engine/runtime-refusal.cjs +4 -1
- package/engine/runtimes/acp-driver.cjs +56 -9
- package/engine/runtimes/auth-evidence.cjs +21 -2
- package/engine/runtimes/detect.cjs +28 -8
- package/engine/runtimes/kinds.cjs +12 -4
- package/engine/runtimes/overrides.cjs +4 -3
- package/engine/runtimes/resolve.cjs +8 -7
- package/engine/runtimes/roles.cjs +7 -5
- package/engine/sessions/apply-fences.cjs +396 -56
- package/engine/sessions/fences.cjs +111 -27
- package/engine/sessions/memory-turn.cjs +62 -3
- package/engine/sessions/orchestrator.cjs +11 -1
- package/engine/sessions/prompt.cjs +66 -9
- package/engine/sessions/session.cjs +70 -10
- package/engine/sessions/sink.cjs +78 -12
- package/engine/sessions/store.cjs +29 -7
- package/engine/storm/storm.cjs +26 -3
- package/engine/storm/swarm.cjs +31 -3
- package/engine/telegram/connect.cjs +185 -22
- package/engine/ui/commands-catalog.cjs +1 -1
- package/engine/ui/palette.cjs +12 -2
- package/engine/ui/repl.cjs +57 -9
- package/engine/ui/screens.cjs +2 -4
- package/engine/vendor/desktop-core.manifest.json +5 -5
- package/engine/workforce/capture.cjs +9 -27
- package/engine/workforce/deps.cjs +18 -6
- package/engine/workforce/local-core-transport.cjs +68 -5
- package/package.json +1 -1
- package/engine/commands/career-graph.cjs +0 -51
|
@@ -24,6 +24,9 @@ const {
|
|
|
24
24
|
upsertLocalCredentialMapCli,
|
|
25
25
|
projectScopedGlobalEnvKeyCli,
|
|
26
26
|
safeCredentialDestRelCli,
|
|
27
|
+
credentialProjectRootCli,
|
|
28
|
+
resolveCredentialDestinationCli,
|
|
29
|
+
copyCredentialFileAtomicCli,
|
|
27
30
|
} = require("../project/credentials.cjs");
|
|
28
31
|
|
|
29
32
|
// 데스크탑과 공유하는 키체인 서비스/키 접두어 (v1 상수 그대로).
|
|
@@ -41,19 +44,27 @@ function readKeytar() {
|
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
function parseCredFlags(args) {
|
|
47
|
+
function parseCredFlags(args, schema) {
|
|
45
48
|
const f = {};
|
|
49
|
+
const values = new Set(schema.values || []);
|
|
50
|
+
const booleans = new Set(schema.booleans || []);
|
|
46
51
|
for (let i = 0; i < args.length; i++) {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const token = String(args[i]);
|
|
53
|
+
if (!token.startsWith("--") || token === "--") throw new Error(`unexpected argument: ${token}`);
|
|
54
|
+
const at = token.indexOf("=");
|
|
55
|
+
const key = token.slice(2, at >= 0 ? at : undefined);
|
|
56
|
+
if (!values.has(key) && !booleans.has(key)) throw new Error(`unknown option: --${key}`);
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(f, key)) throw new Error(`duplicate option: --${key}`);
|
|
58
|
+
if (booleans.has(key)) {
|
|
59
|
+
if (at >= 0) throw new Error(`--${key} does not take a value`);
|
|
60
|
+
f[key] = true;
|
|
61
|
+
continue;
|
|
56
62
|
}
|
|
63
|
+
const next = at >= 0 ? token.slice(at + 1) : args[++i];
|
|
64
|
+
if (next === undefined || next === "" || (at < 0 && String(next).startsWith("--"))) {
|
|
65
|
+
throw new Error(`--${key} requires a value`);
|
|
66
|
+
}
|
|
67
|
+
f[key] = String(next);
|
|
57
68
|
}
|
|
58
69
|
return f;
|
|
59
70
|
}
|
|
@@ -66,20 +77,35 @@ function resolveCredentialSourcePath(source, cwd) {
|
|
|
66
77
|
|
|
67
78
|
async function cmdCredsFile(ctx, args) {
|
|
68
79
|
const fail = (msg) => { ctx.err(msg); return 1; };
|
|
69
|
-
|
|
80
|
+
let f;
|
|
81
|
+
try {
|
|
82
|
+
f = parseCredFlags(args, {
|
|
83
|
+
values: ["source", "path", "provider", "env", "dest", "project", "id", "requiredFor", "staleCheck"],
|
|
84
|
+
booleans: ["force"],
|
|
85
|
+
});
|
|
86
|
+
} catch (error) { return fail(String((error && error.message) || error)); }
|
|
87
|
+
if (f.source !== undefined && f.path !== undefined) return fail("use only one of --source or --path");
|
|
70
88
|
const source = typeof f.source === "string" ? f.source : typeof f.path === "string" ? f.path : "";
|
|
71
89
|
if (!source) return fail("usage: agentlas creds file --source <path> [--provider <name>] [--env <ENV_NAME>] [--dest <relative-path>] [--project <path>] [--force]");
|
|
72
|
-
const
|
|
73
|
-
if (!
|
|
90
|
+
const requestedProject = typeof f.project === "string" && f.project ? f.project : activeProjectPath(ctx.db());
|
|
91
|
+
if (!requestedProject) return fail("creds file requires a project path");
|
|
92
|
+
let project;
|
|
93
|
+
try { project = credentialProjectRootCli(requestedProject); }
|
|
94
|
+
catch (error) { return fail(`credential project is unsafe: ${String((error && error.message) || error)}`); }
|
|
74
95
|
const provider = typeof f.provider === "string" && f.provider ? f.provider : "credential_file";
|
|
96
|
+
if (provider.length > 128 || /[\u0000-\u001f\u007f]/.test(provider)) return fail("credential provider name contains unsafe text");
|
|
75
97
|
const envKey = typeof f.env === "string" && f.env ? f.env.trim() : "";
|
|
76
98
|
if (envKey && !/^[A-Z][A-Z0-9_]*$/.test(envKey)) return fail("credential env name must look like ENV_NAME");
|
|
77
99
|
|
|
78
100
|
const arch = loadArch();
|
|
79
101
|
const cfg = localCredentialConfigCli(arch);
|
|
80
102
|
const projectName = path.basename(project) || "Project";
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
try {
|
|
104
|
+
ensureLocalCredentialStoreCli(project, projectName, arch);
|
|
105
|
+
ensureSoulCredentialIndexCli(project, projectName, arch);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return fail(`credential store is unsafe: ${String((error && error.message) || error)}`);
|
|
108
|
+
}
|
|
83
109
|
|
|
84
110
|
const sourceAbs = resolveCredentialSourcePath(source);
|
|
85
111
|
let stat;
|
|
@@ -99,15 +125,13 @@ async function cmdCredsFile(ctx, args) {
|
|
|
99
125
|
} catch (e) {
|
|
100
126
|
return fail(String((e && e.message) || e));
|
|
101
127
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
let destAbs;
|
|
129
|
+
try {
|
|
130
|
+
destAbs = resolveCredentialDestinationCli(project, destRel);
|
|
131
|
+
copyCredentialFileAtomicCli(sourceAbs, destAbs, { force: Boolean(f.force) });
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return fail(`credential copy refused: ${String((error && error.message) || error)}`);
|
|
105
134
|
}
|
|
106
|
-
if (fs.existsSync(destAbs) && !f.force) return fail(`credential destination already exists: ${destRel} (use --force to replace)`);
|
|
107
|
-
|
|
108
|
-
fs.mkdirSync(path.dirname(destAbs), { recursive: true });
|
|
109
|
-
fs.copyFileSync(sourceAbs, destAbs);
|
|
110
|
-
try { fs.chmodSync(destAbs, 0o600); } catch { /* best-effort */ }
|
|
111
135
|
|
|
112
136
|
const targets = [destRel];
|
|
113
137
|
if (envKey) {
|
|
@@ -185,11 +209,20 @@ async function run(ctx, args) {
|
|
|
185
209
|
const fail = (msg) => { ctx.err(msg); return 1; };
|
|
186
210
|
const sub = args[0];
|
|
187
211
|
if (sub === "file") return cmdCredsFile(ctx, args.slice(1));
|
|
188
|
-
if (sub === "list" || sub === "ls")
|
|
212
|
+
if (sub === "list" || sub === "ls") {
|
|
213
|
+
if (args.length !== 1) return fail(`unexpected argument: ${String(args[1])}`);
|
|
214
|
+
return cmdCredsList(ctx);
|
|
215
|
+
}
|
|
189
216
|
if (sub !== "save") {
|
|
190
217
|
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');
|
|
191
218
|
}
|
|
192
|
-
|
|
219
|
+
let f;
|
|
220
|
+
try {
|
|
221
|
+
f = parseCredFlags(args.slice(1), {
|
|
222
|
+
values: ["provider", "key", "value", "project"],
|
|
223
|
+
booleans: [],
|
|
224
|
+
});
|
|
225
|
+
} catch (error) { return fail(String((error && error.message) || error)); }
|
|
193
226
|
const key = typeof f.key === "string" ? f.key.trim() : "";
|
|
194
227
|
let value = f.value === undefined || f.value === true ? "" : String(f.value);
|
|
195
228
|
/*
|
|
@@ -207,14 +240,26 @@ async function run(ctx, args) {
|
|
|
207
240
|
: "Note: a secret passed via --value lands in shell history and `ps`. Prefer stdin next time: printf '%s' \"$SECRET\" | agentlas creds save --key X --value -");
|
|
208
241
|
}
|
|
209
242
|
if (!key || !value) return fail("creds save requires --key and --value (use `--value -` to read the secret from stdin)");
|
|
243
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(key)) return fail("credential env name must look like ENV_NAME");
|
|
244
|
+
if (/[\u0000\r\n]/.test(value)) return fail("credential env value must be a single line; use `agentlas creds file` for multiline credentials");
|
|
210
245
|
const provider = typeof f.provider === "string" && f.provider ? f.provider : key;
|
|
211
|
-
|
|
246
|
+
if (provider.length > 128 || /[\u0000-\u001f\u007f]/.test(provider)) return fail("credential provider name contains unsafe text");
|
|
247
|
+
const requestedProject = typeof f.project === "string" && f.project ? f.project : activeProjectPath(ctx.db());
|
|
248
|
+
let project = null;
|
|
249
|
+
if (requestedProject) {
|
|
250
|
+
try { project = credentialProjectRootCli(requestedProject); }
|
|
251
|
+
catch (error) { return fail(`credential project is unsafe: ${String((error && error.message) || error)}`); }
|
|
252
|
+
}
|
|
212
253
|
const targets = [];
|
|
213
254
|
const arch = loadArch();
|
|
214
255
|
const projectName = project ? path.basename(project) || "Project" : "Project";
|
|
215
256
|
if (project) {
|
|
216
|
-
|
|
217
|
-
|
|
257
|
+
try {
|
|
258
|
+
ensureLocalCredentialStoreCli(project, projectName, arch);
|
|
259
|
+
ensureSoulCredentialIndexCli(project, projectName, arch);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return fail(`credential store is unsafe: ${String((error && error.message) || error)}`);
|
|
262
|
+
}
|
|
218
263
|
}
|
|
219
264
|
|
|
220
265
|
// 1) keychain vault — project-scoped when a project is active.
|
|
@@ -262,4 +307,4 @@ async function run(ctx, args) {
|
|
|
262
307
|
return 0;
|
|
263
308
|
}
|
|
264
309
|
|
|
265
|
-
module.exports = { run, resolveCredentialSourcePath };
|
|
310
|
+
module.exports = { run, resolveCredentialSourcePath, parseCredFlags };
|
|
@@ -14,6 +14,14 @@ const { runtimeAuthEvidence } = require("../runtimes/auth-evidence.cjs");
|
|
|
14
14
|
const { sharedRuntimeKind } = require("../runtimes/resolve.cjs");
|
|
15
15
|
const { resolvedModelRole } = require("../runtimes/roles.cjs");
|
|
16
16
|
|
|
17
|
+
function hasCloudSessionCredential() {
|
|
18
|
+
try {
|
|
19
|
+
return Boolean(require("../cloud/auth.cjs").cloudSessionCookie());
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
function roleDetail(selection, role, en) {
|
|
18
26
|
if (!selection) return en ? "not set" : "미설정";
|
|
19
27
|
const provider = selection.kind === "byok" ? selection.backend || "byok" : selection.kind;
|
|
@@ -28,6 +36,12 @@ function roleDetail(selection, role, en) {
|
|
|
28
36
|
|
|
29
37
|
async function run(ctx, args = []) {
|
|
30
38
|
const en = ctx.lang === "en";
|
|
39
|
+
const unknown = args.filter((arg) => arg !== "--json");
|
|
40
|
+
if (unknown.length || args.filter((arg) => arg === "--json").length > 1) {
|
|
41
|
+
const error = new Error("usage: agentlas doctor [--json]");
|
|
42
|
+
error.code = "INVALID_ARGUMENT";
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
31
45
|
// clig.dev: 스크립트 소비자를 위한 기계 계약. 사람용 줄과 같은 사실만 담는다.
|
|
32
46
|
if (ctx.output?.format === "json" || args.includes("--json")) {
|
|
33
47
|
const db = ctx.db();
|
|
@@ -43,7 +57,9 @@ async function run(ctx, args = []) {
|
|
|
43
57
|
orchestrator: resolvedModelRole(db, "orchestrator"),
|
|
44
58
|
worker: resolvedModelRole(db, "worker"),
|
|
45
59
|
},
|
|
46
|
-
|
|
60
|
+
// JSON is intentionally offline, but it must use the same validated
|
|
61
|
+
// credential interpretation as the human path rather than file existence.
|
|
62
|
+
cloudSession: hasCloudSessionCredential(),
|
|
47
63
|
}, null, 2));
|
|
48
64
|
return 0;
|
|
49
65
|
})();
|
|
@@ -26,13 +26,42 @@ function toUrl(src) {
|
|
|
26
26
|
return "file://" + path.resolve(src); // 로컬 파일
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function documentArgumentError(message) {
|
|
30
|
+
const error = new Error(message);
|
|
31
|
+
error.code = "INVALID_ARGUMENT";
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parsePdfArgs(args, ko) {
|
|
36
|
+
let landscape = false;
|
|
37
|
+
let out = null;
|
|
38
|
+
let src = null;
|
|
39
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
40
|
+
const token = String(args[index]);
|
|
41
|
+
if (token === "--landscape") {
|
|
42
|
+
if (landscape) throw documentArgumentError(usage(ko));
|
|
43
|
+
landscape = true;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (token === "-o") {
|
|
47
|
+
if (out !== null) throw documentArgumentError(usage(ko));
|
|
48
|
+
const value = args[index + 1];
|
|
49
|
+
if (!value || String(value).startsWith("-")) throw documentArgumentError(usage(ko));
|
|
50
|
+
out = String(value);
|
|
51
|
+
index += 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (token.startsWith("-")) throw documentArgumentError(usage(ko));
|
|
55
|
+
if (src !== null) throw documentArgumentError(usage(ko));
|
|
56
|
+
src = token;
|
|
57
|
+
}
|
|
58
|
+
if (!src) throw documentArgumentError(usage(ko));
|
|
59
|
+
return { landscape, out, src };
|
|
60
|
+
}
|
|
61
|
+
|
|
29
62
|
async function toPdf(ctx, args) {
|
|
30
63
|
const ko = ctx.lang === "ko";
|
|
31
|
-
const
|
|
32
|
-
const oi = args.indexOf("-o");
|
|
33
|
-
const out = oi >= 0 && args[oi + 1] ? args[oi + 1] : null;
|
|
34
|
-
const src = args.find((a, i) => a && !String(a).startsWith("-") && (oi < 0 || i !== oi + 1));
|
|
35
|
-
if (!src) { ctx.err(usage(ko)); return 1; }
|
|
64
|
+
const { landscape, out, src } = parsePdfArgs(args, ko);
|
|
36
65
|
|
|
37
66
|
// 로컬 파일이면 존재 확인(없는 걸 조용히 빈 PDF 로 굽지 않는다).
|
|
38
67
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(src) && !fs.existsSync(path.resolve(src))) {
|
|
@@ -54,7 +83,7 @@ async function toPdf(ctx, args) {
|
|
|
54
83
|
page = await cdp.attachPage({ selection: "new" });
|
|
55
84
|
await page.navigate(url, { waitMs: 1800 });
|
|
56
85
|
await page.waitFor("document.readyState === 'complete'", { timeoutMs: 8000 });
|
|
57
|
-
const r = await page.printPdf(outPath, { landscape
|
|
86
|
+
const r = await page.printPdf(outPath, { landscape });
|
|
58
87
|
if (typeof ctx.ui.stopSpinner === "function") ctx.ui.stopSpinner();
|
|
59
88
|
ctx.out(`${ctx.ui.green("✓")} ${ko ? "PDF 를 만들었습니다" : "wrote PDF"}: ${r.path} ${ctx.ui.dim(`(${Math.round(r.bytes / 1024)} KB)`)}`);
|
|
60
89
|
return 0;
|
|
@@ -70,10 +99,9 @@ async function toPdf(ctx, args) {
|
|
|
70
99
|
async function run(ctx, args) {
|
|
71
100
|
const ko = ctx.lang === "ko";
|
|
72
101
|
const sub = String(args[0] || "").toLowerCase();
|
|
73
|
-
if (!sub ||
|
|
102
|
+
if (!sub || (["help", "-h", "--help"].includes(sub) && args.length === 1)) { ctx.out(usage(ko)); return 0; }
|
|
74
103
|
if (sub === "pdf") return toPdf(ctx, args.slice(1));
|
|
75
|
-
|
|
76
|
-
return 1;
|
|
104
|
+
throw documentArgumentError(usage(ko));
|
|
77
105
|
}
|
|
78
106
|
|
|
79
|
-
module.exports = { run };
|
|
107
|
+
module.exports = { run, parsePdfArgs };
|
package/engine/commands/env.cjs
CHANGED
|
@@ -21,7 +21,12 @@ function sharedEnvKeys() {
|
|
|
21
21
|
return Object.keys(fromFiles).sort();
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function run(ctx) {
|
|
24
|
+
function run(ctx, args = []) {
|
|
25
|
+
if (args.length) {
|
|
26
|
+
const error = new Error("usage: agentlas env");
|
|
27
|
+
error.code = "INVALID_ARGUMENT";
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
25
30
|
const keys = sharedEnvKeys();
|
|
26
31
|
ctx.out(`Shared env keys: ${keys.length} (values hidden; from credentials.env):`);
|
|
27
32
|
for (const k of keys) ctx.out(` ${k}`);
|
package/engine/commands/firm.cjs
CHANGED
|
@@ -9,6 +9,42 @@
|
|
|
9
9
|
*/
|
|
10
10
|
const { rowToAgent } = require("../agents/registry.cjs");
|
|
11
11
|
|
|
12
|
+
function parseFirmArgs(args) {
|
|
13
|
+
const flags = { runtime: null, model: null, effort: null, tier: null, permission: null, task: [] };
|
|
14
|
+
const fields = new Map([
|
|
15
|
+
["--runtime", "runtime"],
|
|
16
|
+
["--model", "model"],
|
|
17
|
+
["--effort", "effort"],
|
|
18
|
+
["--tier", "tier"],
|
|
19
|
+
["--permission", "permission"],
|
|
20
|
+
]);
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
let passthrough = false;
|
|
23
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
24
|
+
const token = String(args[i]);
|
|
25
|
+
if (passthrough) { flags.task.push(token); continue; }
|
|
26
|
+
if (token === "--") { passthrough = true; continue; }
|
|
27
|
+
const at = token.indexOf("=");
|
|
28
|
+
const option = at >= 0 ? token.slice(0, at) : token;
|
|
29
|
+
const field = fields.get(option);
|
|
30
|
+
if (field) {
|
|
31
|
+
if (seen.has(field)) throw new Error(`duplicate option: ${option}`);
|
|
32
|
+
seen.add(field);
|
|
33
|
+
const value = at >= 0 ? token.slice(at + 1) : args[++i];
|
|
34
|
+
if (value === undefined || value === "" || (at < 0 && String(value).startsWith("--"))) {
|
|
35
|
+
throw new Error(`${option} requires a value`);
|
|
36
|
+
}
|
|
37
|
+
flags[field] = String(value);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (token.startsWith("-")) {
|
|
41
|
+
throw new Error(`unknown option: ${token} (use -- before a task that starts with '-')`);
|
|
42
|
+
}
|
|
43
|
+
flags.task.push(token);
|
|
44
|
+
}
|
|
45
|
+
return flags;
|
|
46
|
+
}
|
|
47
|
+
|
|
12
48
|
function findFirm(db, token) {
|
|
13
49
|
const q = String(token || "").trim().toLowerCase();
|
|
14
50
|
if (!q) return null;
|
|
@@ -50,17 +86,11 @@ async function run(ctx, args) {
|
|
|
50
86
|
}
|
|
51
87
|
const ceo = rowToAgent(ceoRow);
|
|
52
88
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
else if (rest[i] === "--model") flags.model = rest[++i];
|
|
59
|
-
else if (rest[i] === "--effort") flags.effort = rest[++i];
|
|
60
|
-
else if (rest[i] === "--tier") flags.tier = rest[++i];
|
|
61
|
-
else if (rest[i] === "--permission") flags.permission = rest[++i];
|
|
62
|
-
else flags.task.push(rest[i]);
|
|
63
|
-
}
|
|
89
|
+
// 명령끼리 참조 금지 규칙상 run.cjs 미차용. 오타 옵션은 CEO 계획 턴으로
|
|
90
|
+
// 흘리지 않고, 대시로 시작하는 실제 작업은 `--` 뒤에서만 받는다.
|
|
91
|
+
let flags;
|
|
92
|
+
try { flags = parseFirmArgs(args.slice(1)); }
|
|
93
|
+
catch (error) { ctx.err(String((error && error.message) || error)); return 1; }
|
|
64
94
|
const task = flags.task.join(" ").trim();
|
|
65
95
|
if (task) {
|
|
66
96
|
// 3-tier 위임 실행 — PLAN → DELEGATE → SYNTHESIZE (firms/orchestrate.cjs).
|
|
@@ -176,4 +206,4 @@ async function run(ctx, args) {
|
|
|
176
206
|
return startRepl(ctx, { agent: ceo.slug });
|
|
177
207
|
}
|
|
178
208
|
|
|
179
|
-
module.exports = { run, findFirm };
|
|
209
|
+
module.exports = { run, findFirm, parseFirmArgs };
|
|
@@ -15,7 +15,11 @@ const fs = require("node:fs");
|
|
|
15
15
|
const path = require("node:path");
|
|
16
16
|
const pkgLib = require("../graph/package.cjs");
|
|
17
17
|
const desktopCore = require("../core/desktop-core.cjs");
|
|
18
|
+
const { terminalTextOf } = require("../cli-output.cjs");
|
|
18
19
|
|
|
20
|
+
function inlineText(value, maxLength = 500) {
|
|
21
|
+
return terminalTextOf(value, maxLength).replace(/\s+/g, " ").trim();
|
|
22
|
+
}
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
function graphRows(ctx, db) {
|
|
@@ -116,7 +120,7 @@ function describe(ctx, row, graph, en) {
|
|
|
116
120
|
const kindLabel = kind === "cron"
|
|
117
121
|
? (en ? "schedule" : "예약")
|
|
118
122
|
: (en ? "input" : "입력");
|
|
119
|
-
return `${ctx.ui.bold(row.name)} ${ctx.ui.dim(`${kindLabel} · ${nodeCount} ${en ? "steps" : "단계"} · ${state} · ${when}`)}`;
|
|
123
|
+
return `${ctx.ui.bold(inlineText(row.name))} ${ctx.ui.dim(`${kindLabel} · ${nodeCount} ${en ? "steps" : "단계"} · ${state} · ${inlineText(when)}`)}`;
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
function findGraph(rows, needle) {
|
|
@@ -135,9 +139,9 @@ function findGraph(rows, needle) {
|
|
|
135
139
|
/** 이름이 여러 개 걸렸을 때, 고르지 말고 후보를 보여준다. */
|
|
136
140
|
function reportAmbiguous(ctx, needle, matches, en) {
|
|
137
141
|
ctx.err(en
|
|
138
|
-
? `"${needle}" matches ${matches.length} automations. Say which one:`
|
|
139
|
-
: `"${needle}"에 자동화 ${matches.length}개가 걸립니다. 어느 것인지 정확히 적어 주세요:`);
|
|
140
|
-
for (const row of matches) ctx.err(` · ${row.name}`);
|
|
142
|
+
? `"${inlineText(needle)}" matches ${matches.length} automations. Say which one:`
|
|
143
|
+
: `"${inlineText(needle)}"에 자동화 ${matches.length}개가 걸립니다. 어느 것인지 정확히 적어 주세요:`);
|
|
144
|
+
for (const row of matches) ctx.err(` · ${inlineText(row.name)}`);
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
function listGraphs(ctx) {
|
|
@@ -240,8 +244,8 @@ async function reconcileGraph(ctx, needle, flags, en) {
|
|
|
240
244
|
const view = reconciliation.get(row.id);
|
|
241
245
|
if (!view) {
|
|
242
246
|
ctx.out(en
|
|
243
|
-
? `"${row.name}" has nothing waiting to be reconciled.`
|
|
244
|
-
: `"${row.name}"에는 재조정을 기다리는 것이 없습니다.`);
|
|
247
|
+
? `"${inlineText(row.name)}" has nothing waiting to be reconciled.`
|
|
248
|
+
: `"${inlineText(row.name)}"에는 재조정을 기다리는 것이 없습니다.`);
|
|
245
249
|
return 0;
|
|
246
250
|
}
|
|
247
251
|
|
|
@@ -255,34 +259,34 @@ async function reconcileGraph(ctx, needle, flags, en) {
|
|
|
255
259
|
|
|
256
260
|
if (done.size === 0 && notDone.size === 0) {
|
|
257
261
|
ctx.out(en
|
|
258
|
-
? `"${row.name}" stopped with steps whose effect cannot be told from the record.`
|
|
259
|
-
: `"${row.name}"은(는) 기록만으로는 일어났는지 알 수 없는 단계가 있어 멈춰 있습니다.`);
|
|
262
|
+
? `"${inlineText(row.name)}" stopped with steps whose effect cannot be told from the record.`
|
|
263
|
+
: `"${inlineText(row.name)}"은(는) 기록만으로는 일어났는지 알 수 없는 단계가 있어 멈춰 있습니다.`);
|
|
260
264
|
ctx.out("");
|
|
261
265
|
for (const node of view.nodes) {
|
|
262
266
|
const needs = node.outputRequired
|
|
263
267
|
? (en ? ` (needs --output ${node.nodeId}=<value> when done)` : ` (일어났다면 --output ${node.nodeId}=<값> 필요)`)
|
|
264
268
|
: "";
|
|
265
|
-
ctx.out(` ${node.nodeId} ${node.label}${needs}`);
|
|
269
|
+
ctx.out(` ${inlineText(node.nodeId)} ${inlineText(node.label)}${inlineText(needs)}`);
|
|
266
270
|
}
|
|
267
271
|
ctx.out("");
|
|
268
272
|
ctx.out(ctx.ui.dim(en
|
|
269
|
-
? `Decide each one: agentlas graph reconcile "${row.name}" --done <node> --not-done <node>`
|
|
270
|
-
: `각각 정하세요: agentlas graph reconcile "${row.name}" --done <노드> --not-done <노드>`));
|
|
273
|
+
? `Decide each one: agentlas graph reconcile "${inlineText(row.name)}" --done <node> --not-done <node>`
|
|
274
|
+
: `각각 정하세요: agentlas graph reconcile "${inlineText(row.name)}" --done <노드> --not-done <노드>`));
|
|
271
275
|
return 0;
|
|
272
276
|
}
|
|
273
277
|
|
|
274
278
|
const undecided = view.nodes.filter((n) => !done.has(n.nodeId) && !notDone.has(n.nodeId));
|
|
275
279
|
if (undecided.length > 0) {
|
|
276
280
|
ctx.err(en
|
|
277
|
-
? `Still undecided: ${undecided.map((n) => n.nodeId).join(", ")}. Every step needs one answer.`
|
|
278
|
-
: `아직 안 정한 단계가 있습니다: ${undecided.map((n) => n.nodeId).join(", ")}. 모든 단계에 답이 필요합니다.`);
|
|
281
|
+
? `Still undecided: ${undecided.map((n) => inlineText(n.nodeId)).join(", ")}. Every step needs one answer.`
|
|
282
|
+
: `아직 안 정한 단계가 있습니다: ${undecided.map((n) => inlineText(n.nodeId)).join(", ")}. 모든 단계에 답이 필요합니다.`);
|
|
279
283
|
return 1;
|
|
280
284
|
}
|
|
281
285
|
const missingOutput = view.nodes.filter((n) => done.has(n.nodeId) && n.outputRequired && !outputs.has(n.nodeId));
|
|
282
286
|
if (missingOutput.length > 0) {
|
|
283
287
|
ctx.err(en
|
|
284
|
-
? `These steps populate a value, so completing them needs it: ${missingOutput.map((n) => `--output ${n.nodeId}=<value>`).join(" ")}`
|
|
285
|
-
: `값을 만드는 단계라 "일어났다"로 두려면 값이 필요합니다: ${missingOutput.map((n) => `--output ${n.nodeId}=<값>`).join(" ")}`);
|
|
288
|
+
? `These steps populate a value, so completing them needs it: ${missingOutput.map((n) => `--output ${inlineText(n.nodeId)}=<value>`).join(" ")}`
|
|
289
|
+
: `값을 만드는 단계라 "일어났다"로 두려면 값이 필요합니다: ${missingOutput.map((n) => `--output ${inlineText(n.nodeId)}=<값>`).join(" ")}`);
|
|
286
290
|
return 1;
|
|
287
291
|
}
|
|
288
292
|
|
|
@@ -304,8 +308,8 @@ async function reconcileGraph(ctx, needle, flags, en) {
|
|
|
304
308
|
? `Reconciled. ${result.completedNodeIds.length} step(s) marked as done, ${result.retryNodeIds.length} to redo.`
|
|
305
309
|
: `재조정했습니다. ${result.completedNodeIds.length}개는 일어난 것으로, ${result.retryNodeIds.length}개는 다시 하는 것으로 두었습니다.`);
|
|
306
310
|
ctx.out(ctx.ui.dim(en
|
|
307
|
-
? `Now run it: agentlas graph run "${row.name}"`
|
|
308
|
-
: `이제 실행하세요: agentlas graph run "${row.name}"`));
|
|
311
|
+
? `Now run it: agentlas graph run "${inlineText(row.name)}"`
|
|
312
|
+
: `이제 실행하세요: agentlas graph run "${inlineText(row.name)}"`));
|
|
309
313
|
return 0;
|
|
310
314
|
} catch (error) {
|
|
311
315
|
ctx.err(en
|
|
@@ -342,8 +346,8 @@ function showGraph(ctx, needle) {
|
|
|
342
346
|
const requirement = graphInputRequirement(graph, en);
|
|
343
347
|
if (requirement) {
|
|
344
348
|
ctx.out(" " + ctx.ui.dim(en
|
|
345
|
-
? `Starts from a value you provide — ${requirement.label}`
|
|
346
|
-
: `시작할 때 값을 받습니다 — ${requirement.label}`));
|
|
349
|
+
? `Starts from a value you provide — ${inlineText(requirement.label)}`
|
|
350
|
+
: `시작할 때 값을 받습니다 — ${inlineText(requirement.label)}`));
|
|
347
351
|
}
|
|
348
352
|
ctx.out("");
|
|
349
353
|
renderGraphTree(ctx, graph, en);
|
|
@@ -354,8 +358,8 @@ function showGraph(ctx, needle) {
|
|
|
354
358
|
? `This graph will stop when it runs (${problems.length}):`
|
|
355
359
|
: `이대로 실행하면 도중에 멈춥니다 (${problems.length}건):`));
|
|
356
360
|
for (const p of problems) {
|
|
357
|
-
ctx.out(` ⚠ ${p.what}`);
|
|
358
|
-
ctx.out(` ${ctx.ui.dim(p.fix)}`);
|
|
361
|
+
ctx.out(` ⚠ ${inlineText(p.what, 1000)}`);
|
|
362
|
+
ctx.out(` ${ctx.ui.dim(inlineText(p.fix, 1000))}`);
|
|
359
363
|
}
|
|
360
364
|
}
|
|
361
365
|
// 그대로 복사해 쓸 수 있는 명령. 예전에는 값이 필요한 그래프인지 화면에서만 알려주고
|
|
@@ -363,8 +367,8 @@ function showGraph(ctx, needle) {
|
|
|
363
367
|
ctx.out("");
|
|
364
368
|
ctx.out(ctx.ui.dim(en ? "Run it with:" : "실행하려면:"));
|
|
365
369
|
ctx.out(requirement
|
|
366
|
-
? ` agentlas graph run "${row.name}" --input "<${requirement.label}>"`
|
|
367
|
-
: ` agentlas graph run "${row.name}"`);
|
|
370
|
+
? ` agentlas graph run "${inlineText(row.name)}" --input "<${inlineText(requirement.label)}>"`
|
|
371
|
+
: ` agentlas graph run "${inlineText(row.name)}"`);
|
|
368
372
|
return 0;
|
|
369
373
|
}
|
|
370
374
|
|
|
@@ -469,17 +473,17 @@ function nodeLine(ctx, node, en) {
|
|
|
469
473
|
const rule = conditionRule(node, en);
|
|
470
474
|
if (rule) marks.unshift(rule);
|
|
471
475
|
}
|
|
472
|
-
return `${ctx.ui.accent(kindWord(node.type, en))} ${node.label || node.id}`
|
|
473
|
-
+ (marks.length ? ctx.ui.dim(` — ${marks.join(", ")}`) : "");
|
|
476
|
+
return `${ctx.ui.accent(inlineText(kindWord(node.type, en)))} ${inlineText(node.label || node.id)}`
|
|
477
|
+
+ (marks.length ? ctx.ui.dim(` — ${marks.map((mark) => inlineText(mark)).join(", ")}`) : "");
|
|
474
478
|
}
|
|
475
479
|
|
|
476
480
|
/** 갈림길이 실제로 무엇을 보는지 한 줄로. 모르는 연산은 지어내지 않고 그대로 보여준다. */
|
|
477
481
|
function conditionRule(node, en) {
|
|
478
482
|
const cfg = node.config || {};
|
|
479
|
-
const v = cfg.var;
|
|
483
|
+
const v = inlineText(cfg.var);
|
|
480
484
|
if (typeof v !== "string" || !v.trim()) return null;
|
|
481
485
|
const value = cfg.value;
|
|
482
|
-
const shown = typeof value === "string" ? `"${value}"` :
|
|
486
|
+
const shown = typeof value === "string" ? `"${inlineText(value)}"` : inlineText(value);
|
|
483
487
|
switch (cfg.op) {
|
|
484
488
|
case "contains": return en ? `yes when {{${v}}} contains ${shown}` : `{{${v}}}에 ${shown}이(가) 들어 있으면 예`;
|
|
485
489
|
case "truthy": return en ? `yes when {{${v}}} has a value` : `{{${v}}}에 값이 있으면 예`;
|
|
@@ -488,7 +492,7 @@ function conditionRule(node, en) {
|
|
|
488
492
|
case "neq": return en ? `yes when {{${v}}} differs from ${shown}` : `{{${v}}}이(가) ${shown}과 다르면 예`;
|
|
489
493
|
case "gt": return en ? `yes when {{${v}}} > ${shown}` : `{{${v}}}이(가) ${shown}보다 크면 예`;
|
|
490
494
|
case "lt": return en ? `yes when {{${v}}} < ${shown}` : `{{${v}}}이(가) ${shown}보다 작으면 예`;
|
|
491
|
-
default: return en ? `checks {{${v}}} with "${cfg.op ?? "?"}"` : `{{${v}}}을(를) "${cfg.op ?? "?"}"(으)로 검사`;
|
|
495
|
+
default: return en ? `checks {{${v}}} with "${inlineText(cfg.op ?? "?")}"` : `{{${v}}}을(를) "${inlineText(cfg.op ?? "?")}"(으)로 검사`;
|
|
492
496
|
}
|
|
493
497
|
}
|
|
494
498
|
|
|
@@ -525,7 +529,7 @@ function kindWord(type, en) {
|
|
|
525
529
|
const read = vocabulary.readEnum(type, vocabulary.GRAPH_NODE_KINDS);
|
|
526
530
|
if ("unknown" in read) {
|
|
527
531
|
// 이 버전이 모르는 종류 — 지어내지 않고 그렇다고 말한다(원문 보존).
|
|
528
|
-
return vocabulary.degradedLabel(read, en ? "en" : "ko");
|
|
532
|
+
return vocabulary.degradedLabel({ unknown: inlineText(read.unknown) }, en ? "en" : "ko");
|
|
529
533
|
}
|
|
530
534
|
return (en ? enWords[read.known] : ko[read.known]) || read.known;
|
|
531
535
|
}
|
|
@@ -557,8 +561,8 @@ function renderGraphTree(ctx, graph, en) {
|
|
|
557
561
|
? (en ? " — no repeat limit set, so it will refuse to run" : " — 반복 횟수가 정해져 있지 않아 실행이 거절됩니다")
|
|
558
562
|
: (en ? ` — up to ${cap} more time(s)` : ` — 최대 ${cap}바퀴까지`);
|
|
559
563
|
ctx.out(`${indent}${prefix}↩ ${ctx.ui.dim(en
|
|
560
|
-
? `back to "${node.label || node.id}"${capText}`
|
|
561
|
-
: `"${node.label || node.id}"(으)로 되돌아감${capText}`)}`);
|
|
564
|
+
? `back to "${inlineText(node.label || node.id)}"${capText}`
|
|
565
|
+
: `"${inlineText(node.label || node.id)}"(으)로 되돌아감${capText}`)}`);
|
|
562
566
|
return;
|
|
563
567
|
}
|
|
564
568
|
seen.add(nodeId);
|
|
@@ -568,7 +572,7 @@ function renderGraphTree(ctx, graph, en) {
|
|
|
568
572
|
for (const item of items) {
|
|
569
573
|
if (!item || typeof item.text !== "string" || !item.text.trim()) continue;
|
|
570
574
|
const mark = item.kind === "mustNot" ? (en ? "must not" : "하면 안 됨") : (en ? "must" : "있어야 함");
|
|
571
|
-
ctx.out(`${indent} ${ctx.ui.dim(`· [${mark}] ${item.text
|
|
575
|
+
ctx.out(`${indent} ${ctx.ui.dim(`· [${mark}] ${inlineText(item.text, 1000)}`)}`);
|
|
572
576
|
}
|
|
573
577
|
}
|
|
574
578
|
const edges = outgoing.get(nodeId) ?? [];
|
package/engine/commands/help.cjs
CHANGED
|
@@ -10,9 +10,19 @@
|
|
|
10
10
|
const catalog = require("../ui/commands-catalog.cjs");
|
|
11
11
|
|
|
12
12
|
function run(ctx, args = []) {
|
|
13
|
+
if (!Array.isArray(args) || args.length > 1) {
|
|
14
|
+
const error = new Error("usage: agentlas help [all|<command>]");
|
|
15
|
+
error.code = "INVALID_ARGUMENT";
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
13
18
|
const first = String((args && args[0]) || "").trim();
|
|
19
|
+
if (first.startsWith("-")) {
|
|
20
|
+
const error = new Error("usage: agentlas help [all|<command>]");
|
|
21
|
+
error.code = "INVALID_ARGUMENT";
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
14
24
|
const all = first.toLowerCase() === "all";
|
|
15
|
-
const name = !all && first
|
|
25
|
+
const name = !all && first ? first : null;
|
|
16
26
|
if (name) return runForCommand(ctx, name);
|
|
17
27
|
ctx.out(catalog.renderHelp({ lang: ctx.lang, surface: "cli", all }));
|
|
18
28
|
return 0;
|
package/engine/commands/hep.cjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
const { create, usageFor, isHelpToken } = require("../hephaestus/runtime.cjs");
|
|
14
14
|
|
|
15
15
|
async function run(ctx, args) {
|
|
16
|
-
if (args.length && args
|
|
16
|
+
if (args.length === 1 && isHelpToken(args[0])) {
|
|
17
17
|
ctx.out(usageFor("hep", ctx.lang));
|
|
18
18
|
return 0;
|
|
19
19
|
}
|
|
@@ -4,11 +4,12 @@ const { importLocalFolder } = require("../agents/import-local.cjs");
|
|
|
4
4
|
|
|
5
5
|
function run(ctx, args) {
|
|
6
6
|
const ko = ctx.lang === "ko";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
if (args.length !== 1) {
|
|
8
|
+
const error = new Error(ko ? "사용법: agentlas import <폴더-경로>" : "Usage: agentlas import <folder-path>");
|
|
9
|
+
error.code = "INVALID_ARGUMENT";
|
|
10
|
+
throw error;
|
|
11
11
|
}
|
|
12
|
+
const absPath = args[0];
|
|
12
13
|
let r;
|
|
13
14
|
try {
|
|
14
15
|
r = importLocalFolder(ctx.db(), absPath);
|
|
@@ -181,7 +181,7 @@ function resolveCommandName(cmd) {
|
|
|
181
181
|
* 인자로 읽는다(`agentlas run --help` 가 "help"라는 이름을 찾는 식). 추측하지 않는다.
|
|
182
182
|
* `test/command-help-contract.cjs` 가 이 목록과 실제 분기를 대조한다.
|
|
183
183
|
*/
|
|
184
|
-
const SELF_HELP_COMMANDS = new Set(["graph", "plugin", "billing", "roles", "native"]);
|
|
184
|
+
const SELF_HELP_COMMANDS = new Set(["graph", "plugin", "billing", "roles", "native", "research"]);
|
|
185
185
|
|
|
186
186
|
function dispatch(ctx, argv) {
|
|
187
187
|
const [rawCmd, ...rest] = argv;
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
const { installHubAgent } = require("../hub/install.cjs");
|
|
8
8
|
|
|
9
9
|
async function run(ctx, args) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
if (args.length !== 1 || String(args[0] || "").startsWith("-")) {
|
|
11
|
+
const error = new Error("usage: agentlas install <slug>");
|
|
12
|
+
error.code = "INVALID_ARGUMENT";
|
|
13
|
+
throw error;
|
|
14
14
|
}
|
|
15
|
+
const slug = String(args[0]).trim();
|
|
16
|
+
if (!slug) throw Object.assign(new Error("usage: agentlas install <slug>"), { code: "INVALID_ARGUMENT" });
|
|
15
17
|
let agent;
|
|
16
18
|
try {
|
|
17
19
|
agent = await installHubAgent(ctx.db(), slug);
|