clawmini 0.0.2 → 0.0.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/.github/workflows/ci.yml +59 -0
- package/README.md +4 -2
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +13 -4
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/cli/index.mjs +7 -6
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +16 -10
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/daemon/index.mjs +590 -401
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{fetch-BjZVyU3Z.mjs → fetch-Cn1XNyiO.mjs} +1 -1
- package/dist/{fetch-BjZVyU3Z.mjs.map → fetch-Cn1XNyiO.mjs.map} +1 -1
- package/dist/lite-oSYSvaOr.mjs +164 -0
- package/dist/lite-oSYSvaOr.mjs.map +1 -0
- package/dist/web/_app/immutable/chunks/{CAZeqksE.js → 8YNcRyEk.js} +1 -1
- package/dist/web/_app/immutable/chunks/{B3YcEpQV.js → DQoygso7.js} +1 -1
- package/dist/web/_app/immutable/entry/{app.ZuicLpkH.js → app.DO5eYwVz.js} +2 -2
- package/dist/web/_app/immutable/entry/start.D48mVn1m.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.BB1CjKco.js → 0.B-0CcADM.js} +1 -1
- package/dist/web/_app/immutable/nodes/{1.CdSgEHu9.js → 1.FixKgvRO.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.CKp7Wkn8.js → dist/web/_app/immutable/nodes/3.ncP0xLO6.js} +1 -1
- package/dist/web/_app/immutable/nodes/{4.FyeoMY-Y.js → 4.CQYJEgv8.js} +1 -1
- package/dist/web/_app/immutable/nodes/{5.D6mVN7l7.js → 5.BpJUN6QH.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +6 -6
- package/dist/{workspace-BC1ahx4R.mjs → workspace-DjoNjhW0.mjs} +12 -42
- package/dist/workspace-DjoNjhW0.mjs.map +1 -0
- package/docs/15_lite_fetch_pending/development_log.md +31 -0
- package/docs/15_lite_fetch_pending/notes.md +48 -0
- package/docs/15_lite_fetch_pending/prd.md +39 -0
- package/docs/15_lite_fetch_pending/questions.md +3 -0
- package/docs/15_lite_fetch_pending/tickets.md +42 -0
- package/docs/CHECKS.md +2 -2
- package/eslint.config.js +12 -0
- package/package.json +3 -2
- package/src/adapter-discord/client.ts +1 -1
- package/src/adapter-discord/index.ts +22 -5
- package/src/cli/client.ts +8 -3
- package/src/cli/e2e/adapter-discord.test.ts +2 -2
- package/src/cli/e2e/daemon.test.ts +2 -1
- package/src/cli/e2e/export-lite-func.test.ts +41 -13
- package/src/cli/e2e/fallbacks.test.ts +4 -0
- package/src/cli/lite.ts +24 -6
- package/src/daemon/api/agent-router.ts +191 -0
- package/src/daemon/{router.test.ts → api/index.test.ts} +101 -34
- package/src/daemon/api/index.ts +4 -0
- package/src/daemon/{router-policy-request.test.ts → api/policy-request.test.ts} +27 -13
- package/src/daemon/api/router-utils.ts +159 -0
- package/src/daemon/api/trpc.ts +30 -0
- package/src/daemon/api/user-router.ts +221 -0
- package/src/daemon/index.ts +3 -3
- package/src/daemon/message-interruption.test.ts +17 -10
- package/src/daemon/message-typing.test.ts +1 -1
- package/src/daemon/message.ts +260 -239
- package/src/daemon/observation.test.ts +1 -1
- package/src/daemon/queue.test.ts +28 -0
- package/src/daemon/queue.ts +30 -15
- package/src/daemon/request-store.test.ts +4 -4
- package/src/daemon/request-store.ts +3 -1
- package/src/shared/workspace.ts +4 -5
- package/templates/debug/settings.json +5 -0
- package/templates/environments/macos/env.json +1 -1
- package/templates/environments/macos-proxy/env.json +1 -1
- package/templates/gemini-claw/.gemini/hooks/insert-pending.sh +9 -0
- package/templates/gemini-claw/.gemini/settings.json +14 -1
- package/templates/gemini-claw/.gemini/system.md +2 -0
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +26 -26
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{CAZeqksE.js → 8YNcRyEk.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{B3YcEpQV.js → DQoygso7.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/{app.ZuicLpkH.js → app.DO5eYwVz.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D48mVn1m.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.BB1CjKco.js → 0.B-0CcADM.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{1.CdSgEHu9.js → 1.FixKgvRO.js} +1 -1
- package/{dist/web/_app/immutable/nodes/3.CKp7Wkn8.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.ncP0xLO6.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{4.FyeoMY-Y.js → 4.CQYJEgv8.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.D6mVN7l7.js → 5.BpJUN6QH.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +1 -1
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +1 -1
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/dist/chats-BcbxvPlj.mjs +0 -29
- package/dist/chats-BcbxvPlj.mjs.map +0 -1
- package/dist/chats-CpRQrNHj.mjs +0 -91
- package/dist/chats-CpRQrNHj.mjs.map +0 -1
- package/dist/fs-B5wW0oaH.mjs +0 -14
- package/dist/fs-B5wW0oaH.mjs.map +0 -1
- package/dist/lite-DBUuHsX0.mjs +0 -80
- package/dist/lite-DBUuHsX0.mjs.map +0 -1
- package/dist/policy-utils-BvfOK6Ih.mjs +0 -114
- package/dist/policy-utils-BvfOK6Ih.mjs.map +0 -1
- package/dist/rolldown-runtime-95iHPtFO.mjs +0 -18
- package/dist/web/_app/immutable/entry/start.DuQwh4Nz.js +0 -1
- package/dist/workspace-BC1ahx4R.mjs.map +0 -1
- package/src/daemon/router.ts +0 -510
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.DuQwh4Nz.js +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
- master
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
static-analysis:
|
|
15
|
+
name: Format, Lint & Check
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout repository
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Setup Node.js
|
|
22
|
+
uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: 22
|
|
25
|
+
cache: 'npm'
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: npm ci
|
|
29
|
+
|
|
30
|
+
- name: Run Prettier check
|
|
31
|
+
run: npm run format:check
|
|
32
|
+
|
|
33
|
+
- name: Run ESLint
|
|
34
|
+
run: npm run lint
|
|
35
|
+
|
|
36
|
+
- name: Run Type Check
|
|
37
|
+
run: npm run check
|
|
38
|
+
|
|
39
|
+
test:
|
|
40
|
+
name: Run Tests (Unit & E2E)
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- name: Checkout repository
|
|
44
|
+
uses: actions/checkout@v4
|
|
45
|
+
|
|
46
|
+
- name: Setup Node.js
|
|
47
|
+
uses: actions/setup-node@v4
|
|
48
|
+
with:
|
|
49
|
+
node-version: 22
|
|
50
|
+
cache: 'npm'
|
|
51
|
+
|
|
52
|
+
- name: Install dependencies
|
|
53
|
+
run: npm ci
|
|
54
|
+
|
|
55
|
+
- name: Install Playwright Browsers
|
|
56
|
+
run: npx playwright install --with-deps
|
|
57
|
+
|
|
58
|
+
- name: Run Tests
|
|
59
|
+
run: npm run test
|
package/README.md
CHANGED
|
@@ -29,9 +29,9 @@ Let's set up a secure, sandboxed agent named Jeeves using the Gemini CLI templat
|
|
|
29
29
|
npm install -g clawmini
|
|
30
30
|
|
|
31
31
|
# 2. Initialize a workspace and create your first sandboxed agent
|
|
32
|
-
# For a more
|
|
32
|
+
# Note: For a more basic experience, you can use the 'gemini' template instead
|
|
33
33
|
mkdir my-workspace && cd my-workspace
|
|
34
|
-
clawmini init --agent jeeves --agent-template gemini --environment macos
|
|
34
|
+
clawmini init --agent jeeves --agent-template gemini-claw --environment macos
|
|
35
35
|
|
|
36
36
|
# 3. Start the background daemon
|
|
37
37
|
clawmini up
|
|
@@ -42,6 +42,8 @@ clawmini web
|
|
|
42
42
|
|
|
43
43
|
**Try asking Jeeves:** _"Summarize the recent changes in my git repository."_ Jeeves will run securely in its sandbox, read the diffs, and report back.
|
|
44
44
|
|
|
45
|
+
**What's going on?** When you send a message, Clawmini looks at the chat+message and then launches Gemini CLI with your message in the `jeeves/` directory. The `jeeves/` directory is set up with OpenClaw-like files and system prompt since you used the `gemini-claw` template. And since we chose the `macos` environment, Gemini CLI will run in a built-in Seatbelt sandbox that prevents it from editing anything outside your workspace folder. We additionally give Gemini CLI ways to schedule reminders and recurring tasks, send files, and request permissions to run sensitive commands (see Permission Requests).
|
|
46
|
+
|
|
45
47
|
### Common Slash Commands
|
|
46
48
|
|
|
47
49
|
You can use these built-in slash commands in your chat interfaces:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/adapter-discord/index.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/adapter-discord/index.ts"],"mappings":";iBAUsB,IAAA,CAAA,GAAI,OAAA"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { l as getSocketPath, o as getClawminiDir, u as getWorkspaceRoot } from "../workspace-
|
|
3
|
-
import { t as createUnixSocketFetch } from "../fetch-
|
|
2
|
+
import { l as getSocketPath, o as getClawminiDir, u as getWorkspaceRoot } from "../workspace-DjoNjhW0.mjs";
|
|
3
|
+
import { t as createUnixSocketFetch } from "../fetch-Cn1XNyiO.mjs";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs$1 from "node:fs/promises";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
import { createTRPCClient, httpLink, httpSubscriptionLink, splitLink } from "@trpc/client";
|
|
9
10
|
import http from "node:http";
|
|
@@ -387,7 +388,6 @@ async function main() {
|
|
|
387
388
|
console.log(`Received message from ${message.author.tag}: ${message.content}`);
|
|
388
389
|
const downloadedFiles = [];
|
|
389
390
|
if (message.attachments.size > 0) {
|
|
390
|
-
const { getClawminiDir } = await import("../workspace-BC1ahx4R.mjs").then((n) => n.v);
|
|
391
391
|
const tmpDir = path.join(getClawminiDir(process.cwd()), "tmp", "discord");
|
|
392
392
|
await fs$1.mkdir(tmpDir, { recursive: true });
|
|
393
393
|
const maxSizeMB = config.maxAttachmentSizeMB ?? 25;
|
|
@@ -446,7 +446,16 @@ async function main() {
|
|
|
446
446
|
process.exit(1);
|
|
447
447
|
}
|
|
448
448
|
}
|
|
449
|
-
|
|
449
|
+
if ((() => {
|
|
450
|
+
try {
|
|
451
|
+
if (typeof process === "undefined" || !process.argv || process.argv.length < 2) return false;
|
|
452
|
+
const argv1 = process.argv[1];
|
|
453
|
+
if (!argv1) return false;
|
|
454
|
+
return path.resolve(argv1) === path.resolve(fileURLToPath(import.meta.url));
|
|
455
|
+
} catch {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
})()) main().catch((error) => {
|
|
450
459
|
console.error("Unhandled error in Discord Adapter:", error);
|
|
451
460
|
process.exit(1);
|
|
452
461
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["fsPromises","fsPromises","fs"],"sources":["../../src/adapter-discord/config.ts","../../src/shared/event-source.ts","../../src/adapter-discord/client.ts","../../src/adapter-discord/state.ts","../../src/adapter-discord/forwarder.ts","../../src/adapter-discord/index.ts"],"sourcesContent":["import fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\nimport fs from 'node:fs';\n\nexport const DiscordConfigSchema = z.looseObject({\n botToken: z.string().min(1, 'Discord Bot Token is required.'),\n authorizedUserId: z.string().min(1, 'Authorized Discord User ID is required.'),\n chatId: z.string().default('default'),\n maxAttachmentSizeMB: z.number().default(25).optional(),\n});\n\nexport type DiscordConfig = z.infer<typeof DiscordConfigSchema>;\n\nexport function getDiscordConfigPath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'discord', 'config.json');\n}\n\nexport async function readDiscordConfig(startDir = process.cwd()): Promise<DiscordConfig | null> {\n const configPath = getDiscordConfigPath(startDir);\n try {\n const data = await fsPromises.readFile(configPath, 'utf-8');\n const parsed = JSON.parse(data);\n const result = DiscordConfigSchema.safeParse(parsed);\n if (!result.success) {\n console.error('Invalid Discord configuration:', result.error.format());\n return null;\n }\n return result.data;\n } catch {\n // Return null if file doesn't exist or is invalid JSON\n return null;\n }\n}\n\nexport async function initDiscordConfig(startDir = process.cwd()): Promise<void> {\n const configPath = getDiscordConfigPath(startDir);\n const configDir = path.dirname(configPath);\n\n await fsPromises.mkdir(configDir, { recursive: true });\n\n if (fs.existsSync(configPath)) {\n console.log(`Config file already exists at ${configPath}`);\n return;\n }\n\n const templateConfig = {\n botToken: 'YOUR_DISCORD_BOT_TOKEN',\n authorizedUserId: 'YOUR_DISCORD_USER_ID',\n chatId: 'default',\n };\n\n await fsPromises.writeFile(configPath, JSON.stringify(templateConfig, null, 2), 'utf-8');\n console.log(`Created template configuration file at ${configPath}`);\n console.log('Please update it with your actual Discord Bot Token and User ID.');\n}\n\nexport function isAuthorized(userId: string, authorizedUserId: string): boolean {\n return userId === authorizedUserId;\n}\n","import http from 'node:http';\n\nexport function createUnixSocketEventSource(socketPath: string) {\n return class UnixSocketEventSource {\n public readyState: number = 0; // CONNECTING\n public readonly CONNECTING = 0;\n public readonly OPEN = 1;\n public readonly CLOSED = 2;\n\n req: http.ClientRequest | null = null;\n listeners: Record<string, ((event: Record<string, unknown>) => void)[]> = {};\n\n constructor(url: string, init?: Record<string, unknown>) {\n const parsedUrl = new URL(url);\n\n const options: http.RequestOptions = {\n socketPath,\n path: parsedUrl.pathname + parsedUrl.search,\n method: 'GET',\n headers: {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...(init?.headers as Record<string, string> | undefined),\n },\n };\n\n this.req = http.request(options, (res) => {\n if (res.statusCode === 200) {\n this.readyState = this.OPEN;\n this.dispatchEvent({ type: 'open' });\n } else {\n this.readyState = this.CLOSED;\n this.dispatchEvent({\n type: 'error',\n message: `Unexpected status code: ${res.statusCode}`,\n });\n return;\n }\n\n let buffer = '';\n res.on('data', (chunk) => {\n buffer += chunk.toString('utf-8');\n const lines = buffer.split(/\\r?\\n\\r?\\n/);\n buffer = lines.pop() || '';\n\n for (const block of lines) {\n this.parseBlock(block);\n }\n });\n\n res.on('end', () => {\n if (buffer) this.parseBlock(buffer);\n this.readyState = this.CLOSED;\n this.dispatchEvent({ type: 'close' });\n });\n });\n\n this.req.on('error', (err) => {\n this.readyState = this.CLOSED;\n this.dispatchEvent({ type: 'error', error: err });\n });\n\n this.req.end();\n }\n\n parseBlock(block: string) {\n if (!block.trim()) return;\n\n const lines = block.split(/\\r?\\n/);\n let eventType = 'message';\n let data = '';\n let id = '';\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith('data: ')) {\n data += (data ? '\\n' : '') + line.slice(6);\n } else if (line.startsWith('id: ')) {\n id = line.slice(4).trim();\n }\n }\n\n if (data) {\n this.dispatchEvent({\n type: eventType,\n data,\n lastEventId: id,\n });\n }\n }\n\n public addEventListener(type: string, listener: (event: Record<string, unknown>) => void) {\n if (!this.listeners[type]) {\n this.listeners[type] = [];\n }\n this.listeners[type].push(listener);\n }\n\n public removeEventListener(type: string, listener: (event: Record<string, unknown>) => void) {\n if (!this.listeners[type]) return;\n this.listeners[type] = this.listeners[type].filter((l) => l !== listener);\n }\n\n dispatchEvent(event: Record<string, unknown>) {\n const type = event.type as string;\n if (this.listeners[type]) {\n for (const listener of this.listeners[type]) {\n listener(event);\n }\n }\n }\n\n public close() {\n this.readyState = this.CLOSED;\n if (this.req) {\n this.req.destroy();\n }\n }\n };\n}\n","import { createTRPCClient, httpLink, splitLink, httpSubscriptionLink } from '@trpc/client';\nimport type { AppRouter } from '../daemon/router.js';\nimport { getSocketPath } from '../shared/workspace.js';\nimport { createUnixSocketFetch } from '../shared/fetch.js';\nimport { createUnixSocketEventSource } from '../shared/event-source.js';\nimport fs from 'node:fs';\n\n/**\n * Creates a TRPC client that connects to the Clawmini daemon via a Unix socket.\n *\n * @param options - Configuration options for the client.\n * @returns A TRPC client instance for the AppRouter.\n */\nexport function getTRPCClient(options: { socketPath?: string } = {}) {\n const socketPath = options.socketPath ?? getSocketPath();\n\n if (!fs.existsSync(socketPath)) {\n throw new Error(`Daemon not running. Socket not found at ${socketPath}`);\n }\n\n const customFetch = createUnixSocketFetch(socketPath);\n const CustomEventSource = createUnixSocketEventSource(socketPath);\n\n return createTRPCClient<AppRouter>({\n links: [\n splitLink({\n condition(op) {\n return op.type === 'subscription';\n },\n true: httpSubscriptionLink({\n url: 'http://localhost',\n EventSource: CustomEventSource,\n }),\n false: httpLink({\n url: 'http://localhost',\n fetch: customFetch,\n }),\n }),\n ],\n });\n}\n","import fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\n\nexport const DiscordStateSchema = z.object({\n lastSyncedMessageId: z.string().optional(),\n});\n\nexport type DiscordState = z.infer<typeof DiscordStateSchema>;\n\nexport function getDiscordStatePath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'discord', 'state.json');\n}\n\nexport async function readDiscordState(startDir = process.cwd()): Promise<DiscordState> {\n const statePath = getDiscordStatePath(startDir);\n try {\n const data = await fsPromises.readFile(statePath, 'utf-8');\n const parsed = JSON.parse(data);\n const result = DiscordStateSchema.safeParse(parsed);\n if (!result.success) {\n return { lastSyncedMessageId: undefined };\n }\n return result.data;\n } catch {\n // Return default state if file doesn't exist or is invalid JSON\n return { lastSyncedMessageId: undefined };\n }\n}\n\nexport async function writeDiscordState(\n state: DiscordState,\n startDir = process.cwd()\n): Promise<void> {\n const statePath = getDiscordStatePath(startDir);\n const dir = path.dirname(statePath);\n try {\n await fsPromises.mkdir(dir, { recursive: true });\n await fsPromises.writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');\n } catch (err) {\n console.error(`Failed to write Discord state to ${statePath}:`, err);\n }\n}\n","import type { Client, MessageCreateOptions } from 'discord.js';\nimport path from 'node:path';\nimport type { getTRPCClient } from './client.js';\nimport { readDiscordState, writeDiscordState } from './state.js';\nimport type { ChatMessage, CommandLogMessage } from '../shared/chats.js';\nimport { getWorkspaceRoot } from '../shared/workspace.js';\n\nexport async function startDaemonToDiscordForwarder(\n client: Client,\n trpc: ReturnType<typeof getTRPCClient>,\n discordUserId: string,\n chatId: string = 'default',\n signal?: AbortSignal\n) {\n const state = await readDiscordState();\n let lastMessageId = state.lastSyncedMessageId;\n\n // 1. If we don't have a lastMessageId, get the most recent one from the daemon\n // to avoid sending the entire chat history on first run.\n if (!lastMessageId) {\n try {\n const messages = await trpc.getMessages.query({ chatId, limit: 1 });\n if (Array.isArray(messages) && messages.length > 0) {\n const lastMsg = messages[messages.length - 1];\n if (lastMsg) {\n lastMessageId = lastMsg.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId });\n }\n }\n } catch (error) {\n if (signal?.aborted) return;\n console.error('Failed to fetch initial messages from daemon:', error);\n }\n }\n\n console.log(\n `Starting daemon-to-discord forwarder for chat ${chatId}, lastMessageId: ${lastMessageId}`\n );\n\n let retryDelay = 1000;\n const maxRetryDelay = 30000;\n\n // 2. Start the observation loop using tRPC subscription\n return new Promise<void>((resolve) => {\n let subscription: { unsubscribe: () => void } | null = null;\n let messageQueue = Promise.resolve();\n\n const connect = () => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n subscription = trpc.waitForMessages.subscribe(\n { chatId, lastMessageId },\n {\n onData: (messages) => {\n retryDelay = 1000; // Reset retry delay on successful data\n\n if (!Array.isArray(messages) || messages.length === 0) {\n return;\n }\n\n // Queue processing to ensure sequential execution\n messageQueue = messageQueue.then(async () => {\n for (const rawMessage of messages) {\n if (signal?.aborted) break;\n\n const message = rawMessage as ChatMessage;\n\n // Only forward logs (agent responses, system messages)\n if (message.role === 'log') {\n const logMessage = message as CommandLogMessage;\n\n if (logMessage.level === 'verbose') {\n lastMessageId = logMessage.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(\n console.error\n );\n continue;\n }\n\n const hasContent = !!logMessage.content?.trim();\n const hasFiles = Array.isArray(logMessage.files) && logMessage.files.length > 0;\n\n // The daemon stores logMessage.files as paths relative to the WORKSPACE directory\n // (the directory containing .clawmini). We must resolve these against the current\n // workspace root so discord.js can successfully locate and read the files.\n let absoluteFiles: string[] = [];\n if (hasFiles) {\n const workspaceRoot = getWorkspaceRoot(process.cwd());\n absoluteFiles = logMessage.files!.map((f) => path.resolve(workspaceRoot, f));\n }\n\n if (!hasContent && !hasFiles) {\n lastMessageId = logMessage.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(\n console.error\n );\n continue;\n }\n\n try {\n const user = await client.users.fetch(discordUserId);\n const dm = await user.createDM();\n\n // Discord has a 2000 character limit for messages.\n if (hasContent && logMessage.content.length > 2000) {\n const chunks = chunkString(logMessage.content, 2000);\n for (let i = 0; i < chunks.length; i++) {\n if (signal?.aborted) break;\n const chunkOptions: MessageCreateOptions = { content: chunks[i] as string };\n if (i === chunks.length - 1 && hasFiles) {\n chunkOptions.files = absoluteFiles;\n }\n await dm.send(chunkOptions);\n }\n } else {\n const options: MessageCreateOptions = {};\n if (hasContent) {\n options.content = logMessage.content;\n }\n if (hasFiles) {\n options.files = absoluteFiles;\n }\n await dm.send(options);\n }\n } catch (error) {\n console.error(\n `Failed to send message to Discord user ${discordUserId}:`,\n error\n );\n // We don't advance lastMessageId if sending failed\n break;\n }\n }\n\n lastMessageId = message.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(\n console.error\n );\n }\n });\n },\n onError: (error) => {\n console.error(\n `Error in daemon-to-discord forwarder subscription. Retrying in ${retryDelay}ms.`,\n error\n );\n subscription?.unsubscribe();\n subscription = null;\n\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n setTimeout(() => {\n retryDelay = Math.min(retryDelay * 2, maxRetryDelay);\n connect();\n }, retryDelay);\n },\n onComplete: () => {\n subscription = null;\n if (!signal?.aborted) {\n setTimeout(() => connect(), retryDelay);\n } else {\n resolve();\n }\n },\n }\n );\n };\n\n let typingSubscription: { unsubscribe: () => void } | null = null;\n let typingRetryDelay = 1000;\n\n const connectTyping = () => {\n if (signal?.aborted) {\n return;\n }\n\n typingSubscription = trpc.waitForTyping.subscribe(\n { chatId },\n {\n onData: async (event) => {\n typingRetryDelay = 1000; // Reset retry delay on successful data\n if (!event) return;\n\n try {\n const user = await client.users.fetch(discordUserId);\n const dm = await user.createDM();\n await dm.sendTyping();\n } catch (error) {\n console.error(\n `Failed to send typing indicator to Discord user ${discordUserId}:`,\n error\n );\n }\n },\n onError: (error) => {\n console.error(\n `Error in daemon-to-discord typing forwarder subscription. Retrying in ${typingRetryDelay}ms.`,\n error\n );\n typingSubscription?.unsubscribe();\n typingSubscription = null;\n\n if (signal?.aborted) {\n return;\n }\n\n setTimeout(() => {\n typingRetryDelay = Math.min(typingRetryDelay * 2, maxRetryDelay);\n connectTyping();\n }, typingRetryDelay);\n },\n onComplete: () => {\n typingSubscription = null;\n if (!signal?.aborted) {\n setTimeout(() => connectTyping(), typingRetryDelay);\n }\n },\n }\n );\n };\n\n connect();\n connectTyping();\n\n signal?.addEventListener('abort', () => {\n subscription?.unsubscribe();\n typingSubscription?.unsubscribe();\n resolve();\n });\n });\n}\n\nfunction chunkString(str: string, size: number): string[] {\n const chunks: string[] = [];\n const chars = Array.from(str);\n for (let i = 0; i < chars.length; i += size) {\n chunks.push(chars.slice(i, i + size).join(''));\n }\n return chunks;\n}\n","#!/usr/bin/env node\n\nimport { Client, Events, GatewayIntentBits, Partials } from 'discord.js';\nimport { readDiscordConfig, isAuthorized, initDiscordConfig } from './config.js';\nimport { getTRPCClient } from './client.js';\nimport { startDaemonToDiscordForwarder } from './forwarder.js';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nexport async function main() {\n const args = process.argv.slice(2);\n\n if (args[0] === 'init') {\n await initDiscordConfig();\n return;\n }\n\n console.log('Discord Adapter starting...');\n\n const config = await readDiscordConfig();\n if (!config) {\n console.error(\n 'Failed to load Discord configuration. Please ensure .clawmini/adapters/discord/config.json exists and is valid.'\n );\n process.exit(1);\n }\n\n const trpc = getTRPCClient();\n\n const client = new Client({\n intents: [GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent],\n partials: [Partials.Channel],\n });\n\n client.once(Events.ClientReady, (readyClient) => {\n console.log(`Ready! Logged in as ${readyClient.user.tag}`);\n\n // Start forwarding from daemon to Discord\n startDaemonToDiscordForwarder(readyClient, trpc, config.authorizedUserId, config.chatId).catch(\n (error) => {\n console.error('Error in daemon-to-discord forwarder:', error);\n }\n );\n });\n\n client.on(Events.MessageCreate, async (message) => {\n // Ignore messages from the bot itself\n if (message.author.id === client.user?.id) return;\n\n // Only handle DM messages\n if (message.guild) return;\n\n // Check if the user is authorized\n if (!isAuthorized(message.author.id, config.authorizedUserId)) {\n console.log(\n `Unauthorized message from ${message.author.tag} (${message.author.id}) ignored.`\n );\n return;\n }\n\n console.log(`Received message from ${message.author.tag}: ${message.content}`);\n\n const downloadedFiles: string[] = [];\n if (message.attachments.size > 0) {\n const { getClawminiDir } = await import('../shared/workspace.js');\n const tmpDir = path.join(getClawminiDir(process.cwd()), 'tmp', 'discord');\n await fs.mkdir(tmpDir, { recursive: true });\n const maxSizeMB = config.maxAttachmentSizeMB ?? 25;\n const maxSizeBytes = maxSizeMB * 1024 * 1024;\n\n for (const attachment of message.attachments.values()) {\n if (attachment.size > maxSizeBytes) {\n console.warn(\n `Attachment ${attachment.name} exceeds size limit (${maxSizeMB}MB). Ignoring.`\n );\n await message.reply(\n `Warning: Attachment ${attachment.name} exceeds the size limit of ${maxSizeMB}MB and was ignored.`\n );\n continue;\n }\n\n try {\n const res = await fetch(attachment.url);\n if (!res.ok) {\n console.error(`Failed to download attachment ${attachment.name}`);\n continue;\n }\n\n const uniqueName = `${Date.now()}-${attachment.name}`;\n const filePath = path.join(tmpDir, uniqueName);\n const arrayBuffer = await res.arrayBuffer();\n await fs.writeFile(filePath, Buffer.from(arrayBuffer));\n downloadedFiles.push(filePath);\n } catch (err) {\n console.error(`Error downloading attachment ${attachment.name}:`, err);\n }\n }\n }\n\n let finalContent = message.content;\n\n if (message.reference && message.reference.messageId) {\n try {\n const referencedMessage = await message.fetchReference();\n if (referencedMessage && referencedMessage.content) {\n const quotedContent = referencedMessage.content\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n');\n finalContent = `${quotedContent}\\n${finalContent}`;\n }\n } catch (err) {\n console.error('Failed to fetch referenced message:', err);\n }\n }\n\n console.log(`Forwarding message to daemon: ${finalContent}`);\n try {\n await trpc.sendMessage.mutate({\n type: 'send-message',\n client: 'cli',\n data: {\n message: finalContent,\n chatId: config.chatId,\n files: downloadedFiles.length > 0 ? downloadedFiles : undefined,\n adapter: 'discord',\n noWait: true,\n },\n });\n console.log('Message forwarded to daemon successfully.');\n } catch (error) {\n console.error('Failed to forward message to daemon:', error);\n }\n });\n\n try {\n await client.login(config.botToken);\n } catch (error) {\n console.error('Failed to login to Discord:', error);\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error('Unhandled error in Discord Adapter:', error);\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["fsPromises","fsPromises","fs"],"sources":["../../src/adapter-discord/config.ts","../../src/shared/event-source.ts","../../src/adapter-discord/client.ts","../../src/adapter-discord/state.ts","../../src/adapter-discord/forwarder.ts","../../src/adapter-discord/index.ts"],"sourcesContent":["import fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\nimport fs from 'node:fs';\n\nexport const DiscordConfigSchema = z.looseObject({\n botToken: z.string().min(1, 'Discord Bot Token is required.'),\n authorizedUserId: z.string().min(1, 'Authorized Discord User ID is required.'),\n chatId: z.string().default('default'),\n maxAttachmentSizeMB: z.number().default(25).optional(),\n});\n\nexport type DiscordConfig = z.infer<typeof DiscordConfigSchema>;\n\nexport function getDiscordConfigPath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'discord', 'config.json');\n}\n\nexport async function readDiscordConfig(startDir = process.cwd()): Promise<DiscordConfig | null> {\n const configPath = getDiscordConfigPath(startDir);\n try {\n const data = await fsPromises.readFile(configPath, 'utf-8');\n const parsed = JSON.parse(data);\n const result = DiscordConfigSchema.safeParse(parsed);\n if (!result.success) {\n console.error('Invalid Discord configuration:', result.error.format());\n return null;\n }\n return result.data;\n } catch {\n // Return null if file doesn't exist or is invalid JSON\n return null;\n }\n}\n\nexport async function initDiscordConfig(startDir = process.cwd()): Promise<void> {\n const configPath = getDiscordConfigPath(startDir);\n const configDir = path.dirname(configPath);\n\n await fsPromises.mkdir(configDir, { recursive: true });\n\n if (fs.existsSync(configPath)) {\n console.log(`Config file already exists at ${configPath}`);\n return;\n }\n\n const templateConfig = {\n botToken: 'YOUR_DISCORD_BOT_TOKEN',\n authorizedUserId: 'YOUR_DISCORD_USER_ID',\n chatId: 'default',\n };\n\n await fsPromises.writeFile(configPath, JSON.stringify(templateConfig, null, 2), 'utf-8');\n console.log(`Created template configuration file at ${configPath}`);\n console.log('Please update it with your actual Discord Bot Token and User ID.');\n}\n\nexport function isAuthorized(userId: string, authorizedUserId: string): boolean {\n return userId === authorizedUserId;\n}\n","import http from 'node:http';\n\nexport function createUnixSocketEventSource(socketPath: string) {\n return class UnixSocketEventSource {\n public readyState: number = 0; // CONNECTING\n public readonly CONNECTING = 0;\n public readonly OPEN = 1;\n public readonly CLOSED = 2;\n\n req: http.ClientRequest | null = null;\n listeners: Record<string, ((event: Record<string, unknown>) => void)[]> = {};\n\n constructor(url: string, init?: Record<string, unknown>) {\n const parsedUrl = new URL(url);\n\n const options: http.RequestOptions = {\n socketPath,\n path: parsedUrl.pathname + parsedUrl.search,\n method: 'GET',\n headers: {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...(init?.headers as Record<string, string> | undefined),\n },\n };\n\n this.req = http.request(options, (res) => {\n if (res.statusCode === 200) {\n this.readyState = this.OPEN;\n this.dispatchEvent({ type: 'open' });\n } else {\n this.readyState = this.CLOSED;\n this.dispatchEvent({\n type: 'error',\n message: `Unexpected status code: ${res.statusCode}`,\n });\n return;\n }\n\n let buffer = '';\n res.on('data', (chunk) => {\n buffer += chunk.toString('utf-8');\n const lines = buffer.split(/\\r?\\n\\r?\\n/);\n buffer = lines.pop() || '';\n\n for (const block of lines) {\n this.parseBlock(block);\n }\n });\n\n res.on('end', () => {\n if (buffer) this.parseBlock(buffer);\n this.readyState = this.CLOSED;\n this.dispatchEvent({ type: 'close' });\n });\n });\n\n this.req.on('error', (err) => {\n this.readyState = this.CLOSED;\n this.dispatchEvent({ type: 'error', error: err });\n });\n\n this.req.end();\n }\n\n parseBlock(block: string) {\n if (!block.trim()) return;\n\n const lines = block.split(/\\r?\\n/);\n let eventType = 'message';\n let data = '';\n let id = '';\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith('data: ')) {\n data += (data ? '\\n' : '') + line.slice(6);\n } else if (line.startsWith('id: ')) {\n id = line.slice(4).trim();\n }\n }\n\n if (data) {\n this.dispatchEvent({\n type: eventType,\n data,\n lastEventId: id,\n });\n }\n }\n\n public addEventListener(type: string, listener: (event: Record<string, unknown>) => void) {\n if (!this.listeners[type]) {\n this.listeners[type] = [];\n }\n this.listeners[type].push(listener);\n }\n\n public removeEventListener(type: string, listener: (event: Record<string, unknown>) => void) {\n if (!this.listeners[type]) return;\n this.listeners[type] = this.listeners[type].filter((l) => l !== listener);\n }\n\n dispatchEvent(event: Record<string, unknown>) {\n const type = event.type as string;\n if (this.listeners[type]) {\n for (const listener of this.listeners[type]) {\n listener(event);\n }\n }\n }\n\n public close() {\n this.readyState = this.CLOSED;\n if (this.req) {\n this.req.destroy();\n }\n }\n };\n}\n","import { createTRPCClient, httpLink, splitLink, httpSubscriptionLink } from '@trpc/client';\nimport type { UserRouter as AppRouter } from '../daemon/api/index.js';\nimport { getSocketPath } from '../shared/workspace.js';\nimport { createUnixSocketFetch } from '../shared/fetch.js';\nimport { createUnixSocketEventSource } from '../shared/event-source.js';\nimport fs from 'node:fs';\n\n/**\n * Creates a TRPC client that connects to the Clawmini daemon via a Unix socket.\n *\n * @param options - Configuration options for the client.\n * @returns A TRPC client instance for the AppRouter.\n */\nexport function getTRPCClient(options: { socketPath?: string } = {}) {\n const socketPath = options.socketPath ?? getSocketPath();\n\n if (!fs.existsSync(socketPath)) {\n throw new Error(`Daemon not running. Socket not found at ${socketPath}`);\n }\n\n const customFetch = createUnixSocketFetch(socketPath);\n const CustomEventSource = createUnixSocketEventSource(socketPath);\n\n return createTRPCClient<AppRouter>({\n links: [\n splitLink({\n condition(op) {\n return op.type === 'subscription';\n },\n true: httpSubscriptionLink({\n url: 'http://localhost',\n EventSource: CustomEventSource,\n }),\n false: httpLink({\n url: 'http://localhost',\n fetch: customFetch,\n }),\n }),\n ],\n });\n}\n","import fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\n\nexport const DiscordStateSchema = z.object({\n lastSyncedMessageId: z.string().optional(),\n});\n\nexport type DiscordState = z.infer<typeof DiscordStateSchema>;\n\nexport function getDiscordStatePath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'discord', 'state.json');\n}\n\nexport async function readDiscordState(startDir = process.cwd()): Promise<DiscordState> {\n const statePath = getDiscordStatePath(startDir);\n try {\n const data = await fsPromises.readFile(statePath, 'utf-8');\n const parsed = JSON.parse(data);\n const result = DiscordStateSchema.safeParse(parsed);\n if (!result.success) {\n return { lastSyncedMessageId: undefined };\n }\n return result.data;\n } catch {\n // Return default state if file doesn't exist or is invalid JSON\n return { lastSyncedMessageId: undefined };\n }\n}\n\nexport async function writeDiscordState(\n state: DiscordState,\n startDir = process.cwd()\n): Promise<void> {\n const statePath = getDiscordStatePath(startDir);\n const dir = path.dirname(statePath);\n try {\n await fsPromises.mkdir(dir, { recursive: true });\n await fsPromises.writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');\n } catch (err) {\n console.error(`Failed to write Discord state to ${statePath}:`, err);\n }\n}\n","import type { Client, MessageCreateOptions } from 'discord.js';\nimport path from 'node:path';\nimport type { getTRPCClient } from './client.js';\nimport { readDiscordState, writeDiscordState } from './state.js';\nimport type { ChatMessage, CommandLogMessage } from '../shared/chats.js';\nimport { getWorkspaceRoot } from '../shared/workspace.js';\n\nexport async function startDaemonToDiscordForwarder(\n client: Client,\n trpc: ReturnType<typeof getTRPCClient>,\n discordUserId: string,\n chatId: string = 'default',\n signal?: AbortSignal\n) {\n const state = await readDiscordState();\n let lastMessageId = state.lastSyncedMessageId;\n\n // 1. If we don't have a lastMessageId, get the most recent one from the daemon\n // to avoid sending the entire chat history on first run.\n if (!lastMessageId) {\n try {\n const messages = await trpc.getMessages.query({ chatId, limit: 1 });\n if (Array.isArray(messages) && messages.length > 0) {\n const lastMsg = messages[messages.length - 1];\n if (lastMsg) {\n lastMessageId = lastMsg.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId });\n }\n }\n } catch (error) {\n if (signal?.aborted) return;\n console.error('Failed to fetch initial messages from daemon:', error);\n }\n }\n\n console.log(\n `Starting daemon-to-discord forwarder for chat ${chatId}, lastMessageId: ${lastMessageId}`\n );\n\n let retryDelay = 1000;\n const maxRetryDelay = 30000;\n\n // 2. Start the observation loop using tRPC subscription\n return new Promise<void>((resolve) => {\n let subscription: { unsubscribe: () => void } | null = null;\n let messageQueue = Promise.resolve();\n\n const connect = () => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n subscription = trpc.waitForMessages.subscribe(\n { chatId, lastMessageId },\n {\n onData: (messages) => {\n retryDelay = 1000; // Reset retry delay on successful data\n\n if (!Array.isArray(messages) || messages.length === 0) {\n return;\n }\n\n // Queue processing to ensure sequential execution\n messageQueue = messageQueue.then(async () => {\n for (const rawMessage of messages) {\n if (signal?.aborted) break;\n\n const message = rawMessage as ChatMessage;\n\n // Only forward logs (agent responses, system messages)\n if (message.role === 'log') {\n const logMessage = message as CommandLogMessage;\n\n if (logMessage.level === 'verbose') {\n lastMessageId = logMessage.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(\n console.error\n );\n continue;\n }\n\n const hasContent = !!logMessage.content?.trim();\n const hasFiles = Array.isArray(logMessage.files) && logMessage.files.length > 0;\n\n // The daemon stores logMessage.files as paths relative to the WORKSPACE directory\n // (the directory containing .clawmini). We must resolve these against the current\n // workspace root so discord.js can successfully locate and read the files.\n let absoluteFiles: string[] = [];\n if (hasFiles) {\n const workspaceRoot = getWorkspaceRoot(process.cwd());\n absoluteFiles = logMessage.files!.map((f) => path.resolve(workspaceRoot, f));\n }\n\n if (!hasContent && !hasFiles) {\n lastMessageId = logMessage.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(\n console.error\n );\n continue;\n }\n\n try {\n const user = await client.users.fetch(discordUserId);\n const dm = await user.createDM();\n\n // Discord has a 2000 character limit for messages.\n if (hasContent && logMessage.content.length > 2000) {\n const chunks = chunkString(logMessage.content, 2000);\n for (let i = 0; i < chunks.length; i++) {\n if (signal?.aborted) break;\n const chunkOptions: MessageCreateOptions = { content: chunks[i] as string };\n if (i === chunks.length - 1 && hasFiles) {\n chunkOptions.files = absoluteFiles;\n }\n await dm.send(chunkOptions);\n }\n } else {\n const options: MessageCreateOptions = {};\n if (hasContent) {\n options.content = logMessage.content;\n }\n if (hasFiles) {\n options.files = absoluteFiles;\n }\n await dm.send(options);\n }\n } catch (error) {\n console.error(\n `Failed to send message to Discord user ${discordUserId}:`,\n error\n );\n // We don't advance lastMessageId if sending failed\n break;\n }\n }\n\n lastMessageId = message.id;\n await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(\n console.error\n );\n }\n });\n },\n onError: (error) => {\n console.error(\n `Error in daemon-to-discord forwarder subscription. Retrying in ${retryDelay}ms.`,\n error\n );\n subscription?.unsubscribe();\n subscription = null;\n\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n setTimeout(() => {\n retryDelay = Math.min(retryDelay * 2, maxRetryDelay);\n connect();\n }, retryDelay);\n },\n onComplete: () => {\n subscription = null;\n if (!signal?.aborted) {\n setTimeout(() => connect(), retryDelay);\n } else {\n resolve();\n }\n },\n }\n );\n };\n\n let typingSubscription: { unsubscribe: () => void } | null = null;\n let typingRetryDelay = 1000;\n\n const connectTyping = () => {\n if (signal?.aborted) {\n return;\n }\n\n typingSubscription = trpc.waitForTyping.subscribe(\n { chatId },\n {\n onData: async (event) => {\n typingRetryDelay = 1000; // Reset retry delay on successful data\n if (!event) return;\n\n try {\n const user = await client.users.fetch(discordUserId);\n const dm = await user.createDM();\n await dm.sendTyping();\n } catch (error) {\n console.error(\n `Failed to send typing indicator to Discord user ${discordUserId}:`,\n error\n );\n }\n },\n onError: (error) => {\n console.error(\n `Error in daemon-to-discord typing forwarder subscription. Retrying in ${typingRetryDelay}ms.`,\n error\n );\n typingSubscription?.unsubscribe();\n typingSubscription = null;\n\n if (signal?.aborted) {\n return;\n }\n\n setTimeout(() => {\n typingRetryDelay = Math.min(typingRetryDelay * 2, maxRetryDelay);\n connectTyping();\n }, typingRetryDelay);\n },\n onComplete: () => {\n typingSubscription = null;\n if (!signal?.aborted) {\n setTimeout(() => connectTyping(), typingRetryDelay);\n }\n },\n }\n );\n };\n\n connect();\n connectTyping();\n\n signal?.addEventListener('abort', () => {\n subscription?.unsubscribe();\n typingSubscription?.unsubscribe();\n resolve();\n });\n });\n}\n\nfunction chunkString(str: string, size: number): string[] {\n const chunks: string[] = [];\n const chars = Array.from(str);\n for (let i = 0; i < chars.length; i += size) {\n chunks.push(chars.slice(i, i + size).join(''));\n }\n return chunks;\n}\n","#!/usr/bin/env node\n\nimport { Client, Events, GatewayIntentBits, Partials } from 'discord.js';\nimport { readDiscordConfig, isAuthorized, initDiscordConfig } from './config.js';\nimport { getTRPCClient } from './client.js';\nimport { startDaemonToDiscordForwarder } from './forwarder.js';\nimport { getClawminiDir } from '../shared/workspace.js';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nexport async function main() {\n const args = process.argv.slice(2);\n\n if (args[0] === 'init') {\n await initDiscordConfig();\n return;\n }\n\n console.log('Discord Adapter starting...');\n\n const config = await readDiscordConfig();\n if (!config) {\n console.error(\n 'Failed to load Discord configuration. Please ensure .clawmini/adapters/discord/config.json exists and is valid.'\n );\n process.exit(1);\n }\n\n const trpc = getTRPCClient();\n\n const client = new Client({\n intents: [GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent],\n partials: [Partials.Channel],\n });\n\n client.once(Events.ClientReady, (readyClient) => {\n console.log(`Ready! Logged in as ${readyClient.user.tag}`);\n\n // Start forwarding from daemon to Discord\n startDaemonToDiscordForwarder(readyClient, trpc, config.authorizedUserId, config.chatId).catch(\n (error) => {\n console.error('Error in daemon-to-discord forwarder:', error);\n }\n );\n });\n\n client.on(Events.MessageCreate, async (message) => {\n // Ignore messages from the bot itself\n if (message.author.id === client.user?.id) return;\n\n // Only handle DM messages\n if (message.guild) return;\n\n // Check if the user is authorized\n if (!isAuthorized(message.author.id, config.authorizedUserId)) {\n console.log(\n `Unauthorized message from ${message.author.tag} (${message.author.id}) ignored.`\n );\n return;\n }\n\n console.log(`Received message from ${message.author.tag}: ${message.content}`);\n\n const downloadedFiles: string[] = [];\n if (message.attachments.size > 0) {\n const tmpDir = path.join(getClawminiDir(process.cwd()), 'tmp', 'discord');\n await fs.mkdir(tmpDir, { recursive: true });\n const maxSizeMB = config.maxAttachmentSizeMB ?? 25;\n const maxSizeBytes = maxSizeMB * 1024 * 1024;\n\n for (const attachment of message.attachments.values()) {\n if (attachment.size > maxSizeBytes) {\n console.warn(\n `Attachment ${attachment.name} exceeds size limit (${maxSizeMB}MB). Ignoring.`\n );\n await message.reply(\n `Warning: Attachment ${attachment.name} exceeds the size limit of ${maxSizeMB}MB and was ignored.`\n );\n continue;\n }\n\n try {\n const res = await fetch(attachment.url);\n if (!res.ok) {\n console.error(`Failed to download attachment ${attachment.name}`);\n continue;\n }\n\n const uniqueName = `${Date.now()}-${attachment.name}`;\n const filePath = path.join(tmpDir, uniqueName);\n const arrayBuffer = await res.arrayBuffer();\n await fs.writeFile(filePath, Buffer.from(arrayBuffer));\n downloadedFiles.push(filePath);\n } catch (err) {\n console.error(`Error downloading attachment ${attachment.name}:`, err);\n }\n }\n }\n\n let finalContent = message.content;\n\n if (message.reference && message.reference.messageId) {\n try {\n const referencedMessage = await message.fetchReference();\n if (referencedMessage && referencedMessage.content) {\n const quotedContent = referencedMessage.content\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n');\n finalContent = `${quotedContent}\\n${finalContent}`;\n }\n } catch (err) {\n console.error('Failed to fetch referenced message:', err);\n }\n }\n\n console.log(`Forwarding message to daemon: ${finalContent}`);\n try {\n await trpc.sendMessage.mutate({\n type: 'send-message',\n client: 'cli',\n data: {\n message: finalContent,\n chatId: config.chatId,\n files: downloadedFiles.length > 0 ? downloadedFiles : undefined,\n adapter: 'discord',\n noWait: true,\n },\n });\n console.log('Message forwarded to daemon successfully.');\n } catch (error) {\n console.error('Failed to forward message to daemon:', error);\n }\n });\n\n try {\n await client.login(config.botToken);\n } catch (error) {\n console.error('Failed to login to Discord:', error);\n process.exit(1);\n }\n}\n\nimport { fileURLToPath } from 'node:url';\n\nconst isMainModule = (() => {\n try {\n if (typeof process === 'undefined' || !process.argv || process.argv.length < 2) return false;\n const argv1 = process.argv[1];\n if (!argv1) return false;\n const p1 = path.resolve(argv1);\n const p2 = path.resolve(fileURLToPath(import.meta.url));\n return p1 === p2;\n } catch {\n return false;\n }\n})();\n\nif (isMainModule) {\n main().catch((error) => {\n console.error('Unhandled error in Discord Adapter:', error);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { S as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { t as createUnixSocketFetch } from "../fetch-BjZVyU3Z.mjs";
|
|
6
|
-
import { i as writeLiteScript, r as getLiteScriptContent, t as exportLiteToAllEnvironments } from "../lite-DBUuHsX0.mjs";
|
|
2
|
+
import { S as pathIsInsideDir, _ as readSettings, a as getAgent, b as writeChatSettings, d as isValidAgentId, f as listAgents, l as getSocketPath, m as readChatSettings, n as deleteAgent, o as getClawminiDir, r as enableEnvironment, t as applyTemplateToAgent, x as writeSettings, y as writeAgentSettings } from "../workspace-DjoNjhW0.mjs";
|
|
3
|
+
import { a as DEFAULT_CHAT_ID, c as deleteChat, d as getMessages, f as isValidChatId, i as writeLiteScript, l as getChatsDir, m as setDefaultChatId, p as listChats, r as getLiteScriptContent, s as createChat, t as exportLiteToAllEnvironments, u as getDefaultChatId } from "../lite-oSYSvaOr.mjs";
|
|
4
|
+
import { t as createUnixSocketFetch } from "../fetch-Cn1XNyiO.mjs";
|
|
7
5
|
import { Command } from "commander";
|
|
8
6
|
import fs from "node:fs";
|
|
9
7
|
import path from "node:path";
|
|
@@ -98,7 +96,10 @@ async function getDaemonClient(options = {}) {
|
|
|
98
96
|
logFile
|
|
99
97
|
]
|
|
100
98
|
}).unref();
|
|
101
|
-
|
|
99
|
+
for (let i = 0; i < 50; i++) {
|
|
100
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
101
|
+
if (fs.existsSync(socketPath)) break;
|
|
102
|
+
}
|
|
102
103
|
if (!fs.existsSync(socketPath)) throw new Error("Failed to start daemon.");
|
|
103
104
|
}
|
|
104
105
|
return createTRPCClient({ links: [httpLink({
|