luca 3.0.2 → 3.1.0

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/bun.lock CHANGED
@@ -5,6 +5,10 @@
5
5
  "name": "luca",
6
6
  "dependencies": {
7
7
  "@modelcontextprotocol/sdk": "1.12.1",
8
+ "@noble/ciphers": "^2.2.0",
9
+ "@noble/curves": "^2.2.0",
10
+ "@noble/hashes": "^2.2.0",
11
+ "@number0/iroh": "^0.35.0",
8
12
  "@openai/codex": "0.99.0",
9
13
  "@resvg/resvg-js": "2.6.2",
10
14
  "@supabase/supabase-js": "2.95.3",
@@ -53,6 +57,7 @@
53
57
  "react-reconciler": "0.33.0",
54
58
  "remark-gfm": "3.0.1",
55
59
  "rimraf": "5.0.10",
60
+ "telnyx": "6.41.1",
56
61
  "typescript": "5.9.3",
57
62
  "unist-util-find-after": "4.0.1",
58
63
  "unist-util-find-all-after": "4.0.1",
@@ -216,6 +221,12 @@
216
221
 
217
222
  "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.1", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw=="],
218
223
 
224
+ "@noble/ciphers": ["@noble/ciphers@2.2.0", "", {}, "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA=="],
225
+
226
+ "@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="],
227
+
228
+ "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="],
229
+
219
230
  "@node-llama-cpp/linux-arm64": ["@node-llama-cpp/linux-arm64@3.17.1", "", { "os": "linux", "cpu": [ "x64", "arm64", ] }, "sha512-QK8opgd/AnGHvSQwIDXukrMNa760PLOxcu4OrxJn/CsHxiKucch0L4bDfu17XvlKiMoK41lXa6CKFm1kjVaGIQ=="],
220
231
 
221
232
  "@node-llama-cpp/linux-armv7l": ["@node-llama-cpp/linux-armv7l@3.17.1", "", { "os": "linux", "cpu": [ "arm", "x64", ] }, "sha512-KCLDVR1hayo/kSDUq6msT4BkBaEiudFsaT14EWFMw7V7JdEX/qBcHnthPa0eAtX0SVfKG7e2TafGuKpvr54GAw=="],
@@ -250,6 +261,32 @@
250
261
 
251
262
  "@npmcli/fs": ["@npmcli/fs@3.1.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg=="],
252
263
 
