nebula-ai-agent 0.3.1

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.
Files changed (60) hide show
  1. package/README.md +39 -0
  2. package/bin/nebula +11 -0
  3. package/package.json +50 -0
  4. package/src/commands/_agents.ts +14 -0
  5. package/src/commands/_unlock.ts +66 -0
  6. package/src/commands/agent-wallet.ts +90 -0
  7. package/src/commands/chat-telegram.ts +398 -0
  8. package/src/commands/chat.tsx +1308 -0
  9. package/src/commands/drain.ts +90 -0
  10. package/src/commands/gateway-logs.ts +49 -0
  11. package/src/commands/gateway-run.ts +42 -0
  12. package/src/commands/gateway-start.ts +216 -0
  13. package/src/commands/gateway-status.ts +90 -0
  14. package/src/commands/gateway-stop.ts +133 -0
  15. package/src/commands/gateway.ts +101 -0
  16. package/src/commands/identity.ts +178 -0
  17. package/src/commands/init/cost.ts +40 -0
  18. package/src/commands/init/funding-gate.ts +64 -0
  19. package/src/commands/init/model-picker.ts +25 -0
  20. package/src/commands/init/operator-picker.ts +233 -0
  21. package/src/commands/init/telegram-step.ts +245 -0
  22. package/src/commands/init/wizard-state.ts +94 -0
  23. package/src/commands/init.ts +439 -0
  24. package/src/commands/login.ts +86 -0
  25. package/src/commands/logs.ts +37 -0
  26. package/src/commands/model.ts +48 -0
  27. package/src/commands/pairing-approve.ts +65 -0
  28. package/src/commands/pairing-clear.ts +39 -0
  29. package/src/commands/pairing-list.ts +55 -0
  30. package/src/commands/pairing-revoke.ts +49 -0
  31. package/src/commands/pairing.ts +81 -0
  32. package/src/commands/status.ts +44 -0
  33. package/src/commands/telegram-remove.ts +62 -0
  34. package/src/commands/telegram-setup.ts +64 -0
  35. package/src/commands/telegram-status.ts +87 -0
  36. package/src/commands/telegram.ts +44 -0
  37. package/src/commands/trust.ts +196 -0
  38. package/src/config/load.ts +35 -0
  39. package/src/config/render.ts +99 -0
  40. package/src/index.ts +184 -0
  41. package/src/profile/crypto.ts +68 -0
  42. package/src/profile/derive.ts +25 -0
  43. package/src/profile/store.ts +86 -0
  44. package/src/profile/unlock.ts +29 -0
  45. package/src/ui/app.tsx +719 -0
  46. package/src/ui/approval-summary.ts +32 -0
  47. package/src/ui/markdown-parse.ts +219 -0
  48. package/src/ui/markdown.tsx +37 -0
  49. package/src/ui/state.ts +181 -0
  50. package/src/util/bootstrap-mode.ts +25 -0
  51. package/src/util/bootstrap-progress-box.ts +378 -0
  52. package/src/util/cli-version.ts +28 -0
  53. package/src/util/format.ts +11 -0
  54. package/src/util/gateway-spawn.ts +125 -0
  55. package/src/util/gateway-version.ts +154 -0
  56. package/src/util/github-releases.ts +79 -0
  57. package/src/util/profile-key.ts +25 -0
  58. package/src/util/ref-resolver.ts +55 -0
  59. package/src/util/silence-console.ts +40 -0
  60. package/src/util/telegram-secrets.ts +218 -0
