@controlflow-ai/daemon 0.1.1 → 0.1.3
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/README.md +66 -24
- package/package.json +16 -3
- package/src/agent-avatar.ts +30 -0
- package/src/agent-key.ts +28 -0
- package/src/agent-permissions.ts +359 -0
- package/src/agent-runtime.ts +810 -28
- package/src/agent-workspace.ts +183 -0
- package/src/app.ts +2183 -79
- package/src/args.ts +54 -7
- package/src/cli.ts +873 -14
- package/src/client.ts +482 -12
- package/src/coco.ts +9 -40
- package/src/codex.ts +33 -5
- package/src/config.ts +28 -4
- package/src/console.ts +460 -26
- package/src/daemon-client.ts +116 -3
- package/src/daemon.ts +958 -101
- package/src/db.ts +3216 -113
- package/src/delivery-ws.ts +269 -0
- package/src/format.ts +4 -1
- package/src/lark/app-registration.ts +141 -0
- package/src/lark/cli.ts +7 -137
- package/src/lark/credentials.ts +36 -3
- package/src/lark/event-router.ts +61 -5
- package/src/lark/inbound-events.ts +156 -3
- package/src/lark/server-integration.ts +659 -111
- package/src/lark/setup.ts +74 -5
- package/src/lark/ws-daemon.ts +136 -10
- package/src/local-api.ts +611 -14
- package/src/local-auth.ts +36 -3
- package/src/message-attachments.ts +71 -0
- package/src/messaging-cli.ts +741 -0
- package/src/messaging-status.ts +669 -0
- package/src/migrations/023_projects.ts +65 -0
- package/src/migrations/024_agents_model.ts +10 -0
- package/src/migrations/025_room_archive.ts +44 -0
- package/src/migrations/026_project_archive.ts +44 -0
- package/src/migrations/027_agent_permission_profiles.ts +16 -0
- package/src/migrations/028_lark_websocket_restart_state.ts +16 -0
- package/src/migrations/029_held_message_drafts.ts +32 -0
- package/src/migrations/030_agent_room_read_state.ts +25 -0
- package/src/migrations/031_room_tasks.ts +29 -0
- package/src/migrations/032_room_reminders.ts +29 -0
- package/src/migrations/033_room_saved_messages.ts +25 -0
- package/src/migrations/034_agent_activity_events.ts +27 -0
- package/src/migrations/035_agent_avatars.ts +17 -0
- package/src/migrations/036_project_agent_defaults.ts +21 -0
- package/src/migrations/037_message_attachments.ts +36 -0
- package/src/migrations/038_agent_activity_room_scope.ts +64 -0
- package/src/migrations/039_message_attachments_path.ts +34 -0
- package/src/migrations/040_message_attachments_file_schema.ts +80 -0
- package/src/migrations/041_room_system_events.ts +30 -0
- package/src/migrations/042_message_attachment_file_kind.ts +52 -0
- package/src/migrations/043_room_mode_skill_registry.ts +92 -0
- package/src/migrations/044_workflow_runtime.ts +69 -0
- package/src/migrations/045_skill_repository_ownership.ts +64 -0
- package/src/migrations.ts +70 -1
- package/src/neeko.ts +40 -4
- package/src/runtime-env.ts +179 -0
- package/src/runtime-registry.ts +83 -13
- package/src/server.ts +244 -4
- package/src/token-file.ts +13 -6
- package/src/types.ts +394 -0
- package/src/workflow-runtime.ts +275 -0
- package/src/web.ts +0 -904
package/src/console.ts
CHANGED
|
@@ -4,7 +4,12 @@ import { boolFlag, parseArgs, flag, numberFlag } from './args.js';
|
|
|
4
4
|
import { LockClient } from './client.js';
|
|
5
5
|
import { defaultServerUrl } from './config.js';
|
|
6
6
|
import { formatMessages } from './format.js';
|
|
7
|
+
import { defaultLarkConfigPath, findCredentialByAgent, loadLarkCredentials, saveLarkCredentials, unbindCredentialAgent, upsertCredential } from './lark/credentials.js';
|
|
8
|
+
import { registerLarkAppFromDeviceFlow, resolveLarkBotInfo, type LarkBotInfoResult } from './lark/setup.js';
|
|
9
|
+
import { runtimeModelOptions, validateRuntimeModel } from './runtime-registry.js';
|
|
10
|
+
import type { LarkRegistrationComplete } from './lark/app-registration.js';
|
|
7
11
|
import type { Computer, ProvisionedComputer } from './types.js';
|
|
12
|
+
import { runMessagingCommand } from './messaging-cli.js';
|
|
8
13
|
|
|
9
14
|
interface Prompt {
|
|
10
15
|
askLine(label: string): Promise<string>;
|
|
@@ -23,14 +28,28 @@ Usage:
|
|
|
23
28
|
bun run src/console.ts run-action <run-id> kill|restart
|
|
24
29
|
bun run src/console.ts computers list [--json]
|
|
25
30
|
bun run src/console.ts computer onboard [--interactive] [--name <display-name>] [--server-url <url>] [--package-name <npm-package>]
|
|
31
|
+
bun run src/console.ts computers reconnect-command [--interactive] [--computer-id <machine>] [--server-url <url>] [--package-name <npm-package>] [--json]
|
|
32
|
+
bun run src/console.ts computers delete [--interactive] [--computer-id <machine>] [--json]
|
|
26
33
|
bun run src/console.ts agents list [--json]
|
|
27
|
-
bun run src/console.ts agents onboard [--interactive] [--key <agent-key>] [--name <display-name>] [--runtime codex] [--computer-id <machine>]
|
|
28
|
-
bun run src/console.ts agents create --key <agent-key> --name <display-name> [--runtime neeko|coco|
|
|
29
|
-
bun run src/console.ts agents update --key <agent-key> --runtime neeko|coco|
|
|
34
|
+
bun run src/console.ts agents onboard [--interactive] [--key <agent-key>] [--name <display-name>] [--runtime codex] [--model <model>] [--computer-id <machine>]
|
|
35
|
+
bun run src/console.ts agents create --key <agent-key> --name <display-name> [--runtime neeko|coco|codex] [--model <model>] [--desc <description>]
|
|
36
|
+
bun run src/console.ts agents update [--interactive] --key <agent-key> [--runtime neeko|coco|codex] [--model <model>]
|
|
37
|
+
[--lark-app-id <id> --lark-app-secret <secret>] [--lark-label <name>] [--lark-config <path>] [--rebind-lark] [--unbind-lark] [--no-reload]
|
|
38
|
+
bun run src/console.ts agents delete --key <agent-key> [--yes] [--lark-config <path>] [--no-reload] [--json]
|
|
30
39
|
bun run src/console.ts lark-users list [--json]
|
|
31
40
|
bun run src/console.ts lark-users add [--interactive] --user-id <union-id> [--name <display-name>]
|
|
32
41
|
bun run src/console.ts lark-users delete --user-id <union-id>
|
|
33
|
-
bun run src/console.ts
|
|
42
|
+
bun run src/console.ts messaging health [--json]
|
|
43
|
+
bun run src/console.ts messaging status [--verbose] [--json]
|
|
44
|
+
bun run src/console.ts messaging restart-lark [--app-id <id>] [--json]
|
|
45
|
+
bun run src/console.ts messaging repair-lark [--dry-run] [--json]
|
|
46
|
+
bun run src/console.ts messaging doctor-lark [--app-id <id>] [--config <path>] [--budget-ms 10000] [--json]
|
|
47
|
+
bun run src/console.ts messaging watch-lark [--app-id <id>] [--timeout-ms 60000] [--interval-ms 1000] [--json]
|
|
48
|
+
bun run src/console.ts messaging verify-lark-ingress [--app-id <id>] [--timeout-ms 60000] [--interval-ms 1000] [--json]
|
|
49
|
+
bun run src/console.ts messaging recent-lark [--limit 20] [--json]
|
|
50
|
+
bun run src/console.ts messaging probe-lark --app-id <id> --sender-user-id <union-id> --chat-id <chat-id> [--mention-open-id <open-id>] [--text <message>] [--json]
|
|
51
|
+
bun run src/console.ts messaging probe-delivery [--agent lock] [--room <room>] [--timeout-ms 120000] [--json]
|
|
52
|
+
bun run src/console.ts lark <list|daemon|events|send> [flags]
|
|
34
53
|
|
|
35
54
|
Environment:
|
|
36
55
|
PAL_SERVER=${defaultServerUrl()}
|
|
@@ -44,6 +63,83 @@ async function requestJson<T>(url: string, init?: RequestInit): Promise<T> {
|
|
|
44
63
|
return payload.data as T;
|
|
45
64
|
}
|
|
46
65
|
|
|
66
|
+
async function reloadLarkIntegration(serverUrl: string): Promise<void> {
|
|
67
|
+
const response = await fetch(`${serverUrl.replace(/\/$/, '')}/api/lark/reload`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'content-type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({}),
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const text = await response.text();
|
|
74
|
+
console.warn(`Lark bot config saved, but server reload failed (${response.status}): ${text}`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log('Lark integration reloaded.');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function resolveBotInfoForConsole(appId: string, appSecret: string, options: { budgetMs?: number } = {}): Promise<LarkBotInfoResult> {
|
|
81
|
+
if (process.env.NODE_ENV === 'test' && process.env.PAL_TEST_LARK_BOT_OPEN_ID) {
|
|
82
|
+
return { ok: true, openId: process.env.PAL_TEST_LARK_BOT_OPEN_ID };
|
|
83
|
+
}
|
|
84
|
+
return resolveLarkBotInfo(appId, appSecret, options);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function registerLarkAppForConsole(): Promise<LarkRegistrationComplete | null> {
|
|
88
|
+
if (process.env.NODE_ENV === 'test' && process.env.PAL_TEST_LARK_REGISTRATION_APP_ID && process.env.PAL_TEST_LARK_REGISTRATION_APP_SECRET) {
|
|
89
|
+
console.log('Create a Feishu app by scanning this QR code or opening the link:');
|
|
90
|
+
console.log('https://example.test/lark-registration');
|
|
91
|
+
return {
|
|
92
|
+
appId: process.env.PAL_TEST_LARK_REGISTRATION_APP_ID,
|
|
93
|
+
appSecret: process.env.PAL_TEST_LARK_REGISTRATION_APP_SECRET,
|
|
94
|
+
tenantBrand: 'feishu',
|
|
95
|
+
userOpenId: process.env.PAL_TEST_LARK_REGISTRATION_USER_OPEN_ID,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return registerLarkAppFromDeviceFlow({ log: console });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function larkConfigPath(flags: Record<string, string | boolean>): string {
|
|
102
|
+
return flag(flags, 'lark-config') ?? flag(flags, 'config') ?? defaultLarkConfigPath();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function unbindLarkBotFromAgent(serverClient: LockClient, flags: Record<string, string | boolean>, agentKey: string): Promise<void> {
|
|
106
|
+
const configPath = larkConfigPath(flags);
|
|
107
|
+
const store = loadLarkCredentials(configPath);
|
|
108
|
+
const result = unbindCredentialAgent(store, agentKey);
|
|
109
|
+
if (!result.changed) {
|
|
110
|
+
console.log(`Agent ${agentKey} has no Lark bot binding.`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
saveLarkCredentials(result.store, configPath);
|
|
114
|
+
console.log(`Lark bot ${result.unbound!.appId} unbound from agent ${agentKey}.`);
|
|
115
|
+
if (!boolFlag(flags, 'no-reload')) await reloadLarkIntegration(serverClient.baseUrl);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function bindLarkBotToAgent(serverClient: LockClient, flags: Record<string, string | boolean>, agentKey: string): Promise<void> {
|
|
119
|
+
const appId = flag(flags, 'lark-app-id') ?? flag(flags, 'app-id');
|
|
120
|
+
const appSecret = flag(flags, 'lark-app-secret') ?? flag(flags, 'app-secret');
|
|
121
|
+
if (!appId && !appSecret) return;
|
|
122
|
+
if (!appId || !appSecret) throw new Error('--lark-app-id and --lark-app-secret are required together');
|
|
123
|
+
|
|
124
|
+
const botInfo = await resolveBotInfoForConsole(appId, appSecret);
|
|
125
|
+
if (!botInfo.ok) throw new Error(`could not resolve Lark bot open_id (${botInfo.error}): ${botInfo.message}`);
|
|
126
|
+
|
|
127
|
+
const configPath = larkConfigPath(flags);
|
|
128
|
+
const store = loadLarkCredentials(configPath);
|
|
129
|
+
const result = upsertCredential(store, {
|
|
130
|
+
appId,
|
|
131
|
+
appSecret,
|
|
132
|
+
label: flag(flags, 'lark-label') ?? flag(flags, 'label'),
|
|
133
|
+
agent: agentKey,
|
|
134
|
+
botOpenId: botInfo.openId,
|
|
135
|
+
}, { rebind: boolFlag(flags, 'rebind-lark') });
|
|
136
|
+
saveLarkCredentials(result.store, configPath);
|
|
137
|
+
|
|
138
|
+
const moved = result.unbound.length > 0 ? `; moved from ${result.unbound.map((bot) => bot.appId).join(', ')}` : '';
|
|
139
|
+
console.log(`Lark bot ${appId} bound to agent ${agentKey}${moved}.`);
|
|
140
|
+
if (!boolFlag(flags, 'no-reload')) await reloadLarkIntegration(serverClient.baseUrl);
|
|
141
|
+
}
|
|
142
|
+
|
|
47
143
|
function printJson(value: unknown): void {
|
|
48
144
|
console.log(JSON.stringify(value, null, 2));
|
|
49
145
|
}
|
|
@@ -86,7 +182,7 @@ async function askRequired(prompt: Prompt, label: string, defaultValue = ''): Pr
|
|
|
86
182
|
}
|
|
87
183
|
|
|
88
184
|
async function askRuntime(prompt: Prompt, defaultValue: string): Promise<string> {
|
|
89
|
-
const runtimes = ['codex', 'neeko', 'coco'
|
|
185
|
+
const runtimes = ['codex', 'neeko', 'coco'];
|
|
90
186
|
const fallback = runtimes.includes(defaultValue) ? defaultValue : 'codex';
|
|
91
187
|
console.log('Runtime:');
|
|
92
188
|
runtimes.forEach((runtime, index) => {
|
|
@@ -102,6 +198,29 @@ async function askRuntime(prompt: Prompt, defaultValue: string): Promise<string>
|
|
|
102
198
|
}
|
|
103
199
|
}
|
|
104
200
|
|
|
201
|
+
async function askModel(prompt: Prompt, runtime: string, defaultValue = ''): Promise<string> {
|
|
202
|
+
const models = await runtimeModelOptions(runtime);
|
|
203
|
+
if (models.length === 0) {
|
|
204
|
+
console.log(`Model: ${runtime} has no built-in model list; using runtime default.`);
|
|
205
|
+
return '';
|
|
206
|
+
}
|
|
207
|
+
const fallback = models.includes(defaultValue) ? defaultValue : models[0]!;
|
|
208
|
+
console.log('Model:');
|
|
209
|
+
console.log(' 0. Runtime default');
|
|
210
|
+
models.forEach((model, index) => {
|
|
211
|
+
const marker = model === fallback ? ' (default)' : '';
|
|
212
|
+
console.log(` ${index + 1}. ${model}${marker}`);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
while (true) {
|
|
216
|
+
const answer = await ask(prompt, `Choose ${runtime} model`, fallback);
|
|
217
|
+
if (answer === '0') return '';
|
|
218
|
+
const selected = models[Number(answer) - 1] ?? answer;
|
|
219
|
+
if (models.includes(selected)) return selected;
|
|
220
|
+
console.log(`Model must be one of: ${models.join(', ')}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
105
224
|
async function askYesNo(prompt: Prompt, label: string, defaultValue = false): Promise<boolean> {
|
|
106
225
|
const fallback = defaultValue ? 'Y/n' : 'y/N';
|
|
107
226
|
while (true) {
|
|
@@ -132,12 +251,35 @@ async function askComputerId(prompt: Prompt, computers: Computer[]): Promise<str
|
|
|
132
251
|
}
|
|
133
252
|
}
|
|
134
253
|
|
|
254
|
+
async function listAgentRecords(serverClient: LockClient): Promise<Array<Record<string, unknown>>> {
|
|
255
|
+
const response = await fetch(`${serverClient.baseUrl}/api/agents`);
|
|
256
|
+
const payload = await response.json() as { data?: { agents: Array<Record<string, unknown>> }; message?: string };
|
|
257
|
+
if (!response.ok) throw new Error(payload.message ?? `list failed: ${response.status}`);
|
|
258
|
+
return payload.data?.agents ?? [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function askAgentKey(prompt: Prompt, agents: Array<Record<string, unknown>>, defaultValue = ''): Promise<string> {
|
|
262
|
+
if (agents.length === 0) throw new Error('No agents found. Set up an agent first with "bun run console -- agents onboard".');
|
|
263
|
+
|
|
264
|
+
console.log('Agents:');
|
|
265
|
+
agents.forEach((agent, index) => {
|
|
266
|
+
console.log(` ${index + 1}. ${agent.display_name} (${agent.agent_key}) runtime=${agent.runtime ?? '-'}`);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
while (true) {
|
|
270
|
+
const answer = await ask(prompt, 'Choose agent', defaultValue);
|
|
271
|
+
const selected = agents[Number(answer) - 1] ?? agents.find((agent) => agent.agent_key === answer);
|
|
272
|
+
if (selected?.agent_key) return String(selected.agent_key);
|
|
273
|
+
console.log('Choose a listed number or agent key.');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
135
277
|
async function collectComputerOnboardInput(serverClient: LockClient, flags: Record<string, string | boolean>): Promise<{ name: string; serverUrl: string; packageName: string }> {
|
|
136
278
|
const hasAnyProvisionFlag = Boolean(flag(flags, 'name') || flag(flags, 'server-url') || flag(flags, 'package-name'));
|
|
137
279
|
const interactive = boolFlag(flags, 'interactive') || (!hasAnyProvisionFlag && process.stdin.isTTY === true);
|
|
138
280
|
const defaultName = flag(flags, 'name') ?? 'Local computer';
|
|
139
281
|
const defaultServerUrl = flag(flags, 'server-url') ?? serverClient.baseUrl;
|
|
140
|
-
const defaultPackageName = flag(flags, 'package-name') ?? '
|
|
282
|
+
const defaultPackageName = flag(flags, 'package-name') ?? 'bun run daemon';
|
|
141
283
|
|
|
142
284
|
if (!interactive) {
|
|
143
285
|
return {
|
|
@@ -153,12 +295,12 @@ async function collectComputerOnboardInput(serverClient: LockClient, flags: Reco
|
|
|
153
295
|
console.log('Provision a computer credential and daemon start command.');
|
|
154
296
|
const name = await askRequired(prompt, 'Computer display name', defaultName);
|
|
155
297
|
const serverUrl = await askRequired(prompt, 'Server URL', defaultServerUrl);
|
|
156
|
-
const packageName = await askRequired(prompt, 'Daemon package', defaultPackageName);
|
|
298
|
+
const packageName = await askRequired(prompt, 'Daemon command/package', defaultPackageName);
|
|
157
299
|
console.log('');
|
|
158
300
|
console.log('Summary:');
|
|
159
301
|
console.log(` name: ${name}`);
|
|
160
302
|
console.log(` server_url: ${serverUrl}`);
|
|
161
|
-
console.log(`
|
|
303
|
+
console.log(` command_prefix: ${packageName}`);
|
|
162
304
|
if (!await askYesNo(prompt, 'Provision this computer?', true)) throw new Error('computer onboarding cancelled');
|
|
163
305
|
return { name, serverUrl, packageName };
|
|
164
306
|
} finally {
|
|
@@ -176,7 +318,56 @@ function printProvisionedComputer(provisioned: ProvisionedComputer): void {
|
|
|
176
318
|
console.log(provisioned.command);
|
|
177
319
|
}
|
|
178
320
|
|
|
179
|
-
async function
|
|
321
|
+
async function collectLarkBindFlags(prompt: Prompt, flags: Record<string, string | boolean>, agentKey: string, existingAppId?: string): Promise<Record<string, string | boolean>> {
|
|
322
|
+
const configPath = larkConfigPath(flags);
|
|
323
|
+
console.log('Lark bot setup');
|
|
324
|
+
console.log(' 1. Scan or open link to create app');
|
|
325
|
+
console.log(' 2. Paste App ID / App Secret');
|
|
326
|
+
const method = await ask(prompt, 'Setup method', '1');
|
|
327
|
+
|
|
328
|
+
let appId = flag(flags, 'lark-app-id') ?? flag(flags, 'app-id');
|
|
329
|
+
let appSecret = flag(flags, 'lark-app-secret') ?? flag(flags, 'app-secret');
|
|
330
|
+
let scannedUserOpenId: string | undefined;
|
|
331
|
+
if (method !== '2' && (!appId || !appSecret)) {
|
|
332
|
+
const registration = await registerLarkAppForConsole().catch((error) => {
|
|
333
|
+
console.warn(`App registration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
334
|
+
return null;
|
|
335
|
+
});
|
|
336
|
+
if (registration?.tenantBrand === 'lark') {
|
|
337
|
+
throw new Error('Lark international tenants are not supported yet; use a Feishu tenant');
|
|
338
|
+
}
|
|
339
|
+
if (registration) {
|
|
340
|
+
appId = registration.appId;
|
|
341
|
+
appSecret = registration.appSecret;
|
|
342
|
+
scannedUserOpenId = registration.userOpenId;
|
|
343
|
+
console.log(`App created: ${appId}`);
|
|
344
|
+
} else {
|
|
345
|
+
console.warn('Falling back to manual App ID / App Secret entry.');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
appId = appId || await askRequired(prompt, 'Lark App ID', existingAppId ?? '');
|
|
350
|
+
appSecret = appSecret || await askRequired(prompt, 'Lark App Secret');
|
|
351
|
+
const label = await ask(prompt, 'Bot label (optional)', flag(flags, 'lark-label') ?? flag(flags, 'label') ?? '');
|
|
352
|
+
if (scannedUserOpenId) {
|
|
353
|
+
console.log(`Scanner open_id ${scannedUserOpenId} was detected; add it under Lark authorized users if this user should operate Pal.`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const isChangingBot = Boolean(existingAppId && existingAppId !== appId);
|
|
357
|
+
const rebind = boolFlag(flags, 'rebind-lark') || (isChangingBot && await askYesNo(prompt, `Move ${agentKey} from ${existingAppId} to ${appId}?`, false));
|
|
358
|
+
if (isChangingBot && !rebind) throw new Error('Lark bot rebind cancelled');
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
...flags,
|
|
362
|
+
'lark-app-id': appId,
|
|
363
|
+
'lark-app-secret': appSecret,
|
|
364
|
+
'lark-config': configPath,
|
|
365
|
+
...(label ? { 'lark-label': label } : {}),
|
|
366
|
+
...(rebind ? { 'rebind-lark': true } : {}),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function collectOnboardInput(serverClient: LockClient, flags: Record<string, string | boolean>): Promise<{ agentKey: string; displayName: string; runtime: string; model: string | null; desc: string | null; computerId: string | undefined; larkFlags?: Record<string, string | boolean> }> {
|
|
180
371
|
const hasRequiredFlags = Boolean(flag(flags, 'key') && flag(flags, 'name'));
|
|
181
372
|
const interactive = boolFlag(flags, 'interactive') || (!hasRequiredFlags && process.stdin.isTTY === true);
|
|
182
373
|
if (!interactive) {
|
|
@@ -185,8 +376,10 @@ async function collectOnboardInput(serverClient: LockClient, flags: Record<strin
|
|
|
185
376
|
agentKey: flag(flags, 'key')!,
|
|
186
377
|
displayName: flag(flags, 'name')!,
|
|
187
378
|
runtime: flag(flags, 'runtime') ?? 'codex',
|
|
379
|
+
model: await validateRuntimeModel(flag(flags, 'runtime') ?? 'codex', flag(flags, 'model')),
|
|
188
380
|
desc: flag(flags, 'desc') ?? null,
|
|
189
381
|
computerId: flag(flags, 'computer-id'),
|
|
382
|
+
larkFlags: flag(flags, 'lark-app-id') || flag(flags, 'app-id') ? flags : undefined,
|
|
190
383
|
};
|
|
191
384
|
}
|
|
192
385
|
|
|
@@ -197,6 +390,7 @@ async function collectOnboardInput(serverClient: LockClient, flags: Record<strin
|
|
|
197
390
|
const agentKey = await askRequired(prompt, 'Agent key', flag(flags, 'key') ?? 'codex');
|
|
198
391
|
const displayName = await askRequired(prompt, 'Display name', flag(flags, 'name') ?? agentKey);
|
|
199
392
|
const runtime = await askRuntime(prompt, flag(flags, 'runtime') ?? 'codex');
|
|
393
|
+
const model = await askModel(prompt, runtime, flag(flags, 'model') ?? '');
|
|
200
394
|
const desc = await ask(prompt, 'Description (optional)', flag(flags, 'desc') ?? '');
|
|
201
395
|
const shouldAssign = Boolean(flag(flags, 'computer-id')) || await askYesNo(prompt, 'Assign this agent to a computer?', false);
|
|
202
396
|
const computerId = shouldAssign
|
|
@@ -207,21 +401,152 @@ async function collectOnboardInput(serverClient: LockClient, flags: Record<strin
|
|
|
207
401
|
console.log(` agent_key: ${agentKey}`);
|
|
208
402
|
console.log(` display_name: ${displayName}`);
|
|
209
403
|
console.log(` runtime: ${runtime}`);
|
|
404
|
+
console.log(` model: ${model || '-'}`);
|
|
210
405
|
console.log(` description: ${desc || '-'}`);
|
|
211
406
|
console.log(` computer_id: ${computerId || '-'}`);
|
|
212
407
|
if (!await askYesNo(prompt, 'Create/update this agent?', true)) throw new Error('onboarding cancelled');
|
|
408
|
+
const shouldSetupLark = await askYesNo(prompt, 'Set up a Lark bot for this agent?', false);
|
|
409
|
+
const larkFlags = shouldSetupLark ? await collectLarkBindFlags(prompt, flags, agentKey) : undefined;
|
|
213
410
|
return {
|
|
214
411
|
agentKey,
|
|
215
412
|
displayName,
|
|
216
413
|
runtime,
|
|
414
|
+
model: model || null,
|
|
217
415
|
desc: desc || null,
|
|
218
416
|
computerId: computerId || undefined,
|
|
417
|
+
larkFlags,
|
|
418
|
+
};
|
|
419
|
+
} finally {
|
|
420
|
+
prompt.close();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function collectAgentUpdateInput(serverClient: LockClient, flags: Record<string, string | boolean>): Promise<{ agentKey: string; flags: Record<string, string | boolean> }> {
|
|
425
|
+
const prompt = await createPrompt();
|
|
426
|
+
try {
|
|
427
|
+
console.log('Agent settings');
|
|
428
|
+
const agents = await listAgentRecords(serverClient);
|
|
429
|
+
const agentKey = await askAgentKey(prompt, agents, flag(flags, 'key') ?? (agents.length === 1 ? '1' : ''));
|
|
430
|
+
const agent = agents.find((candidate) => candidate.agent_key === agentKey);
|
|
431
|
+
if (!agent) throw new Error(`agent ${agentKey} not found`);
|
|
432
|
+
|
|
433
|
+
if (flag(flags, 'model')) return { agentKey, flags };
|
|
434
|
+
|
|
435
|
+
const requestedLarkUpdate = Boolean(
|
|
436
|
+
boolFlag(flags, 'unbind-lark') ||
|
|
437
|
+
flag(flags, 'lark-app-id') ||
|
|
438
|
+
flag(flags, 'app-id') ||
|
|
439
|
+
flag(flags, 'lark-app-secret') ||
|
|
440
|
+
flag(flags, 'app-secret'),
|
|
441
|
+
);
|
|
442
|
+
let updateAction = requestedLarkUpdate ? (boolFlag(flags, 'unbind-lark') ? '3' : '2') : '';
|
|
443
|
+
if (!updateAction) {
|
|
444
|
+
console.log('Update:');
|
|
445
|
+
console.log(' 1. Model');
|
|
446
|
+
console.log(' 2. Bind/rebind Lark bot');
|
|
447
|
+
console.log(' 3. Unbind Lark bot');
|
|
448
|
+
updateAction = await ask(prompt, 'Choose update', '1');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (updateAction === '1' || updateAction.toLowerCase() === 'model') {
|
|
452
|
+
const runtime = typeof agent.runtime === 'string' ? agent.runtime : '';
|
|
453
|
+
if (!runtime) throw new Error(`agent ${agentKey} has no runtime configured`);
|
|
454
|
+
const model = await askModel(prompt, runtime, typeof agent.model === 'string' ? agent.model : '');
|
|
455
|
+
console.log('');
|
|
456
|
+
console.log('Summary:');
|
|
457
|
+
console.log(` agent_key: ${agentKey}`);
|
|
458
|
+
console.log(` runtime: ${runtime}`);
|
|
459
|
+
console.log(` model: ${model || '-'}`);
|
|
460
|
+
if (!await askYesNo(prompt, 'Update this agent model?', true)) throw new Error('agent update cancelled');
|
|
461
|
+
return { agentKey, flags: { ...flags, model } };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (updateAction !== '2' && updateAction !== '3') throw new Error('Choose 1, 2, or 3.');
|
|
465
|
+
|
|
466
|
+
const configPath = larkConfigPath(flags);
|
|
467
|
+
const store = loadLarkCredentials(configPath);
|
|
468
|
+
const existing = findCredentialByAgent(store, agentKey);
|
|
469
|
+
if (existing) {
|
|
470
|
+
console.log(`Current Lark bot: ${existing.label ?? existing.appId} (${existing.appId})`);
|
|
471
|
+
} else {
|
|
472
|
+
console.log('Current Lark bot: -');
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (updateAction === '3') {
|
|
476
|
+
if (!existing) throw new Error(`agent ${agentKey} has no Lark bot binding`);
|
|
477
|
+
if (!await askYesNo(prompt, `Unbind ${existing.appId} from ${agentKey}?`, false)) throw new Error('Lark bot unbind cancelled');
|
|
478
|
+
return {
|
|
479
|
+
agentKey,
|
|
480
|
+
flags: {
|
|
481
|
+
...flags,
|
|
482
|
+
'lark-config': configPath,
|
|
483
|
+
'unbind-lark': true,
|
|
484
|
+
},
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const defaultAction = '1';
|
|
489
|
+
console.log('Lark action:');
|
|
490
|
+
console.log(` 1. ${existing ? 'Bind/rebind bot' : 'Bind bot'}`);
|
|
491
|
+
if (existing) console.log(' 2. Unbind bot');
|
|
492
|
+
const action = await ask(prompt, 'Choose action', defaultAction);
|
|
493
|
+
if (existing && (action === '2' || action.toLowerCase() === 'unbind')) {
|
|
494
|
+
if (!await askYesNo(prompt, `Unbind ${existing.appId} from ${agentKey}?`, false)) throw new Error('Lark bot unbind cancelled');
|
|
495
|
+
return {
|
|
496
|
+
agentKey,
|
|
497
|
+
flags: {
|
|
498
|
+
...flags,
|
|
499
|
+
'lark-config': configPath,
|
|
500
|
+
'unbind-lark': true,
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const larkFlags = await collectLarkBindFlags(prompt, flags, agentKey, existing?.appId);
|
|
506
|
+
|
|
507
|
+
console.log('');
|
|
508
|
+
console.log('Summary:');
|
|
509
|
+
console.log(` agent_key: ${agentKey}`);
|
|
510
|
+
console.log(` lark_app_id: ${flag(larkFlags, 'lark-app-id')}`);
|
|
511
|
+
console.log(` lark_label: ${flag(larkFlags, 'lark-label') || '-'}`);
|
|
512
|
+
console.log(` rebind: ${boolFlag(larkFlags, 'rebind-lark') ? 'yes' : 'no'}`);
|
|
513
|
+
if (!await askYesNo(prompt, 'Bind this Lark bot?', true)) throw new Error('Lark bot binding cancelled');
|
|
514
|
+
|
|
515
|
+
return {
|
|
516
|
+
agentKey,
|
|
517
|
+
flags: larkFlags,
|
|
219
518
|
};
|
|
220
519
|
} finally {
|
|
221
520
|
prompt.close();
|
|
222
521
|
}
|
|
223
522
|
}
|
|
224
523
|
|
|
524
|
+
async function collectAgentDeleteInput(serverClient: LockClient, flags: Record<string, string | boolean>): Promise<string> {
|
|
525
|
+
const agents = await listAgentRecords(serverClient);
|
|
526
|
+
const explicitKey = flag(flags, 'key') ?? flag(flags, 'agent');
|
|
527
|
+
const interactive = boolFlag(flags, 'interactive') || (!explicitKey && process.stdin.isTTY === true);
|
|
528
|
+
if (!interactive) {
|
|
529
|
+
if (!explicitKey) throw new Error('agents delete requires --key when not running interactively');
|
|
530
|
+
if (!boolFlag(flags, 'yes') && !boolFlag(flags, 'force')) throw new Error('agents delete requires --yes to confirm non-interactively');
|
|
531
|
+
return explicitKey;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const prompt = await createPrompt();
|
|
535
|
+
try {
|
|
536
|
+
console.log('Delete agent');
|
|
537
|
+
const agentKey = await askAgentKey(prompt, agents, explicitKey ?? (agents.length === 1 ? '1' : ''));
|
|
538
|
+
const agent = agents.find((candidate) => candidate.agent_key === agentKey);
|
|
539
|
+
console.log('');
|
|
540
|
+
console.log('This will remove the agent record, computer assignment, room memberships, room delivery subscriptions, provider bindings, and active deliveries.');
|
|
541
|
+
console.log(` agent_key: ${agentKey}`);
|
|
542
|
+
console.log(` display_name: ${agent?.display_name ?? '-'}`);
|
|
543
|
+
if (!await askYesNo(prompt, `Delete ${agentKey}?`, false)) throw new Error('agent delete cancelled');
|
|
544
|
+
return agentKey;
|
|
545
|
+
} finally {
|
|
546
|
+
prompt.close();
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
225
550
|
async function collectLarkUserInput(flags: Record<string, string | boolean>): Promise<{ userId: string; displayName: string | null }> {
|
|
226
551
|
const userIdFlag = flag(flags, 'user-id') ?? flag(flags, 'id');
|
|
227
552
|
const interactive = boolFlag(flags, 'interactive') || (!userIdFlag && process.stdin.isTTY === true);
|
|
@@ -359,6 +684,52 @@ async function main(): Promise<void> {
|
|
|
359
684
|
return;
|
|
360
685
|
}
|
|
361
686
|
|
|
687
|
+
if (sub === 'reconnect-command') {
|
|
688
|
+
let computerId = flag(args.flags, 'computer-id');
|
|
689
|
+
const interactive = boolFlag(args.flags, 'interactive') || (!computerId && process.stdin.isTTY === true);
|
|
690
|
+
if (interactive) {
|
|
691
|
+
const prompt = await createPrompt();
|
|
692
|
+
try {
|
|
693
|
+
computerId = await askComputerId(prompt, await serverClient.listComputers());
|
|
694
|
+
} finally {
|
|
695
|
+
prompt.close();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (!computerId) throw new Error('--computer-id is required');
|
|
699
|
+
const provisioned = await serverClient.regenerateComputerCommand(computerId, {
|
|
700
|
+
server_url: flag(args.flags, 'server-url') ?? serverClient.baseUrl,
|
|
701
|
+
package_name: flag(args.flags, 'package-name') ?? 'bun run daemon',
|
|
702
|
+
});
|
|
703
|
+
if (args.flags.json) {
|
|
704
|
+
printJson(provisioned);
|
|
705
|
+
} else {
|
|
706
|
+
console.log('Computer reconnect command generated');
|
|
707
|
+
printProvisionedComputer(provisioned);
|
|
708
|
+
}
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (sub === 'delete') {
|
|
713
|
+
let computerId = flag(args.flags, 'computer-id');
|
|
714
|
+
const interactive = boolFlag(args.flags, 'interactive') || (!computerId && process.stdin.isTTY === true);
|
|
715
|
+
if (interactive) {
|
|
716
|
+
const prompt = await createPrompt();
|
|
717
|
+
try {
|
|
718
|
+
computerId = await askComputerId(prompt, await serverClient.listComputers());
|
|
719
|
+
} finally {
|
|
720
|
+
prompt.close();
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if (!computerId) throw new Error('--computer-id is required');
|
|
724
|
+
const result = await serverClient.deleteComputer(computerId);
|
|
725
|
+
if (args.flags.json) {
|
|
726
|
+
printJson(result);
|
|
727
|
+
} else {
|
|
728
|
+
console.log(`Computer deleted: ${result.computer.id}`);
|
|
729
|
+
}
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
|
|
362
733
|
throw new Error(`unknown ${args.command} subcommand: ${sub ?? '(none)'}\n\n${usage()}`);
|
|
363
734
|
}
|
|
364
735
|
|
|
@@ -366,15 +737,12 @@ async function main(): Promise<void> {
|
|
|
366
737
|
const sub = args.values[0];
|
|
367
738
|
|
|
368
739
|
if (sub === 'list') {
|
|
369
|
-
const
|
|
370
|
-
const payload = await response.json() as { data?: { agents: Array<Record<string, unknown>> }; message?: string };
|
|
371
|
-
if (!response.ok) throw new Error(payload.message ?? `list failed: ${response.status}`);
|
|
372
|
-
const agents = payload.data?.agents ?? [];
|
|
740
|
+
const agents = await listAgentRecords(serverClient);
|
|
373
741
|
if (args.flags.json) {
|
|
374
742
|
printJson(agents);
|
|
375
743
|
} else {
|
|
376
744
|
for (const agent of agents) {
|
|
377
|
-
console.log(`${agent.agent_key} name="${agent.display_name}" runtime=${agent.runtime ?? '-'} desc=${agent.description ?? '-'}`);
|
|
745
|
+
console.log(`${agent.agent_key} name="${agent.display_name}" runtime=${agent.runtime ?? '-'} model=${agent.model ?? '-'} desc=${agent.description ?? '-'}`);
|
|
378
746
|
}
|
|
379
747
|
}
|
|
380
748
|
return;
|
|
@@ -384,6 +752,7 @@ async function main(): Promise<void> {
|
|
|
384
752
|
const agentKey = flag(args.flags, 'key');
|
|
385
753
|
const displayName = flag(args.flags, 'name');
|
|
386
754
|
const runtime = flag(args.flags, 'runtime');
|
|
755
|
+
const model = flag(args.flags, 'model');
|
|
387
756
|
const desc = flag(args.flags, 'desc');
|
|
388
757
|
if (!agentKey) throw new Error('--key is required');
|
|
389
758
|
if (!displayName) throw new Error('--name is required');
|
|
@@ -395,6 +764,7 @@ async function main(): Promise<void> {
|
|
|
395
764
|
agent_key: agentKey,
|
|
396
765
|
display_name: displayName,
|
|
397
766
|
runtime: runtime ?? null,
|
|
767
|
+
model: await validateRuntimeModel(runtime ?? '', model),
|
|
398
768
|
description: desc ?? null,
|
|
399
769
|
}),
|
|
400
770
|
});
|
|
@@ -405,7 +775,7 @@ async function main(): Promise<void> {
|
|
|
405
775
|
}
|
|
406
776
|
|
|
407
777
|
if (sub === 'onboard') {
|
|
408
|
-
const { agentKey, displayName, runtime, desc, computerId } = await collectOnboardInput(serverClient, args.flags);
|
|
778
|
+
const { agentKey, displayName, runtime, model, desc, computerId, larkFlags } = await collectOnboardInput(serverClient, args.flags);
|
|
409
779
|
if (!agentKey) throw new Error('agent key is required');
|
|
410
780
|
if (!displayName) throw new Error('display name is required');
|
|
411
781
|
|
|
@@ -416,30 +786,89 @@ async function main(): Promise<void> {
|
|
|
416
786
|
agent_key: agentKey,
|
|
417
787
|
display_name: displayName,
|
|
418
788
|
runtime,
|
|
789
|
+
model,
|
|
419
790
|
description: desc,
|
|
420
791
|
computer_id: computerId,
|
|
421
792
|
}),
|
|
422
793
|
});
|
|
423
794
|
const payload = await response.json() as { data?: Record<string, unknown>; message?: string };
|
|
424
795
|
if (!response.ok) throw new Error(payload.message ?? `onboard failed: ${response.status}`);
|
|
796
|
+
if (larkFlags) await bindLarkBotToAgent(serverClient, larkFlags, agentKey);
|
|
425
797
|
printJson(payload.data);
|
|
426
798
|
return;
|
|
427
799
|
}
|
|
428
800
|
|
|
429
801
|
if (sub === 'update') {
|
|
430
|
-
const
|
|
431
|
-
|
|
802
|
+
const updateInput = boolFlag(args.flags, 'interactive')
|
|
803
|
+
? await collectAgentUpdateInput(serverClient, args.flags)
|
|
804
|
+
: { agentKey: flag(args.flags, 'key') ?? '', flags: args.flags };
|
|
805
|
+
const agentKey = updateInput.agentKey;
|
|
806
|
+
const updateFlags = updateInput.flags;
|
|
807
|
+
const runtime = flag(updateFlags, 'runtime');
|
|
808
|
+
const hasModel = Object.prototype.hasOwnProperty.call(updateFlags, 'model');
|
|
809
|
+
const model = flag(updateFlags, 'model');
|
|
432
810
|
if (!agentKey) throw new Error('--key is required');
|
|
433
|
-
|
|
811
|
+
const hasLarkBinding = Boolean(flag(updateFlags, 'lark-app-id') || flag(updateFlags, 'app-id') || flag(updateFlags, 'lark-app-secret') || flag(updateFlags, 'app-secret'));
|
|
812
|
+
const hasLarkUnbind = boolFlag(updateFlags, 'unbind-lark');
|
|
813
|
+
if (!runtime && !hasModel && !hasLarkBinding && !hasLarkUnbind) throw new Error('--runtime, --model, --lark-app-id/--lark-app-secret, or --unbind-lark is required');
|
|
814
|
+
|
|
815
|
+
let agent: Record<string, unknown> | undefined;
|
|
816
|
+
if (runtime || hasModel) {
|
|
817
|
+
if (hasModel && typeof model !== 'string') throw new Error('--model requires a value');
|
|
818
|
+
const existingAgent = hasModel && !runtime
|
|
819
|
+
? (await listAgentRecords(serverClient)).find((candidate) => candidate.agent_key === agentKey)
|
|
820
|
+
: undefined;
|
|
821
|
+
if (hasModel && !runtime && !existingAgent) throw new Error(`agent ${agentKey} not found`);
|
|
822
|
+
const runtimeForModel = runtime ?? (typeof existingAgent?.runtime === 'string' ? existingAgent.runtime : '');
|
|
823
|
+
const response = await fetch(`${serverClient.baseUrl}/api/agents/${encodeURIComponent(agentKey)}`, {
|
|
824
|
+
method: 'PATCH',
|
|
825
|
+
headers: { 'content-type': 'application/json' },
|
|
826
|
+
body: JSON.stringify({
|
|
827
|
+
...(runtime ? { runtime } : {}),
|
|
828
|
+
...(hasModel ? { model: await validateRuntimeModel(runtimeForModel, model) } : {}),
|
|
829
|
+
}),
|
|
830
|
+
});
|
|
831
|
+
const payload = await response.json() as { data?: { agent: Record<string, unknown> }; message?: string };
|
|
832
|
+
if (!response.ok) throw new Error(payload.message ?? `update failed: ${response.status}`);
|
|
833
|
+
agent = payload.data?.agent;
|
|
834
|
+
} else {
|
|
835
|
+
agent = (await listAgentRecords(serverClient)).find((candidate) => candidate.agent_key === agentKey);
|
|
836
|
+
if (!agent) throw new Error(`agent ${agentKey} not found`);
|
|
837
|
+
}
|
|
434
838
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
839
|
+
if (hasLarkUnbind) await unbindLarkBotFromAgent(serverClient, updateFlags, agentKey);
|
|
840
|
+
else await bindLarkBotToAgent(serverClient, updateFlags, agentKey);
|
|
841
|
+
printJson(agent);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (sub === 'delete' || sub === 'remove') {
|
|
846
|
+
const agentKey = await collectAgentDeleteInput(serverClient, args.flags);
|
|
847
|
+
const params = new URLSearchParams();
|
|
848
|
+
const configPath = larkConfigPath(args.flags);
|
|
849
|
+
if (configPath) params.set('lark_config', configPath);
|
|
850
|
+
const suffix = params.toString() ? `?${params}` : '';
|
|
851
|
+
const data = await requestJson<{ deletion: Record<string, unknown>; lark: { unbound: boolean; app_id: string | null; error?: string | null } }>(
|
|
852
|
+
`${serverClient.baseUrl}/api/agents/${encodeURIComponent(agentKey)}${suffix}`,
|
|
853
|
+
{
|
|
854
|
+
method: 'DELETE',
|
|
855
|
+
headers: { 'content-type': 'application/json' },
|
|
856
|
+
body: JSON.stringify({ confirm_delete: true, leave_rooms: true, unbind_lark: true }),
|
|
857
|
+
},
|
|
858
|
+
);
|
|
859
|
+
if (data.lark.unbound && !boolFlag(args.flags, 'no-reload')) await reloadLarkIntegration(serverClient.baseUrl);
|
|
860
|
+
if (args.flags.json) {
|
|
861
|
+
printJson(data);
|
|
862
|
+
} else {
|
|
863
|
+
const deletion = data.deletion;
|
|
864
|
+
console.log(`Deleted agent ${agentKey}.`);
|
|
865
|
+
console.log(` rooms_left: ${deletion.rooms_left}`);
|
|
866
|
+
console.log(` subscriptions_removed: ${deletion.subscriptions_removed}`);
|
|
867
|
+
console.log(` active_deliveries_canceled: ${deletion.active_deliveries_canceled}`);
|
|
868
|
+
console.log(` running_runs_marked_kill: ${deletion.running_runs_marked_kill}`);
|
|
869
|
+
if (data.lark.unbound) console.log(` lark_unbound: ${data.lark.app_id}`);
|
|
870
|
+
if (data.lark.error) console.log(` lark_unbind_error: ${data.lark.error}`);
|
|
871
|
+
}
|
|
443
872
|
return;
|
|
444
873
|
}
|
|
445
874
|
|
|
@@ -486,6 +915,11 @@ async function main(): Promise<void> {
|
|
|
486
915
|
throw new Error(`unknown lark-users subcommand: ${sub ?? '(none)'}\n\n${usage()}`);
|
|
487
916
|
}
|
|
488
917
|
|
|
918
|
+
if (args.command === 'messaging') {
|
|
919
|
+
await runMessagingCommand(serverClient, args, usage);
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
489
923
|
if (args.command === 'lark') {
|
|
490
924
|
const { runLarkCli } = await import('./lark/cli.js');
|
|
491
925
|
const code = await runLarkCli({
|