264
+ "@number0/iroh": ["@number0/iroh@0.35.0", "", { "optionalDependencies": { "@number0/iroh-android-arm-eabi": "0.35.0", "@number0/iroh-android-arm64": "0.35.0", "@number0/iroh-darwin-arm64": "0.35.0", "@number0/iroh-darwin-universal": "0.35.0", "@number0/iroh-linux-arm-gnueabihf": "0.35.0", "@number0/iroh-linux-arm-musleabihf": "0.35.0", "@number0/iroh-linux-arm64-gnu": "0.35.0", "@number0/iroh-linux-arm64-musl": "0.35.0", "@number0/iroh-linux-x64-gnu": "0.35.0", "@number0/iroh-linux-x64-musl": "0.35.0", "@number0/iroh-win32-arm64-msvc": "0.35.0", "@number0/iroh-win32-x64-msvc": "0.35.0" } }, "sha512-E3BQYZVuolHChbguaEwqcg/LT2q5sh3/poz0Op3A9q/B8j306RDCUCOlBV40IUZYYgsAPUHUW+OAyANUID8t/w=="],
265
+
266
+ "@number0/iroh-android-arm-eabi": ["@number0/iroh-android-arm-eabi@0.35.0", "", { "os": "android", "cpu": "arm" }, "sha512-l/G4tZZ7l3h/aIwi6X34YxEl6qneflxkAN0uylsjZFcDqDQYctJMDhJtYJD5GRx8uIVbLvzCNeDpVupx8TCzWQ=="],
267
+
268
+ "@number0/iroh-android-arm64": ["@number0/iroh-android-arm64@0.35.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Fc2R7yWnJkPuw1TSbR3Y+mQtLuWzE834nhNVcEUJuBs68w/KQSPmCeUVnpN7gUY2/9QskHUS5M9AfwxiD/yfMg=="],
269
+
270
+ "@number0/iroh-darwin-arm64": ["@number0/iroh-darwin-arm64@0.35.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-O95ZxMIx15Oz5ZHbnAD5pNAcxhgq601otS+oj/OB4j81IJ/bfhB0gVuhqHmJ4n3cJFKD5UydvACzQ+QbnsOsbQ=="],
271
+
272
+ "@number0/iroh-darwin-universal": ["@number0/iroh-darwin-universal@0.35.0", "", { "os": "darwin" }, "sha512-yVvTWn9CWyassjxLQ1jbhWRRXkK/WBw6P8VZTmqUUvPmFKvNbneTvurvOQAaZJ1E5yk2YoNseAjQ/zvdH4JQTQ=="],
273
+
274
+ "@number0/iroh-linux-arm-gnueabihf": ["@number0/iroh-linux-arm-gnueabihf@0.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-ThIQj2FL5gtXaC6Bba+uuF8Gm3eLpdDV4KiAcu5/tW2th9RIns5Nv/HekqAcX0OtxgyxDlopmdwNUnURKVGisg=="],
275
+
276
+ "@number0/iroh-linux-arm-musleabihf": ["@number0/iroh-linux-arm-musleabihf@0.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-kkO4YL8IccPDg5shdVPfWmFpyxwnX7Ycca8oBmDljmWjGDd9dhX1Q1uX6edV1/lYM1Tgj1hXFKiZ3kLLhNAELA=="],
277
+
278
+ "@number0/iroh-linux-arm64-gnu": ["@number0/iroh-linux-arm64-gnu@0.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-wclde7ePsHKZgtxdgzpPChPy+W5tlvEZ4RBrwVO6CaOXOJK2kyGKtc33iFQzMgovsZV0eg8FhK20FSuGbHgMaA=="],
279
+
280
+ "@number0/iroh-linux-arm64-musl": ["@number0/iroh-linux-arm64-musl@0.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-C2nmuJaNKjRzJftyRuBUTCBbLkrIXIEABq5qSgU1h7ZHBzyyEnfbl6J5xlt2/mBUgLC5lot0lOho15VYJ288TQ=="],
281
+
282
+ "@number0/iroh-linux-x64-gnu": ["@number0/iroh-linux-x64-gnu@0.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Zo9fOxJttrZR5MPBaZW2zDTokE+U+Xw7QGpwwUZWnb10HsVjvHvZeDjNMAP6SJFkjQcGZA/YtRNVP4muAZESYw=="],
283
+
284
+ "@number0/iroh-linux-x64-musl": ["@number0/iroh-linux-x64-musl@0.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-GsNJgO0ia4PzjUhpLXK2q+2BtI4gVPug1xDrAZkeg44GLLz7/UcXQBcy6II1h01E8YV3931SMb+7guztG4S9jw=="],
285
+
286
+ "@number0/iroh-win32-arm64-msvc": ["@number0/iroh-win32-arm64-msvc@0.35.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-79hmSrU61yaBAA/btMv9XGBvHZIjW5WXEeQ2FyMxLlU9HCRW5F1Fo1OvTT6gjQQy13U4j0vR6QE5wV8M068K9g=="],
287
+
288
+ "@number0/iroh-win32-x64-msvc": ["@number0/iroh-win32-x64-msvc@0.35.0", "", { "os": "win32", "cpu": "x64" }, "sha512-ymDdwXop1M0u8ehxQeiwOFbU5WodEljk6+Suzy7Xk+qxrYpKUDUij3bwlx9lYKYohtlK8rvhLMkLO3v0BNMoWQ=="],
289
+
253
290
  "@openai/codex": ["@openai/codex@0.99.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.99.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.99.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.99.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.99.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.99.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.99.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-tnER1vtbrk5DDIz9raQC16lXqLZ3+DiWjoBHHPS9Sc8kybjP3LodDQTYaCfz8lRMkT0vFgEEXpm4hHH/lGN5yw=="],
254
291
 
255
292
  "@openai/codex-darwin-arm64": ["@openai/codex@0.99.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pu02RGrHbdCbPKAJBvOQx5sfIr78CZ7BtlG2jMhiyX2QXin7+WJ2jsGXtjJSpNU4ttmieuuHGXKLMgjsw+/6vg=="],
@@ -318,6 +355,8 @@
318
355
 