@@ -0,0 +1,39 @@
1
+ import { confirm, isCancel } from '@clack/prompts'
2
+ import { PairingStore, agentPaths, placeholderAgentId } from 'nebula-ai-core'
3
+ import { findAndLoadConfig } from '../config/load'
4
+
5
+ export interface RunPairingClearOpts {
6
+ platform?: string
7
+ yes?: boolean
8
+ }
9
+
10
+ export async function runPairingClear(opts: RunPairingClearOpts): Promise<void> {
11
+ const loaded = await findAndLoadConfig()
12
+ if (!loaded) {
13
+ console.error('No nebula.config.ts found. Run `nebula init` first.')
14
+ process.exit(1)
15
+ }
16
+ const { config } = loaded
17
+ if (!config.identity.agent) {
18
+ console.error('Config has no agent. Run `nebula init` first.')
19
+ process.exit(1)
20
+ }
21
+ const agentId = placeholderAgentId(config.identity.agent)
22
+ const dir = agentPaths.agent(agentId).pairingDir
23
+ const store = new PairingStore({ dir })
24
+
25
+ if (!opts.yes) {
26
+ const target = opts.platform ? `${opts.platform} pending` : 'ALL pending pairing codes'
27
+ const ok = await confirm({
28
+ message: `Clear ${target}?`,
29
+ initialValue: false,
30
+ })
31
+ if (isCancel(ok) || !ok) {
32
+ console.log('Aborted.')
33
+ return
34
+ }
35
+ }
36
+
37
+ const count = store.clearPending(opts.platform)
38
+ console.log(`✓ Cleared ${count} pending pairing code${count === 1 ? '' : 's'}`)
39
+ }
@@ -0,0 +1,55 @@
1
+ import { PairingStore, agentPaths, placeholderAgentId } from 'nebula-ai-core'
2
+ import { findAndLoadConfig } from '../config/load'
3
+
4
+ export interface RunPairingListOpts {
5
+ platform?: string
6
+ }
7
+
8
+ export async function runPairingList(opts: RunPairingListOpts): Promise<void> {
9
+ const store = await openPairingStore()
10
+ if (!store) return
11
+
12
+ const pending = store.listPending(opts.platform)
13
+ const approved = store.listApproved(opts.platform)
14
+
15
+ const pendingTitle = opts.platform ? `Pending (${opts.platform})` : 'Pending'
16
+ console.log(`\n${pendingTitle} (1h TTL):`)
17
+ if (pending.length === 0) {
18
+ console.log(' (none)')
19
+ } else {
20
+ for (const p of pending) {
21
+ const userLabel = p.userName ? `@${p.userName}` : '(unknown)'
22
+ const idLabel = `id=${p.userId}`
23
+ console.log(` [${p.platform}] ${p.code} ${userLabel} ${idLabel} age=${p.ageMinutes}m`)
24
+ }
25
+ }
26
+
27
+ const approvedTitle = opts.platform ? `Approved (${opts.platform})` : 'Approved'
28
+ console.log(`\n${approvedTitle}:`)
29
+ if (approved.length === 0) {
30
+ console.log(' (none)')
31
+ } else {
32
+ for (const a of approved) {
33
+ const userLabel = a.userName ? `@${a.userName}` : '(unknown)'
34
+ const idLabel = `id=${a.userId}`
35
+ console.log(` [${a.platform}] ${userLabel} ${idLabel}`)
36
+ }
37
+ }
38
+ console.log()
39
+ }
40
+
41
+ async function openPairingStore(): Promise<PairingStore | null> {
42
+ const loaded = await findAndLoadConfig()
43
+ if (!loaded) {
44
+ console.error('No nebula.config.ts found. Run `nebula init` first.')
45
+ return null
46
+ }
47
+ const { config } = loaded
48
+ if (!config.identity.agent) {
49
+ console.error('Config has no agent. Run `nebula init` first.')
50
+ return null
51
+ }
52
+ const agentId = placeholderAgentId(config.identity.agent)
53
+ const dir = agentPaths.agent(agentId).pairingDir
54
+ return new PairingStore({ dir })
55
+ }
@@ -0,0 +1,49 @@
1
+ import { confirm, isCancel } from '@clack/prompts'
2
+ import { PairingStore, agentPaths, placeholderAgentId } from 'nebula-ai-core'
3
+ import { findAndLoadConfig } from '../config/load'
4
+
5
+ export interface RunPairingRevokeOpts {
6
+ platform: string
7
+ userId: string
8
+ yes?: boolean
9
+ }
10
+
11
+ export async function runPairingRevoke(opts: RunPairingRevokeOpts): Promise<void> {
12
+ const loaded = await findAndLoadConfig()
13
+ if (!loaded) {
14
+ console.error('No nebula.config.ts found. Run `nebula init` first.')
15
+ process.exit(1)
16
+ }
17
+ const { config } = loaded
18
+ if (!config.identity.agent) {
19
+ console.error('Config has no agent. Run `nebula init` first.')
20
+ process.exit(1)
21
+ }
22
+ const agentId = placeholderAgentId(config.identity.agent)
23
+ const dir = agentPaths.agent(agentId).pairingDir
24
+ const store = new PairingStore({ dir })
25
+
26
+ if (!store.isApproved(opts.platform, opts.userId)) {
27
+ console.error(`User ${opts.userId} is not on the ${opts.platform} approved list.`)
28
+ process.exit(1)
29
+ }
30
+
31
+ if (!opts.yes) {
32
+ const ok = await confirm({
33
+ message: `Revoke ${opts.platform} access for user id ${opts.userId}?`,
34
+ initialValue: false,
35
+ })
36
+ if (isCancel(ok) || !ok) {
37
+ console.log('Aborted.')
38
+ return
39
+ }
40
+ }
41
+
42
+ const removed = store.revoke(opts.platform, opts.userId)
43
+ if (removed) {
44
+ console.log(`✓ Revoked: ${opts.platform} id=${opts.userId}`)
45
+ } else {
46
+ console.error('Revoke failed (concurrent removal?)')
47
+ process.exit(1)
48
+ }
49
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `nebula pairing <subcommand>` — argv dispatcher for the DM pairing flow.
3
+ *
4
+ * Subcommands:
5
+ * list show pending codes + approved users
6
+ * approve <platform> <code> approve a pairing code (case-insensitive)
7
+ * revoke <platform> <userId> revoke an approved user
8
+ * clear-pending [platform] drop all pending codes
9
+ *
10
+ * Platform is `telegram` for Phase 12. Future platforms (discord, slack) will
11
+ * reuse the same command surface.
12
+ */
13
+
14
+ export interface PairingArgs {
15
+ sub: 'list' | 'approve' | 'revoke' | 'clear-pending'
16
+ platform?: string
17
+ code?: string
18
+ userId?: string
19
+ yes?: boolean
20
+ }
21
+
22
+ const VALID_SUBS = ['list', 'approve', 'revoke', 'clear-pending'] as const
23
+
24
+ export type PairingParseResult = PairingArgs | { error: string }
25
+
26
+ export function parsePairingArgs(argv: string[]): PairingParseResult {
27
+ const sub = argv[0]
28
+ if (!sub) {
29
+ return {
30
+ error:
31
+ 'usage: nebula pairing <list | approve <platform> <code> | revoke <platform> <userId> | clear-pending [platform]>',
32
+ }
33
+ }
34
+ if (!(VALID_SUBS as readonly string[]).includes(sub)) {
35
+ return { error: `unknown subcommand '${sub}' (expected: ${VALID_SUBS.join(' | ')})` }
36
+ }
37
+ const positional = argv.slice(1).filter(a => !a.startsWith('-'))
38
+ const yes = argv.includes('--yes') || argv.includes('-y')
39
+
40
+ if (sub === 'approve') {
41
+ if (positional.length < 2) {
42
+ return { error: 'usage: nebula pairing approve <platform> <code>' }
43
+ }
44
+ return { sub: 'approve', platform: positional[0], code: positional[1], yes }
45
+ }
46
+ if (sub === 'revoke') {
47
+ if (positional.length < 2) {
48
+ return { error: 'usage: nebula pairing revoke <platform> <userId>' }
49
+ }
50
+ return { sub: 'revoke', platform: positional[0], userId: positional[1], yes }
51
+ }
52
+ if (sub === 'clear-pending') {
53
+ return { sub: 'clear-pending', platform: positional[0], yes }
54
+ }
55
+ return { sub: 'list', platform: positional[0], yes }
56
+ }
57
+
58
+ export async function runPairing(args: PairingArgs): Promise<void> {
59
+ switch (args.sub) {
60
+ case 'list': {
61
+ const { runPairingList } = await import('./pairing-list')
62
+ await runPairingList({ platform: args.platform })
63
+ return
64
+ }
65
+ case 'approve': {
66
+ const { runPairingApprove } = await import('./pairing-approve')
67
+ await runPairingApprove({ platform: args.platform!, code: args.code! })
68
+ return
69
+ }
70
+ case 'revoke': {
71
+ const { runPairingRevoke } = await import('./pairing-revoke')
72
+ await runPairingRevoke({ platform: args.platform!, userId: args.userId!, yes: args.yes })
73
+ return
74
+ }
75
+ case 'clear-pending': {
76
+ const { runPairingClear } = await import('./pairing-clear')
77
+ await runPairingClear({ platform: args.platform, yes: args.yes })
78
+ return
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,44 @@
1
+ import { existsSync, statSync } from 'node:fs'
2
+ import { NETWORK_CHAIN_ID, NETWORK_RPC, agentPaths } from 'nebula-ai-core'
3
+ import { http, createPublicClient } from 'viem'
4
+ import { findAndLoadConfig } from '../config/load'
5
+ import { listAgentIds } from './_agents'
6
+
7
+ export async function runStatus(opts?: { cwd?: string }): Promise<void> {
8
+ const cwd = opts?.cwd ?? process.cwd()
9
+ const found = await findAndLoadConfig(cwd)
10
+ if (!found) {
11
+ console.log('No nebula.config.ts found. Run `nebula init` first.')
12
+ process.exit(1)
13
+ }
14
+ const { config, path } = found
15
+ console.log(`config ${path}`)
16
+ console.log(`network ${config.network} (chain ${NETWORK_CHAIN_ID[config.network]})`)
17
+ console.log(`rpc ${NETWORK_RPC[config.network]}`)
18
+ console.log(`plugins ${config.plugins.join(', ')}`)
19
+ if (config.identity.operator) console.log(`operator ${config.identity.operator}`)
20
+ if (config.identity.agent) console.log(`agent EOA ${config.identity.agent}`)
21
+ console.log(`brain ${config.brain.provider ?? '(not picked)'}`)
22
+
23
+ const ids = await listAgentIds()
24
+ if (ids.length === 0) {
25
+ console.log('\nNo agents found in ~/.nebula/agents. Re-run `nebula init`.')
26
+ return
27
+ }
28
+
29
+ const client = createPublicClient({
30
+ transport: http(NETWORK_RPC[config.network]),
31
+ })
32
+
33
+ for (const id of ids) {
34
+ console.log('')
35
+ console.log(`agent ${id}`)
36
+ console.log(`dir ${agentPaths.agent(id).dir}`)
37
+ const activityPath = agentPaths.agent(id).activityLog
38
+ if (existsSync(activityPath)) {
39
+ const sz = statSync(activityPath).size
40
+ console.log(`activity ${sz} bytes`)
41
+ }
42
+ void client
43
+ }
44
+ }
@@ -0,0 +1,62 @@
1
+ import { cancel, confirm, intro, isCancel, note, outro } from '@clack/prompts'
2
+ import { placeholderAgentId } from 'nebula-ai-core'
3
+ import { findAndLoadConfig } from '../config/load'
4
+ import { writeConfigTs } from '../config/render'
5
+ import {
6
+ removeTelegramSecrets,
7
+ telegramSecretsExist,
8
+ telegramSecretsPath,
9
+ } from '../util/telegram-secrets'
10
+
11
+ export interface TelegramRemoveOpts {
12
+ yes?: boolean
13
+ }
14
+
15
+ export async function runTelegramRemove(opts: TelegramRemoveOpts = {}): Promise<void> {
16
+ intro('nebula telegram remove')
17
+
18
+ const loaded = await findAndLoadConfig()
19
+ if (!loaded) {
20
+ cancel('No nebula.config.ts found. Run `nebula init` first.')
21
+ return
22
+ }
23
+ const { config, path: configPath } = loaded
24
+ if (!config.identity.agent) {
25
+ cancel('Config has no agent. Run `nebula init` first.')
26
+ return
27
+ }
28
+
29
+ const agentId = placeholderAgentId(config.identity.agent)
30
+
31
+ if (!telegramSecretsExist(agentId)) {
32
+ note('Nothing to remove.')
33
+ outro('not configured')
34
+ return
35
+ }
36
+
37
+ if (!opts.yes) {
38
+ const ok = (await confirm({
39
+ message: `Delete encrypted telegram-secrets for ${agentId}?`,
40
+ initialValue: false,
41
+ })) as boolean | symbol
42
+ if (isCancel(ok) || !ok) {
43
+ cancel('Aborted.')
44
+ return
45
+ }
46
+ }
47
+
48
+ await removeTelegramSecrets(agentId)
49
+
50
+ const plugins = (config.plugins ?? []).filter(p => p !== 'telegram')
51
+ if (plugins.length !== (config.plugins ?? []).length) {
52
+ const updated = { ...config, plugins }
53
+ await writeConfigTs(configPath, updated)
54
+ }
55
+
56
+ note(
57
+ `Local blob deleted: ${telegramSecretsPath(agentId)}\nThe bot token at @BotFather is STILL VALID. To fully revoke, run /token in\n@BotFather and pick "Revoke" for this bot.`,
58
+ 'reminder',
59
+ )
60
+
61
+ outro('telegram removed')
62
+ }
@@ -0,0 +1,64 @@
1
+ import { cancel, intro, note, outro } from '@clack/prompts'
2
+ import { placeholderAgentId } from 'nebula-ai-core'
3
+ import { type Address, getAddress } from 'viem'
4
+ import { findAndLoadConfig } from '../config/load'
5
+ import { loadOrPickOperatorSigner } from './init/operator-picker'
6
+ import { runTelegramStep } from './init/telegram-step'
7
+
8
+ /**
9
+ * `nebula telegram setup` — standalone entry. Loads the operator wallet, then
10
+ * delegates to `runTelegramStep` (the same helper bundled into `nebula init`'s
11
+ * Phase E). Owns its own intro/outro framing.
12
+ */
13
+ export async function runTelegramSetup(): Promise<void> {
14
+ intro('nebula telegram setup')
15
+
16
+ const loaded = await findAndLoadConfig()
17
+ if (!loaded) {
18
+ cancel('No nebula.config.ts found. Run `nebula init` first.')
19
+ return
20
+ }
21
+ const { config, path: configPath } = loaded
22
+ if (!config.identity.agent) {
23
+ cancel('Config has no agent. Run `nebula init` first.')
24
+ return
25
+ }
26
+
27
+ const agentAddress = getAddress(config.identity.agent) as Address
28
+ const agentId = placeholderAgentId(agentAddress)
29
+
30
+ const operator = await loadOrPickOperatorSigner({
31
+ network: config.network,
32
+ hint: config.operator,
33
+ })
34
+ if (!operator) {
35
+ cancel('No operator wallet available; cannot encrypt secrets.')
36
+ return
37
+ }
38
+
39
+ let result: Awaited<ReturnType<typeof runTelegramStep>>
40
+ try {
41
+ result = await runTelegramStep({
42
+ signer: operator,
43
+ agentId,
44
+ agentAddress,
45
+ configPath,
46
+ config,
47
+ network: config.network,
48
+ })
49
+ } finally {
50
+ await operator.close?.()
51
+ }
52
+
53
+ if (!result.configured) {
54
+ cancel(result.cancelled ? 'Aborted.' : 'Setup failed.')
55
+ return
56
+ }
57
+
58
+ note(
59
+ `Open https://t.me/${result.botUsername} in Telegram and send any message.\nThen run \`nebula\` (or \`nebula gateway start\`) to bring the agent online.`,
60
+ 'next step',
61
+ )
62
+
63
+ outro(`telegram setup complete (@${result.botUsername}, mode: ${result.modeUsed})`)
64
+ }
@@ -0,0 +1,87 @@
1
+ import { cancel, intro, log, outro, spinner } from '@clack/prompts'
2
+ import { placeholderAgentId } from 'nebula-ai-core'
3
+ import { type Address, getAddress } from 'viem'
4
+ import { findAndLoadConfig } from '../config/load'
5
+ import {
6
+ fetchBotInfo,
7
+ loadTelegramSecrets,
8
+ telegramSecretsExist,
9
+ telegramSecretsPath,
10
+ } from '../util/telegram-secrets'
11
+ import { loadOrPickOperatorSigner } from './init/operator-picker'
12
+
13
+ export async function runTelegramStatus(): Promise<void> {
14
+ intro('nebula telegram status')
15
+
16
+ const loaded = await findAndLoadConfig()
17
+ if (!loaded) {
18
+ cancel('No nebula.config.ts found. Run `nebula init` first.')
19
+ return
20
+ }
21
+ const { config } = loaded
22
+ if (!config.identity.agent) {
23
+ cancel('Config has no agent. Run `nebula init` first.')
24
+ return
25
+ }
26
+
27
+ const agentAddress = getAddress(config.identity.agent) as Address
28
+ const agentId = placeholderAgentId(agentAddress)
29
+ const path = telegramSecretsPath(agentId)
30
+
31
+ if (!telegramSecretsExist(agentId)) {
32
+ log.warn(`No telegram secrets stored for ${agentId}.`)
33
+ log.info(`Expected at: ${path}\nRun \`nebula telegram setup\` to configure.`)
34
+ outro('not configured')
35
+ return
36
+ }
37
+
38
+ const operator = await loadOrPickOperatorSigner({
39
+ network: config.network,
40
+ hint: config.operator,
41
+ })
42
+ if (!operator) {
43
+ cancel('No operator wallet available; cannot decrypt secrets.')
44
+ return
45
+ }
46
+
47
+ const sLoad = spinner()
48
+ sLoad.start('Decrypting telegram secrets via operator wallet')
49
+ let secrets: Awaited<ReturnType<typeof loadTelegramSecrets>>
50
+ try {
51
+ secrets = await loadTelegramSecrets({ signer: operator, agentAddress, agentId })
52
+ sLoad.stop('decrypted')
53
+ } catch (e) {
54
+ sLoad.stop(`decrypt failed: ${(e as Error).message.slice(0, 200)}`)
55
+ await operator.close?.()
56
+ return
57
+ } finally {
58
+ await operator.close?.()
59
+ }
60
+ if (!secrets) {
61
+ cancel('Empty telegram-secrets blob.')
62
+ return
63
+ }
64
+
65
+ const sPing = spinner()
66
+ sPing.start('Pinging Telegram getMe')
67
+ try {
68
+ const info = await fetchBotInfo(secrets.botToken)
69
+ sPing.stop(`bot ok: @${info.username} (id ${info.id})`)
70
+ } catch (e) {
71
+ sPing.stop(`getMe failed: ${(e as Error).message.slice(0, 200)}`)
72
+ log.warn('Token may have been revoked at @BotFather. Re-run `nebula telegram setup`.')
73
+ return
74
+ }
75
+
76
+ log.info(
77
+ [
78
+ `path ${path}`,
79
+ `bot username @${secrets.botUsername ?? '(unknown)'}`,
80
+ `bot id ${secrets.botId ?? '(unknown)'}`,
81
+ `allowed user ids ${secrets.allowedUserIds.length === 0 ? '(open access)' : secrets.allowedUserIds.join(', ')}`,
82
+ `plugin enabled ${(config.plugins ?? []).includes('telegram') ? 'yes' : 'no — add `telegram` to plugins'}`,
83
+ ].join('\n'),
84
+ )
85
+
86
+ outro(`telegram configured for ${agentId}`)
87
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `nebula telegram <subcommand>` — argv dispatcher.
3
+ *
4
+ * Subcommands:
5
+ * setup interactive wizard: validate token, encrypt + persist locally
6
+ * status confirm token still valid + show stored config
7
+ * remove delete the encrypted local blob (does NOT revoke at @BotFather)
8
+ */
9
+
10
+ export interface TelegramArgs {
11
+ sub: 'setup' | 'status' | 'remove'
12
+ yes?: boolean
13
+ }
14
+
15
+ const VALID_SUBS = ['setup', 'status', 'remove'] as const
16
+
17
+ export function parseTelegramArgs(argv: string[]): TelegramArgs | { error: string } {
18
+ const sub = argv[0]
19
+ if (!sub) return { error: 'usage: nebula telegram <setup | status | remove>' }
20
+ const valid = (VALID_SUBS as readonly string[]).includes(sub)
21
+ if (!valid) return { error: `unknown subcommand '${sub}' (expected: ${VALID_SUBS.join(' | ')})` }
22
+ const yes = argv.includes('--yes') || argv.includes('-y')
23
+ return { sub: sub as TelegramArgs['sub'], yes }
24
+ }
25
+
26
+ export async function runTelegram(args: TelegramArgs): Promise<void> {
27
+ switch (args.sub) {
28
+ case 'setup': {
29
+ const { runTelegramSetup } = await import('./telegram-setup')
30
+ await runTelegramSetup()
31
+ return
32
+ }
33
+ case 'status': {
34
+ const { runTelegramStatus } = await import('./telegram-status')
35
+ await runTelegramStatus()
36
+ return
37
+ }
38
+ case 'remove': {
39
+ const { runTelegramRemove } = await import('./telegram-remove')
40
+ await runTelegramRemove({ yes: args.yes })
41
+ return
42
+ }
43
+ }
44
+ }