agentlas 1.0.65 → 1.0.67
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 +94 -0
- package/README.md +12 -4
- package/bin/agentlas.cjs +354 -16
- package/engine/acp/server.cjs +34 -3
- 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 +30 -5
- package/engine/agentlas-workload-routing.cjs +83 -10
- package/engine/agentlas.cjs +21 -4
- 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 +96 -30
- 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 +609 -116
- 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 +72 -14
- 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 +150 -37
- 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 +35 -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 +484 -63
- 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 +46 -10
- package/engine/mcp/plan.cjs +10 -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 +382 -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
package/engine/commands/acp.cjs
CHANGED
|
@@ -28,14 +28,25 @@ function descriptor() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async function run(ctx, args) {
|
|
31
|
-
|
|
31
|
+
const descriptorMode = ctx.output?.format === "json" || (args.length === 1 && ["--info", "--json"].includes(args[0]));
|
|
32
|
+
if (descriptorMode) {
|
|
33
|
+
if (args.some((arg) => !["--info", "--json"].includes(arg)) || new Set(args).size !== args.length) {
|
|
34
|
+
const error = new Error("Usage: agentlas acp [--info]");
|
|
35
|
+
error.code = "INVALID_ARGUMENT";
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
32
38
|
ctx.out(JSON.stringify(descriptor(), null, 2));
|
|
33
39
|
return 0;
|
|
34
40
|
}
|
|
35
|
-
if (args.
|
|
41
|
+
if (args.length === 1 && ["--help", "-h", "help"].includes(args[0])) {
|
|
36
42
|
ctx.out("Usage: agentlas acp [--info]\n Speak the Agent Client Protocol (v1) on stdio so an editor can run Agentlas as its agent.");
|
|
37
43
|
return 0;
|
|
38
44
|
}
|
|
45
|
+
if (args.length) {
|
|
46
|
+
const error = new Error("Usage: agentlas acp [--info]");
|
|
47
|
+
error.code = "INVALID_ARGUMENT";
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
39
50
|
// stdout is the protocol wire from here on: route everything human to stderr.
|
|
40
51
|
const server = new AcpAgentServer({ ctx, input: process.stdin, output: process.stdout });
|
|
41
52
|
await server.start();
|
|
@@ -15,6 +15,39 @@ const schedule = require("../automation/schedule.cjs");
|
|
|
15
15
|
const store = require("../automation/store.cjs");
|
|
16
16
|
const daemon = require("../automation/daemon.cjs");
|
|
17
17
|
|
|
18
|
+
function parseOptions(args, schema) {
|
|
19
|
+
const values = new Set(schema.values || []);
|
|
20
|
+
const booleans = new Set(schema.booleans || []);
|
|
21
|
+
const out = {};
|
|
22
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
23
|
+
const token = String(args[i]);
|
|
24
|
+
if (!token.startsWith("--") || token === "--") throw new Error(`unexpected argument: ${token}`);
|
|
25
|
+
const at = token.indexOf("=");
|
|
26
|
+
const key = token.slice(2, at >= 0 ? at : undefined);
|
|
27
|
+
if (!values.has(key) && !booleans.has(key)) throw new Error(`unknown option: --${key}`);
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(out, key)) throw new Error(`duplicate option: --${key}`);
|
|
29
|
+
if (booleans.has(key)) {
|
|
30
|
+
if (at >= 0) throw new Error(`--${key} does not take a value`);
|
|
31
|
+
out[key] = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const value = at >= 0 ? token.slice(at + 1) : args[++i];
|
|
35
|
+
if (value === undefined || value === "" || (at < 0 && String(value).startsWith("--"))) {
|
|
36
|
+
throw new Error(`--${key} requires a value`);
|
|
37
|
+
}
|
|
38
|
+
out[key] = String(value);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function intervalOption(raw, minimum, label = "--interval") {
|
|
44
|
+
const parsed = Number(raw);
|
|
45
|
+
if (!Number.isInteger(parsed) || parsed < minimum) {
|
|
46
|
+
throw new Error(`${label} requires an integer of at least ${minimum} seconds`);
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
|
|
18
51
|
function resolveFirm(db, token) {
|
|
19
52
|
const q = String(token || "").trim();
|
|
20
53
|
if (!q) return null;
|
|
@@ -34,6 +67,7 @@ async function run(ctx, args) {
|
|
|
34
67
|
const fail = (msg) => { ctx.err(msg); return 1; };
|
|
35
68
|
|
|
36
69
|
if (sub === "list") {
|
|
70
|
+
if (args.length !== 1 && args.length !== 0) return fail(`unexpected argument: ${String(args[1])}`);
|
|
37
71
|
const rows = store.listAutomations(db);
|
|
38
72
|
if (!rows.length) {
|
|
39
73
|
ctx.out(ko
|
|
@@ -62,17 +96,14 @@ async function run(ctx, args) {
|
|
|
62
96
|
|
|
63
97
|
if (sub === "add") {
|
|
64
98
|
// agentlas automation add --name "..." --agent <slug>|--firm <slug> --cron "0 9 * * *" --prompt "..."
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
else if (a === "--tz") flags.tz = args[++i];
|
|
74
|
-
else if (a === "--disabled") flags.disabled = true;
|
|
75
|
-
}
|
|
99
|
+
let flags;
|
|
100
|
+
try {
|
|
101
|
+
flags = parseOptions(args.slice(1), {
|
|
102
|
+
values: ["name", "agent", "firm", "cron", "prompt", "tz"],
|
|
103
|
+
booleans: ["disabled"],
|
|
104
|
+
});
|
|
105
|
+
} catch (error) { return fail(String((error && error.message) || error)); }
|
|
106
|
+
if (flags.agent && flags.firm) return fail("use only one of --agent or --firm");
|
|
76
107
|
if (!flags.cron || !flags.prompt || (!flags.agent && !flags.firm)) {
|
|
77
108
|
ctx.err(ko
|
|
78
109
|
? '사용법: agentlas automation add --name "이름" --agent <슬러그>|--firm <슬러그> --cron "0 9 * * *" --prompt "지시" [--tz Asia/Seoul] [--disabled]'
|
|
@@ -128,9 +159,15 @@ async function run(ctx, args) {
|
|
|
128
159
|
if (sub === "on" || sub === "off") {
|
|
129
160
|
const idPrefix = args[1];
|
|
130
161
|
if (!idPrefix) return fail(usage(`${sub} <id>`));
|
|
162
|
+
if (args.length !== 2) return fail(`unexpected argument: ${String(args[2])}`);
|
|
131
163
|
const row = store.getAutomationByPrefix(db, idPrefix);
|
|
132
164
|
if (!row) return fail(missingAutomation(idPrefix));
|
|
133
|
-
store.
|
|
165
|
+
const outcome = store.setEnabledChecked(db, row, sub === "on");
|
|
166
|
+
if (!outcome.changed) {
|
|
167
|
+
return fail(ko
|
|
168
|
+
? `자동화가 변경되는 동안 사라졌습니다: ${row.id}`
|
|
169
|
+
: `Automation changed before it could be updated: ${row.id}`);
|
|
170
|
+
}
|
|
134
171
|
ctx.out(`${sub === "on" ? (ko ? "활성화됨" : "Enabled") : (ko ? "비활성화됨" : "Disabled")}: ${row.id.slice(0, 8)} ${row.name}`);
|
|
135
172
|
return 0;
|
|
136
173
|
}
|
|
@@ -138,14 +175,20 @@ async function run(ctx, args) {
|
|
|
138
175
|
if (sub === "remove" || sub === "rm") {
|
|
139
176
|
const idPrefix = args[1];
|
|
140
177
|
if (!idPrefix) return fail(usage("remove <id>"));
|
|
178
|
+
if (args.length !== 2) return fail(`unexpected argument: ${String(args[2])}`);
|
|
141
179
|
const row = store.getAutomationByPrefix(db, idPrefix);
|
|
142
180
|
if (!row) return fail(missingAutomation(idPrefix));
|
|
143
|
-
store.removeAutomation(db, row.id)
|
|
181
|
+
if (!store.removeAutomation(db, row.id)) {
|
|
182
|
+
return fail(ko
|
|
183
|
+
? `자동화가 삭제되는 동안 사라졌습니다: ${row.id}`
|
|
184
|
+
: `Automation changed before it could be deleted: ${row.id}`);
|
|
185
|
+
}
|
|
144
186
|
ctx.out(`${ko ? "삭제됨" : "Deleted"}: ${row.id.slice(0, 8)} ${row.name}`);
|
|
145
187
|
return 0;
|
|
146
188
|
}
|
|
147
189
|
|
|
148
190
|
if (sub === "runs") {
|
|
191
|
+
if (args.length !== 1) return fail(`unexpected argument: ${String(args[1])}`);
|
|
149
192
|
const rows = store.listRuns(db, 15);
|
|
150
193
|
if (!rows.length) {
|
|
151
194
|
ctx.out(ko ? "실행 기록이 없습니다." : "No run history.");
|
|
@@ -165,11 +208,11 @@ async function run(ctx, args) {
|
|
|
165
208
|
|
|
166
209
|
if (sub === "run") {
|
|
167
210
|
const idPrefix = args[1];
|
|
168
|
-
let runtimeOverride = null;
|
|
169
|
-
for (let i = 2; i < args.length; i++) {
|
|
170
|
-
if (args[i] === "--runtime") runtimeOverride = args[++i];
|
|
171
|
-
}
|
|
172
211
|
if (!idPrefix) return fail(usage("run <id>"));
|
|
212
|
+
let runFlags;
|
|
213
|
+
try { runFlags = parseOptions(args.slice(2), { values: ["runtime"], booleans: [] }); }
|
|
214
|
+
catch (error) { return fail(String((error && error.message) || error)); }
|
|
215
|
+
const runtimeOverride = runFlags.runtime || null;
|
|
173
216
|
const row = store.getAutomationByPrefix(db, idPrefix);
|
|
174
217
|
if (!row) return fail(missingAutomation(idPrefix));
|
|
175
218
|
// run-now는 스케줄을 건드리지 않는다 (앱의 advanceSchedule=false와 동일).
|
|
@@ -190,17 +233,19 @@ async function run(ctx, args) {
|
|
|
190
233
|
}
|
|
191
234
|
|
|
192
235
|
if (sub === "daemon") {
|
|
236
|
+
let daemonFlags;
|
|
237
|
+
try { daemonFlags = parseOptions(args.slice(1), { values: ["interval", "runtime"], booleans: [] }); }
|
|
238
|
+
catch (error) { return fail(String((error && error.message) || error)); }
|
|
193
239
|
let interval = 30;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
else if (args[i] === "--runtime") runtimeOverride = args[++i];
|
|
198
|
-
}
|
|
240
|
+
try { if (daemonFlags.interval !== undefined) interval = intervalOption(daemonFlags.interval, 10); }
|
|
241
|
+
catch (error) { return fail(String((error && error.message) || error)); }
|
|
242
|
+
const runtimeOverride = daemonFlags.runtime || null;
|
|
199
243
|
return daemon.automationDaemon(ctx, db, { intervalSec: interval, runtimeOverride });
|
|
200
244
|
}
|
|
201
245
|
|
|
202
246
|
// 1회 due 스윕 후 종료 — launchd/cron 이 poke 하는 진입점(상주 루프 아님).
|
|
203
247
|
if (sub === "tick") {
|
|
248
|
+
if (args.length !== 1) return fail(`unexpected argument: ${String(args[1])}`);
|
|
204
249
|
await daemon.daemonTick(ctx, db, {});
|
|
205
250
|
return 0;
|
|
206
251
|
}
|
|
@@ -209,6 +254,7 @@ async function run(ctx, args) {
|
|
|
209
254
|
if (sub === "install" || sub === "uninstall" || sub === "status") {
|
|
210
255
|
const launchd = require("../automation/launchd.cjs");
|
|
211
256
|
if (sub === "status") {
|
|
257
|
+
if (args.length !== 1) return fail(`unexpected argument: ${String(args[1])}`);
|
|
212
258
|
const st = launchd.launchdStatus();
|
|
213
259
|
if (!st.supported) { ctx.out(ko ? "launchd 상주는 macOS 전용입니다. 다른 OS 는 `agentlas automation daemon` 을 켜 두세요." : "launchd persistence is macOS-only. On other systems keep `agentlas automation daemon` running."); return 0; }
|
|
214
260
|
ctx.out(`${st.loaded ? ctx.ui.green("✓") : ctx.ui.dim("○")} ${ko ? "상주(launchd)" : "persistence (launchd)"}: ${st.loaded ? (ko ? "실행 중" : "loaded") : st.installed ? (ko ? "설치됨(미로드)" : "installed (not loaded)") : (ko ? "미설치" : "not installed")}`);
|
|
@@ -217,8 +263,12 @@ async function run(ctx, args) {
|
|
|
217
263
|
return 0;
|
|
218
264
|
}
|
|
219
265
|
if (sub === "install") {
|
|
266
|
+
let installFlags;
|
|
267
|
+
try { installFlags = parseOptions(args.slice(1), { values: ["interval"], booleans: [] }); }
|
|
268
|
+
catch (error) { return fail(String((error && error.message) || error)); }
|
|
220
269
|
let interval = 300;
|
|
221
|
-
|
|
270
|
+
try { if (installFlags.interval !== undefined) interval = intervalOption(installFlags.interval, 30); }
|
|
271
|
+
catch (error) { return fail(String((error && error.message) || error)); }
|
|
222
272
|
const st = launchd.enableLaunchd({ intervalSec: interval });
|
|
223
273
|
if (st.error) { ctx.err(`${ctx.ui.red("✖")} ${st.error}`); return 1; }
|
|
224
274
|
ctx.out(`${ctx.ui.green("✓")} ${ko ? "상주를 켰습니다 — 앱/창이 꺼져 있어도 자동화가 발동합니다" : "persistence on — automations fire even with the app/window closed"} (${interval}s)`);
|
|
@@ -226,6 +276,7 @@ async function run(ctx, args) {
|
|
|
226
276
|
return 0;
|
|
227
277
|
}
|
|
228
278
|
// uninstall
|
|
279
|
+
if (args.length !== 1) return fail(`unexpected argument: ${String(args[1])}`);
|
|
229
280
|
const st = launchd.disableLaunchd();
|
|
230
281
|
if (st.error) { ctx.err(`${ctx.ui.red("✖")} ${st.error}`); return 1; }
|
|
231
282
|
ctx.out(`${ctx.ui.green("✓")} ${ko ? "상주를 껐습니다. 자동화는 포그라운드 daemon 을 켜 둘 때만 발동합니다." : "persistence off. Automations fire only while a foreground daemon runs."}`);
|
|
@@ -236,4 +287,4 @@ async function run(ctx, args) {
|
|
|
236
287
|
return 1;
|
|
237
288
|
}
|
|
238
289
|
|
|
239
|
-
module.exports = { run };
|
|
290
|
+
module.exports = { run, parseOptions, intervalOption };
|
|
@@ -36,10 +36,15 @@ function usage(ko) {
|
|
|
36
36
|
|
|
37
37
|
async function run(ctx, args) {
|
|
38
38
|
const ko = ctx.lang === "ko";
|
|
39
|
-
if (args.
|
|
39
|
+
if (args.length === 1 && ["--help", "-h", "help"].includes(args[0])) {
|
|
40
40
|
ctx.out(usage(ko));
|
|
41
41
|
return 0;
|
|
42
42
|
}
|
|
43
|
+
if (args.length) {
|
|
44
|
+
const error = new Error(usage(ko).split("\n")[0]);
|
|
45
|
+
error.code = "INVALID_ARGUMENT";
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
43
48
|
|
|
44
49
|
const cookie = await cloudSessionCookie();
|
|
45
50
|
if (!cookie) {
|
|
@@ -26,11 +26,40 @@ const cdp = require("../browser/cdp.cjs");
|
|
|
26
26
|
|
|
27
27
|
const SUBS = new Set(["status", "sites", "add", "login", "mark", "go", "rm", "remove"]);
|
|
28
28
|
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
|
|
29
|
+
function browserArgumentError(message) {
|
|
30
|
+
const error = new Error(message);
|
|
31
|
+
error.code = "INVALID_ARGUMENT";
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseBrowserRest(rest, { values = [], positionals, usage }) {
|
|
36
|
+
const allowedValues = new Set(values);
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
const parsed = { _: [] };
|
|
39
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
40
|
+
const token = String(rest[index]);
|
|
41
|
+
if (!token.startsWith("-")) {
|
|
42
|
+
parsed._.push(token);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (!token.startsWith("--")) throw browserArgumentError(usage);
|
|
46
|
+
const equal = token.indexOf("=");
|
|
47
|
+
const key = token.slice(2, equal === -1 ? undefined : equal);
|
|
48
|
+
if (!allowedValues.has(key) || seen.has(key)) throw browserArgumentError(usage);
|
|
49
|
+
let value;
|
|
50
|
+
if (equal !== -1) value = token.slice(equal + 1);
|
|
51
|
+
else {
|
|
52
|
+
value = rest[index + 1];
|
|
53
|
+
if (value === undefined || String(value).startsWith("-")) throw browserArgumentError(usage);
|
|
54
|
+
index += 1;
|
|
55
|
+
}
|
|
56
|
+
if (!String(value)) throw browserArgumentError(usage);
|
|
57
|
+
seen.add(key);
|
|
58
|
+
parsed[key] = String(value);
|
|
59
|
+
}
|
|
60
|
+
if (parsed._.length !== positionals) throw browserArgumentError(usage);
|
|
61
|
+
return parsed;
|
|
32
62
|
}
|
|
33
|
-
function positional(rest) { return rest.find((a) => a && !String(a).startsWith("-")) || null; }
|
|
34
63
|
|
|
35
64
|
function siteUrl(site) { return /^[a-z][a-z0-9+.-]*:\/\//i.test(site) ? site : `https://${site}`; }
|
|
36
65
|
|
|
@@ -70,12 +99,11 @@ function browserSites(ctx) {
|
|
|
70
99
|
return 0;
|
|
71
100
|
}
|
|
72
101
|
|
|
73
|
-
function browserAdd(ctx,
|
|
102
|
+
function browserAdd(ctx, parsed) {
|
|
74
103
|
const ko = ctx.lang === "ko";
|
|
75
|
-
const site =
|
|
76
|
-
if (!site) { ctx.err(ko ? "사용법: agentlas browser add <site> [--label 이름] [--user 아이디]" : "Usage: agentlas browser add <site> [--label name] [--user handle]"); return 1; }
|
|
104
|
+
const site = parsed._[0];
|
|
77
105
|
try {
|
|
78
|
-
const row = vault.upsertBrowserSite(ctx.db(), { site, label:
|
|
106
|
+
const row = vault.upsertBrowserSite(ctx.db(), { site, label: parsed.label || null, username: parsed.user || null });
|
|
79
107
|
vault.logBrowserAction(ctx.db(), { site: row.site, action: "vault.save", result: "ok" });
|
|
80
108
|
ctx.out(`${ctx.ui.green("✓")} ${ko ? "사이트를 저장했습니다" : "site saved"}: ${row.site}`);
|
|
81
109
|
ctx.out(ctx.ui.dim(ko ? `로그인: agentlas browser login ${row.site}` : `Log in: agentlas browser login ${row.site}`));
|
|
@@ -83,10 +111,8 @@ function browserAdd(ctx, rest) {
|
|
|
83
111
|
} catch (e) { ctx.err(`${ctx.ui.red("✖")} ${String((e && e.message) || e)}`); return 1; }
|
|
84
112
|
}
|
|
85
113
|
|
|
86
|
-
async function browserLogin(ctx,
|
|
114
|
+
async function browserLogin(ctx, site) {
|
|
87
115
|
const ko = ctx.lang === "ko";
|
|
88
|
-
const site = positional(rest);
|
|
89
|
-
if (!site) { ctx.err(ko ? "사용법: agentlas browser login <site>" : "Usage: agentlas browser login <site>"); return 1; }
|
|
90
116
|
const db = ctx.db();
|
|
91
117
|
let row;
|
|
92
118
|
try { row = vault.upsertBrowserSite(db, { site }); } catch (e) { ctx.err(`${ctx.ui.red("✖")} ${String((e && e.message) || e)}`); return 1; }
|
|
@@ -117,11 +143,8 @@ async function browserLogin(ctx, rest) {
|
|
|
117
143
|
return 0;
|
|
118
144
|
}
|
|
119
145
|
|
|
120
|
-
function browserMark(ctx,
|
|
146
|
+
function browserMark(ctx, site, status) {
|
|
121
147
|
const ko = ctx.lang === "ko";
|
|
122
|
-
const site = positional(rest);
|
|
123
|
-
const status = rest.filter((a) => a && !String(a).startsWith("-") && a !== site)[0];
|
|
124
|
-
if (!site || !status) { ctx.err(ko ? "사용법: agentlas browser mark <site> <valid|expired|none>" : "Usage: agentlas browser mark <site> <valid|expired|none>"); return 1; }
|
|
125
148
|
try {
|
|
126
149
|
const row = vault.setBrowserSession(ctx.db(), site, status);
|
|
127
150
|
vault.logBrowserAction(ctx.db(), { site: row.site, action: "session.mark", result: status });
|
|
@@ -130,10 +153,8 @@ function browserMark(ctx, rest) {
|
|
|
130
153
|
} catch (e) { ctx.err(`${ctx.ui.red("✖")} ${String((e && e.message) || e)}`); return 1; }
|
|
131
154
|
}
|
|
132
155
|
|
|
133
|
-
async function browserGo(ctx,
|
|
156
|
+
async function browserGo(ctx, url) {
|
|
134
157
|
const ko = ctx.lang === "ko";
|
|
135
|
-
const url = positional(rest);
|
|
136
|
-
if (!url) { ctx.err(ko ? "사용법: agentlas browser go <url>" : "Usage: agentlas browser go <url>"); return 1; }
|
|
137
158
|
if (!(await cdp.cdpReady())) {
|
|
138
159
|
ctx.err(ko
|
|
139
160
|
? `Agentlas 브라우저가 실행 중이 아닙니다. 먼저 열어 주세요: agentlas browser ${siteUrl(url)}`
|
|
@@ -149,10 +170,8 @@ async function browserGo(ctx, rest) {
|
|
|
149
170
|
} catch (e) { ctx.err(`${ctx.ui.red("✖")} ${String((e && e.message) || e)}`); return 1; }
|
|
150
171
|
}
|
|
151
172
|
|
|
152
|
-
function browserRemove(ctx,
|
|
173
|
+
function browserRemove(ctx, site) {
|
|
153
174
|
const ko = ctx.lang === "ko";
|
|
154
|
-
const site = positional(rest);
|
|
155
|
-
if (!site) { ctx.err(ko ? "사용법: agentlas browser rm <site>" : "Usage: agentlas browser rm <site>"); return 1; }
|
|
156
175
|
vault.deleteBrowserSite(ctx.db(), site);
|
|
157
176
|
vault.logBrowserAction(ctx.db(), { site: vault.normalizeSite(site), action: "vault.delete", result: "ok" });
|
|
158
177
|
ctx.out(`${ctx.ui.green("✓")} ${ko ? "사이트를 삭제했습니다" : "site removed"}: ${vault.normalizeSite(site) || site}`);
|
|
@@ -160,17 +179,30 @@ function browserRemove(ctx, rest) {
|
|
|
160
179
|
}
|
|
161
180
|
|
|
162
181
|
async function run(ctx, args) {
|
|
163
|
-
if (args.
|
|
182
|
+
if (args.length === 1 && isHelpToken(args[0])) { ctx.out(usageFor("browser", ctx.lang)); return 0; }
|
|
164
183
|
const sub = String(args[0] || "").toLowerCase();
|
|
165
184
|
const rest = args.slice(1);
|
|
166
185
|
if (SUBS.has(sub)) {
|
|
167
|
-
if (sub === "status"
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (sub === "
|
|
172
|
-
|
|
173
|
-
|
|
186
|
+
if (sub === "status" || sub === "sites") {
|
|
187
|
+
parseBrowserRest(rest, { positionals: 0, usage: `Usage: agentlas browser ${sub}` });
|
|
188
|
+
return sub === "status" ? browserStatus(ctx) : browserSites(ctx);
|
|
189
|
+
}
|
|
190
|
+
if (sub === "add") {
|
|
191
|
+
const parsed = parseBrowserRest(rest, {
|
|
192
|
+
values: ["label", "user"], positionals: 1,
|
|
193
|
+
usage: "Usage: agentlas browser add <site> [--label name] [--user handle]",
|
|
194
|
+
});
|
|
195
|
+
return browserAdd(ctx, parsed);
|
|
196
|
+
}
|
|
197
|
+
if (sub === "mark") {
|
|
198
|
+
const parsed = parseBrowserRest(rest, { positionals: 2, usage: "Usage: agentlas browser mark <site> <valid|expired|none>" });
|
|
199
|
+
return browserMark(ctx, parsed._[0], parsed._[1]);
|
|
200
|
+
}
|
|
201
|
+
const usage = `Usage: agentlas browser ${sub} <${sub === "go" ? "url" : "site"}>`;
|
|
202
|
+
const parsed = parseBrowserRest(rest, { positionals: 1, usage });
|
|
203
|
+
if (sub === "login") return browserLogin(ctx, parsed._[0]);
|
|
204
|
+
if (sub === "go") return browserGo(ctx, parsed._[0]);
|
|
205
|
+
return browserRemove(ctx, parsed._[0]);
|
|
174
206
|
}
|
|
175
207
|
// 그 외(무인자·URL·검색어)는 v1 그대로 hep-browser 하드포인트로 — Chrome 을 실제로 띄운다.
|
|
176
208
|
return create(ctx).cmdHep(["hep-browser", ...args]);
|
|
@@ -23,24 +23,72 @@ const fs = require("node:fs");
|
|
|
23
23
|
|
|
24
24
|
function usage(ko) {
|
|
25
25
|
return ko
|
|
26
|
-
? "사용법: agentlas build \"<만들고 싶은 에이전트>\" [--runtime <kind>] [--print]"
|
|
27
|
-
: "Usage: agentlas build \"<the agent you want>\" [--runtime <kind>] [--print]";
|
|
26
|
+
? "사용법: agentlas build \"<만들고 싶은 에이전트>\" [--runtime <kind>] [--print] [-- <옵션처럼 시작하는 요청>]"
|
|
27
|
+
: "Usage: agentlas build \"<the agent you want>\" [--runtime <kind>] [--print] [-- <request starting like an option>]";
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
30
|
+
function buildError(message, code = "INVALID_ARGUMENT") {
|
|
31
|
+
const error = new Error(message);
|
|
32
|
+
error.code = code;
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
const flags = {};
|
|
36
|
+
function parseArgs(args) {
|
|
37
|
+
const flags = { runtime: null, print: false };
|
|
36
38
|
const rest = [];
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
let passthrough = false;
|
|
37
41
|
for (let i = 0; i < args.length; i += 1) {
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
|
|
42
|
+
const token = String(args[i]);
|
|
43
|
+
if (passthrough) { rest.push(token); continue; }
|
|
44
|
+
if (token === "--") { passthrough = true; continue; }
|
|
45
|
+
if (token === "--print" || token === "-p") {
|
|
46
|
+
if (seen.has("print")) throw new Error("duplicate option: --print");
|
|
47
|
+
seen.add("print");
|
|
48
|
+
flags.print = true;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (token === "--runtime" || token.startsWith("--runtime=")) {
|
|
52
|
+
if (seen.has("runtime")) throw new Error("duplicate option: --runtime");
|
|
53
|
+
seen.add("runtime");
|
|
54
|
+
const inline = token.startsWith("--runtime=");
|
|
55
|
+
const value = inline ? token.slice(10) : args[++i];
|
|
56
|
+
if (value === undefined || value === "" || (!inline && String(value).startsWith("--"))) {
|
|
57
|
+
throw new Error("--runtime requires a value");
|
|
58
|
+
}
|
|
59
|
+
flags.runtime = String(value);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (token.startsWith("-")) {
|
|
63
|
+
throw new Error(`unknown option: ${token} (use -- before a request that starts with '-')`);
|
|
64
|
+
}
|
|
65
|
+
rest.push(token);
|
|
66
|
+
}
|
|
67
|
+
return { flags, rest };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function run(ctx, args) {
|
|
71
|
+
const ko = ctx.lang === "ko";
|
|
72
|
+
if (args.length === 1 && ["--help", "-h", "help"].includes(args[0])) { ctx.out(usage(ko)); return 0; }
|
|
73
|
+
|
|
74
|
+
let parsed;
|
|
75
|
+
try { parsed = parseArgs(args); }
|
|
76
|
+
catch (error) {
|
|
77
|
+
const typed = error && typeof error === "object" && typeof error.code === "string"
|
|
78
|
+
? error
|
|
79
|
+
: buildError(String((error && error.message) || error));
|
|
80
|
+
if (typeof ctx.fail === "function") ctx.fail(typed);
|
|
81
|
+
else ctx.err(String(typed.message || typed));
|
|
82
|
+
return 1;
|
|
41
83
|
}
|
|
84
|
+
const { flags, rest } = parsed;
|
|
42
85
|
const request = rest.join(" ").trim();
|
|
43
|
-
if (!request) {
|
|
86
|
+
if (!request) {
|
|
87
|
+
const error = buildError("✖ " + usage(ko));
|
|
88
|
+
if (typeof ctx.fail === "function") ctx.fail(error);
|
|
89
|
+
else ctx.err(error.message);
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
44
92
|
|
|
45
93
|
const db = ctx.db();
|
|
46
94
|
const cwd = projectCwd();
|
|
@@ -53,7 +101,12 @@ async function run(ctx, args) {
|
|
|
53
101
|
});
|
|
54
102
|
} catch (e) {
|
|
55
103
|
// no_runtime 등 정직 정지 그대로.
|
|
56
|
-
|
|
104
|
+
const error = e && typeof e === "object" && typeof e.message === "string"
|
|
105
|
+
? e
|
|
106
|
+
: buildError(String(e), "RUNTIME_RESOLUTION_FAILED");
|
|
107
|
+
if (typeof error.code !== "string") error.code = "RUNTIME_RESOLUTION_FAILED";
|
|
108
|
+
if (typeof ctx.fail === "function") ctx.fail(error);
|
|
109
|
+
else ctx.err(error.message);
|
|
57
110
|
return 1;
|
|
58
111
|
}
|
|
59
112
|
|
|
@@ -77,7 +130,12 @@ async function run(ctx, args) {
|
|
|
77
130
|
if (flags.print && finalText) process.stdout.write(finalText.trimEnd() + "\n");
|
|
78
131
|
|
|
79
132
|
if (session.status === "failed") {
|
|
80
|
-
|
|
133
|
+
const error = session.lastError && typeof session.lastError === "object" && typeof session.lastError.message === "string"
|
|
134
|
+
? session.lastError
|
|
135
|
+
: buildError(String(session.lastError || (ko ? "빌드 실행에 실패했습니다." : "The build session failed.")), "BUILD_FAILED");
|
|
136
|
+
if (typeof error.code !== "string") error.code = "BUILD_FAILED";
|
|
137
|
+
if (typeof ctx.fail === "function") ctx.fail(error);
|
|
138
|
+
else ctx.err(error.message);
|
|
81
139
|
return 1;
|
|
82
140
|
}
|
|
83
141
|
|
|
@@ -111,4 +169,4 @@ async function run(ctx, args) {
|
|
|
111
169
|
}
|
|
112
170
|
}
|
|
113
171
|
|
|
114
|
-
module.exports = { run };
|
|
172
|
+
module.exports = { run, parseArgs };
|
package/engine/commands/call.cjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
const { create, usageFor, isHelpToken } = require("../hephaestus/runtime.cjs");
|
|
11
11
|
|
|
12
12
|
async function run(ctx, args) {
|
|
13
|
-
if (args.
|
|
13
|
+
if (args.length === 1 && isHelpToken(args[0])) {
|
|
14
14
|
ctx.out(usageFor("call", ctx.lang));
|
|
15
15
|
return 0;
|
|
16
16
|
}
|
package/engine/commands/cd.cjs
CHANGED
|
@@ -5,7 +5,7 @@ const { agentFolder } = require("../agents/files.cjs");
|
|
|
5
5
|
|
|
6
6
|
function run(ctx, args) {
|
|
7
7
|
const ko = ctx.lang === "ko";
|
|
8
|
-
if (!args[0]) {
|
|
8
|
+
if (!args[0] || args.length !== 1) {
|
|
9
9
|
ctx.err(ko ? "사용법: agentlas cd <agent>" : "Usage: agentlas cd <agent>");
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
@@ -12,7 +12,8 @@ async function run(ctx, args) {
|
|
|
12
12
|
: args;
|
|
13
13
|
return await runCloud(ctx, commandArgs);
|
|
14
14
|
} catch (e) {
|
|
15
|
-
ctx.
|
|
15
|
+
if (typeof ctx.fail === "function") ctx.fail(e);
|
|
16
|
+
else ctx.err(String((e && e.message) || e));
|
|
16
17
|
return 1;
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -43,10 +43,21 @@ function resolveTarget(db, token) {
|
|
|
43
43
|
/** 봇 토큰을 stdin 에서 읽는다(비밀은 argv 금지). 파이프면 첫 줄, TTY면 숨김 안내. */
|
|
44
44
|
async function readTokenFromStdin(ctx, ko) {
|
|
45
45
|
if (!process.stdin.isTTY) {
|
|
46
|
-
return new Promise((resolve) => {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
47
|
let buf = "";
|
|
48
|
-
|
|
49
|
-
process.stdin.on("
|
|
48
|
+
let tooLarge = false;
|
|
49
|
+
process.stdin.on("data", (d) => {
|
|
50
|
+
if (tooLarge) return;
|
|
51
|
+
buf += d;
|
|
52
|
+
if (Buffer.byteLength(buf, "utf8") > 2048) tooLarge = true;
|
|
53
|
+
});
|
|
54
|
+
process.stdin.on("end", () => {
|
|
55
|
+
if (tooLarge) return reject(new Error("Telegram bot token input is too large"));
|
|
56
|
+
const value = buf.replace(/\r?\n$/, "");
|
|
57
|
+
if (/[\r\n]/.test(value)) return reject(new Error("Telegram bot token input must contain exactly one line"));
|
|
58
|
+
resolve(value.trim());
|
|
59
|
+
});
|
|
60
|
+
process.stdin.on("error", reject);
|
|
50
61
|
});
|
|
51
62
|
}
|
|
52
63
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -95,7 +106,11 @@ async function connectTelegram(ctx, targetToken, { auto = false } = {}) {
|
|
|
95
106
|
}
|
|
96
107
|
ctx.out(ctx.ui.dim(ko ? "토큰을 읽었습니다." : "Captured the token."));
|
|
97
108
|
} else {
|
|
98
|
-
token = await readTokenFromStdin(ctx, ko);
|
|
109
|
+
try { token = await readTokenFromStdin(ctx, ko); }
|
|
110
|
+
catch (error) {
|
|
111
|
+
ctx.err(`${ctx.ui.red("✖")} ${String((error && error.message) || error)}`);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
99
114
|
if (!token) { ctx.err(ko ? "토큰이 필요합니다." : "a bot token is required."); return 1; }
|
|
100
115
|
}
|
|
101
116
|
|
|
@@ -145,16 +160,21 @@ async function run(ctx, args) {
|
|
|
145
160
|
const sub = String(args[0] || "").toLowerCase();
|
|
146
161
|
|
|
147
162
|
if (!sub || sub === "help" || sub === "--help" || sub === "-h") { ctx.out(usage(ko)); return 0; }
|
|
148
|
-
if (sub === "status")
|
|
163
|
+
if (sub === "status") {
|
|
164
|
+
if (args.length !== 1) { ctx.err(usage(ko)); return 1; }
|
|
165
|
+
return renderTelegram(ctx);
|
|
166
|
+
}
|
|
149
167
|
if (sub === "telegram") {
|
|
150
168
|
const rest = args.slice(1);
|
|
169
|
+
const unknownOptions = rest.filter((item) => String(item).startsWith("-") && item !== "--auto");
|
|
170
|
+
const positionals = rest.filter((item) => item && !String(item).startsWith("-"));
|
|
171
|
+
if (unknownOptions.length || positionals.length !== 1) { ctx.err(usage(ko)); return 1; }
|
|
151
172
|
const auto = rest.includes("--auto");
|
|
152
|
-
const targetToken =
|
|
153
|
-
if (!targetToken) { ctx.err(usage(ko)); return 1; }
|
|
173
|
+
const targetToken = positionals[0];
|
|
154
174
|
return connectTelegram(ctx, targetToken, { auto });
|
|
155
175
|
}
|
|
156
176
|
if (sub === "pair") {
|
|
157
|
-
if (!args[1]) { ctx.err(ko ? "사용법: agentlas connect pair <id>" : "Usage: agentlas connect pair <id>"); return 1; }
|
|
177
|
+
if (!args[1] || args.length !== 2) { ctx.err(ko ? "사용법: agentlas connect pair <id>" : "Usage: agentlas connect pair <id>"); return 1; }
|
|
158
178
|
const row = tg.getBinding(ctx.db(), args[1]);
|
|
159
179
|
if (!row) { ctx.err(ko ? "연결을 찾을 수 없습니다." : "connection not found."); return 1; }
|
|
160
180
|
if (row.telegram_chat_id) {
|
|
@@ -181,7 +201,7 @@ async function run(ctx, args) {
|
|
|
181
201
|
}
|
|
182
202
|
}
|
|
183
203
|
if (sub === "test") {
|
|
184
|
-
if (!args[1]) { ctx.err(ko ? "사용법: agentlas connect test <id>" : "Usage: agentlas connect test <id>"); return 1; }
|
|
204
|
+
if (!args[1] || args.length !== 2) { ctx.err(ko ? "사용법: agentlas connect test <id>" : "Usage: agentlas connect test <id>"); return 1; }
|
|
185
205
|
try {
|
|
186
206
|
await tg.sendTest(ctx.db(), args[1], ko ? "Agentlas 테스트 메시지" : "Agentlas test message");
|
|
187
207
|
ctx.out(`${ctx.ui.green("✓")} ${ko ? "메시지를 보냈습니다." : "message sent."}`);
|
|
@@ -189,10 +209,15 @@ async function run(ctx, args) {
|
|
|
189
209
|
} catch (e) { ctx.err(`${ctx.ui.red("✖")} ${String((e && e.message) || e)}`); return 1; }
|
|
190
210
|
}
|
|
191
211
|
if (sub === "remove") {
|
|
192
|
-
if (!args[1]) { ctx.err(ko ? "사용법: agentlas connect remove <id>" : "Usage: agentlas connect remove <id>"); return 1; }
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
212
|
+
if (!args[1] || args.length !== 2) { ctx.err(ko ? "사용법: agentlas connect remove <id>" : "Usage: agentlas connect remove <id>"); return 1; }
|
|
213
|
+
try {
|
|
214
|
+
tg.removeBinding(ctx.db(), args[1]);
|
|
215
|
+
ctx.out(`${ctx.ui.green("✓")} ${ko ? "연결을 제거했습니다." : "connection removed."}`);
|
|
216
|
+
return 0;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
ctx.err(`${ctx.ui.red("✖")} ${String((error && error.message) || error)}`);
|
|
219
|
+
return 1;
|
|
220
|
+
}
|
|
196
221
|
}
|
|
197
222
|
ctx.err(usage(ko));
|
|
198
223
|
return 1;
|