319
356
  "@soederpop/luca": ["@soederpop/luca@0.3.8", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.12.1", "@openai/codex": "0.99.0", "@resvg/resvg-js": "2.6.2", "@supabase/supabase-js": "2.95.3", "@types/marked": "6.0.0", "@types/marked-terminal": "6.1.1", "axios": "1.13.6", "cacache": "17.1.4", "chalk": "5.4.1", "child-process-promise": "2.2.1", "chokidar": "3.6.0", "cli-markdown": "3.5.0", "compromise": "14.14.5", "contentbase": "0.2.0", "cors": "2.8.5", "detect-port": "1.6.1", "dotenv": "17.2.4", "endent": "2.1.0", "excalidraw-to-svg": "3.1.0", "express": "4.21.2", "figlet": "1.11.0", "glob": "11.0.2", "googleapis": "171.4.0", "grammy": "1.40.0", "inflect": "0.5.0", "ink": "6.7.0", "inquirer": "9.3.7", "ioredis": "5.10.1", "isomorphic-vm": "0.0.1", "isomorphic-ws": "5.0.0", "js-tiktoken": "1.0.21", "js-yaml": "4.1.0", "lodash-es": "4.17.21", "marked": "15.0.12", "marked-terminal": "7.3.0", "mdast-util-to-markdown": "1.5.0", "mdast-util-to-string": "3.2.0", "micromatch": "4.0.8", "minimist": "1.2.8", "node-uuid": "1.4.8", "object-hash": "3.0.0", "openai": "5.1.1", "opener": "1.5.2", "react": "19.2.4", "react-devtools-core": "7.0.1", "react-dom": "19.2.4", "react-reconciler": "0.33.0", "remark-gfm": "3.0.1", "rimraf": "5.0.10", "typescript": "5.9.3", "unist-util-find-after": "4.0.1", "unist-util-find-all-after": "4.0.1", "unist-util-find-all-before": "4.0.1", "unist-util-find-before": "3.0.1", "unist-util-select": "4.0.3", "unist-util-visit": "4.1.2", "wink-eng-lite-web-model": "1.8.1", "wink-nlp": "2.4.0", "ws": "8.18.2", "zod": "4.3.6" }, "optionalDependencies": { "node-llama-cpp": "3.17.1" }, "bin": { "luca": "src/cli/cli.ts" } }, "sha512-53a1vDj329pVbkYFXG5ZlbnwoKTXhnNsaxda2MKQLfTmnPPZKUr6YPRpSduS2lcawTHQ1rNT/kxC7FrHbERndw=="],
320
357
 
358
+ "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
359
+
321
360
  "@supabase/auth-js": ["@supabase/auth-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-vD2YoS8E2iKIX0F7EwXTmqhUpaNsmbU6X2R0/NdFcs02oEfnHyNP/3M716f3wVJ2E5XHGiTFXki6lRckhJ0Thg=="],
322
361
 
323
362
  "@supabase/functions-js": ["@supabase/functions-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-uTuOAKzs9R/IovW1krO0ZbUHSJnsnyJElTXIRhjJTqymIVGcHzkAYnBCJqd7468Fs/Foz1BQ7Dv6DCl05lr7ig=="],
@@ -852,6 +891,8 @@
852
891
 
853
892
  "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
854
893
 
894
+ "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="],
895
+
855
896
  "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
856
897
 
857
898
  "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
@@ -1592,6 +1633,8 @@
1592
1633
 
1593
1634
  "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
1594
1635
 
1636
+ "standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="],
1637
+
1595
1638
  "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
1596
1639
 
1597
1640
  "stdin-discarder": ["stdin-discarder@0.3.2", "", {}, "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A=="],
@@ -1632,6 +1675,8 @@
1632
1675
 
1633
1676
  "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
1634
1677
 
1678
+ "telnyx": ["telnyx@6.41.1", "", { "dependencies": { "standardwebhooks": "^1.0.0" }, "peerDependencies": { "ws": "^8.18.0" }, "optionalPeers": ["ws"] }, "sha512-ILrCP1NRNSfdZ2yTYoQoODtTD+6rMvuRwYlyiIfZAjhGxst1X8Cirxwb9tTLv8d24aOO2394F/m41DPVHObe6Q=="],
1679
+
1635
1680
  "terminal-size": ["terminal-size@4.0.1", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
1636
1681
 
1637
1682
  "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="],
@@ -0,0 +1,137 @@
1
+ import { z } from 'zod'
2
+ import { CommandOptionsSchema } from 'luca'
3
+ import type { ContainerContext } from 'luca'
4
+
5
+ export const argsSchema = CommandOptionsSchema.extend({
6
+ name: z.string().default('luca-agent').describe('Agent name / identity to use'),
7
+ bootstrap: z.string().optional().describe('JSON NodeAddr from another agent — enables immediate gossip connectivity'),
8
+ file: z.string().optional().describe('Path to a file to send as a blob attachment'),
9
+ mesh: z.string().optional().describe('Private mesh ID — all agents must use the same value to communicate'),
10
+ subcommand: z.string().optional().describe('Subcommand: whoami | listen | send'),
11
+ arg1: z.string().optional().describe('First positional argument'),
12
+ arg2: z.string().optional().describe('Second positional argument'),
13
+ })
14
+
15
+ export const positionals = ['subcommand', 'arg1', 'arg2']
16
+
17
+ const USAGE = `
18
+ Usage:
19
+ luca social whoami [--name <name>] [--mesh <id>] Print this agent's identity
20
+ luca social listen [--name <name>] [--mesh <id>] Join the mesh and print incoming messages
21
+ luca social send <pubkey> <msg> [--name <name>] [--mesh <id>] Send an encrypted message
22
+ luca social send <pubkey> --file <path> [--name <name>] [--mesh <id>] Send a file
23
+ luca social send <pubkey> --file <path> <caption> [--name <name>] [--mesh <id>] Send a file with caption
24
+
25
+ --mesh <id> Private mesh ID. All agents must use the same value.
26
+ Omit to use the public Cipher topic (interop with Cipher desktop app).
27
+ `.trim()
28
+
29
+ export async function social(options: z.infer<typeof argsSchema>, context: ContainerContext) {
30
+ const container = context.container as any
31
+ const ui = container.feature('ui')
32
+ const subcommand = options.subcommand
33
+
34
+ if (!subcommand || subcommand === 'help') {
35
+ ui.print(USAGE)
36
+ return
37
+ }
38
+
39
+ await container.helpers.discoverAll()
40
+ const bootstrapAddrs = options.bootstrap ? [options.bootstrap] : []
41
+ const agent = container.feature('cipherSocial', { name: options.name, bootstrapAddrs, meshId: options.mesh })
42
+
43
+ if (subcommand === 'whoami') {
44
+ await agent.loadIdentity()
45
+ ui.print(`Name: ${options.name}`)
46
+ ui.print(`Public Key: ${agent.publicKey}`)
47
+ ui.print(`Data dir: ${agent.dataDir}`)
48
+ return
49
+ }
50
+
51
+ if (subcommand === 'listen') {
52
+ ui.print(`Connecting as "${options.name}"...`)
53
+ await agent.connect()
54
+ ui.print(`Connected`)
55
+ ui.print(`Node ID: ${agent.nodeId}`)
56
+ ui.print(`Public Key: ${agent.publicKey}`)
57
+ ui.print(`\nTo send to this agent from another machine:`)
58
+ ui.print(` luca social send '${agent.publicKey}' 'hello' --bootstrap '${agent.nodeAddrJson}'`)
59
+ ui.print(`\nListening for messages. Ctrl+C to exit.\n`)
60
+
61
+ agent.on('presence', ({ publicKey, name }: any) => {
62
+ ui.print(`[presence] ${name} (${publicKey.slice(0, 12)}...)`)
63
+ })
64
+
65
+ agent.on('message', async ({ from, payload }: any) => {
66
+ const fromShort = from.slice(0, 12) + '...'
67
+
68
+ if (payload?.type === 'file') {
69
+ const outPath = `downloads/${payload.filename}`
70
+ ui.print(`\n[file from ${fromShort}] ${payload.filename} (${(payload.size / 1024).toFixed(1)} KB)`)
71
+ if (payload.caption) ui.print(` caption: ${payload.caption}`)
72
+ ui.print(` fetching blob...`)
73
+ try {
74
+ await agent.fetchBlobToFile(payload.blobHash, payload.blobNodeAddr, outPath)
75
+ ui.print(` saved to ${outPath}`)
76
+ } catch (err: any) {
77
+ ui.print(` fetch failed: ${err.message}`)
78
+ }
79
+ return
80
+ }
81
+
82
+ ui.print(`\n[message from ${fromShort}]`)
83
+ ui.print(JSON.stringify(payload, null, 2))
84
+ })
85
+
86
+ process.on('SIGINT', async () => {
87
+ await agent.disconnect()
88
+ process.exit(0)
89
+ })
90
+
91
+ await new Promise(() => {})
92
+ return
93
+ }
94
+
95
+ if (subcommand === 'send') {
96
+ const recipientKey = options.arg1
97
+
98
+ if (!recipientKey) {
99
+ ui.print('Usage: luca social send <public-key> <message>')
100
+ ui.print(' luca social send <public-key> --file <path>')
101
+ process.exit(1)
102
+ }
103
+
104
+ ui.print(`Connecting as "${options.name}"...`)
105
+ await agent.connect()
106
+
107
+ if (options.file) {
108
+ ui.print(`Storing file ${options.file}...`)
109
+ ui.print(`Sending file to ${recipientKey.slice(0, 12)}...`)
110
+ await agent.sendFile(recipientKey, options.file, options.arg2)
111
+ } else {
112
+ const message = options.arg2
113
+ if (!message) {
114
+ ui.print('Usage: luca social send <public-key> <message>')
115
+ await agent.disconnect()
116
+ process.exit(1)
117
+ }
118
+ ui.print(`Sending to ${recipientKey.slice(0, 12)}...`)
119
+ await agent.send(recipientKey, { type: 'text', text: message })
120
+ }
121
+
122
+ // Wait for gossip broadcast to propagate
123
+ await new Promise(resolve => setTimeout(resolve, 3000))
124
+ await agent.disconnect()
125
+ ui.print('Sent.')
126
+ return
127
+ }
128
+
129
+ ui.print(`Unknown subcommand: ${subcommand}`)
130
+ ui.print(USAGE)
131
+ }
132
+
133
+ export default {
134
+ description: 'Encrypted P2P messaging between Luca agents via the Cipher social network.',
135
+ argsSchema,
136
+ handler: social,
137
+ }