clawmini 0.0.1 → 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 +61 -76
- 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 +8 -6
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +64 -10
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/daemon/index.mjs +732 -251
- 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/{COekwvP2.js → 8YNcRyEk.js} +1 -1
- package/dist/web/_app/immutable/chunks/{CSvS_NwK.js → DQoygso7.js} +1 -1
- package/dist/web/_app/immutable/entry/{app.B-vZe7PN.js → app.DO5eYwVz.js} +2 -2
- package/dist/web/_app/immutable/entry/start.D48mVn1m.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.B5WFN0zw.js → 0.B-0CcADM.js} +1 -1
- package/dist/web/_app/immutable/nodes/{1.D1wtJb2k.js → 1.FixKgvRO.js} +1 -1
- package/dist/web/_app/immutable/nodes/{3.BB5wCoBf.js → 3.ncP0xLO6.js} +1 -1
- package/dist/web/_app/immutable/nodes/{4.Dr2jvAXK.js → 4.CQYJEgv8.js} +1 -1
- package/dist/web/_app/immutable/nodes/{5.BJl7oM3b.js → 5.BpJUN6QH.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +6 -6
- package/dist/{workspace-CSgfo_2J.mjs → workspace-DjoNjhW0.mjs} +21 -40
- 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/docs/CLI_REFERENCE.md +35 -0
- package/docs/guides/sandbox_policies.md +12 -5
- 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/ambient.d.ts +2 -6
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +29 -29
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{COekwvP2.js → 8YNcRyEk.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{CSvS_NwK.js → DQoygso7.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/{app.B-vZe7PN.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.B5WFN0zw.js → 0.B-0CcADM.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{1.D1wtJb2k.js → 1.FixKgvRO.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{3.BB5wCoBf.js → 3.ncP0xLO6.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{4.Dr2jvAXK.js → 4.CQYJEgv8.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BJl7oM3b.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-DKgTeU7i.mjs +0 -91
- package/dist/chats-DKgTeU7i.mjs.map +0 -1
- package/dist/chats-Zd_HXDHx.mjs +0 -29
- package/dist/chats-Zd_HXDHx.mjs.map +0 -1
- package/dist/fs-B5wW0oaH.mjs +0 -14
- package/dist/fs-B5wW0oaH.mjs.map +0 -1
- package/dist/lite-Dl7WXyaH.mjs +0 -80
- package/dist/lite-Dl7WXyaH.mjs.map +0 -1
- package/dist/rolldown-runtime-95iHPtFO.mjs +0 -18
- package/dist/web/_app/immutable/entry/start.oP1AgKhs.js +0 -1
- package/dist/workspace-CSgfo_2J.mjs.map +0 -1
- package/src/daemon/router.ts +0 -510
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.oP1AgKhs.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
|
@@ -1,98 +1,93 @@
|
|
|
1
1
|
# Clawmini
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The secure, local-first orchestrator for your AI agents.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Clawmini gives you the power of a proactive personal AI assistant, without compromising your system's security. Unlike other local agents that run with unrestricted access to your machine, Clawmini is built from the ground up with **sandboxing**, **human-in-the-loop approvals**, and **strict network isolation**.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Safe Concurrency:** Automatically manages state and handles race conditions, queuing background commands safely to prevent file lock issues.
|
|
9
|
-
- **Built-in & Bring-Your-Own UI:** Includes a fast, beautifully designed SvelteKit Web UI to visually manage agents, chats, and monitor real-time execution. Alternatively, easily build and connect your own interfaces to its local API.
|
|
10
|
-
- **Agent Templates:** Quickly scaffold new agents using built-in or custom templates (e.g. `gemini`), automatically merging configuration settings.
|
|
11
|
-
- **Routers & Middleware Pipeline:** Process user messages through an extensible pipeline to dynamically alter content, target specific agents or sessions, and expand slash commands before they reach an agent.
|
|
12
|
-
- **Local File System Storage:** Everything is stored completely locally in `.clawmini/` within your workspace as transparent JSON/JSONL files. No cloud syncing required.
|
|
13
|
-
- **Sandboxed Agent API:** The daemon can optionally expose a secure HTTP server configured via the `api` setting in `.clawmini/settings.json`, allowing remote or containerized access. Give agents a minimal zero-dependency standalone client exported via `clawmini export-lite`. It securely authenticates with the Agent API using dynamically generated HMAC tokens (`CLAW_API_TOKEN`) to allow sandboxed agents to log messages and manage cron jobs without needing direct access to the host's Unix socket.
|
|
14
|
-
- **Proactivity:** Incoming messages or events from various external sources can be proactively routed back to the user or directly to the agent for autonomous handling.
|
|
7
|
+
Bring your favorite CLI agents (Gemini CLI, Claude Code, OpenAI Codex, etc.), and Clawmini provides the memory, the chat support, and the security boundaries.
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
## Why Clawmini?
|
|
17
10
|
|
|
18
|
-
|
|
11
|
+
🛡️ **Zero-Trust Security by Default**
|
|
12
|
+
Run agents safely. Clawmini uses built-in sandboxing (macOS seatbelt or containerized environments) to restrict what your AI can touch. Sensitive actions require your explicit approval before execution.
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
🧠 **Persistent Memory & Proactivity**
|
|
15
|
+
Your agents don't just react; they act. Clawmini allows agents to schedule recurring tasks (cron jobs), maintain long-term context across sessions, and proactively notify you of updates or route messages from external sources.
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
💻 **Beautiful Web UI & Chat App Extensibility**
|
|
18
|
+
No need to stay in the terminal. Manage your agents and chat with them through a fast, built-in Web interface running entirely on `localhost`. Hook up Discord to chat on-the-go, or easily build your adapters against the local API.
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
clawmini init --agent coder --agent-template gemini-cladding
|
|
20
|
+
🔌 **Bring Your Own Agent**
|
|
21
|
+
Clawmini isn't tied to one model. It orchestrates _any_ CLI-based agent, acting as the secure bridge between the AI and your filesystem.
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
clawmini web
|
|
30
|
-
```
|
|
23
|
+
## Quick Start: Meet Jeeves
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
Let's set up a secure, sandboxed agent named Jeeves using the Gemini CLI template.
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
```bash
|
|
28
|
+
# 1. Install globally
|
|
29
|
+
npm install -g clawmini
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
31
|
+
# 2. Initialize a workspace and create your first sandboxed agent
|
|
32
|
+
# Note: For a more basic experience, you can use the 'gemini' template instead
|
|
33
|
+
mkdir my-workspace && cd my-workspace
|
|
34
|
+
clawmini init --agent jeeves --agent-template gemini-claw --environment macos
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
# 3. Start the background daemon
|
|
37
|
+
clawmini up
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- `clawmini chats set-default <id>`: Update the globally configured default chat for the workspace.
|
|
39
|
+
# 4. Open the Web UI to start chatting!
|
|
40
|
+
clawmini web
|
|
41
|
+
```
|
|
47
42
|
|
|
48
|
-
|
|
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.
|
|
49
44
|
|
|
50
|
-
|
|
51
|
-
- `clawmini messages tail [-n NUM] [--json] [--chat <id>]`: Display the most recent messages and command logs in a chat.
|
|
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).
|
|
52
46
|
|
|
53
|
-
###
|
|
47
|
+
### Common Slash Commands
|
|
54
48
|
|
|
55
|
-
-
|
|
56
|
-
- `clawmini agents add <id> [-d, --directory <dir>] [-t, --template <name>] [-e, --env <KEY=VALUE>...]`: Create a new agent, optionally setting its working directory, applying a template, and environment variables.
|
|
57
|
-
- `clawmini agents update <id> [-d, --directory <dir>] [-e, --env <KEY=VALUE>...]`: Update an existing agent's configuration.
|
|
58
|
-
- `clawmini agents delete <id>`: Remove an agent.
|
|
49
|
+
You can use these built-in slash commands in your chat interfaces:
|
|
59
50
|
|
|
60
|
-
|
|
51
|
+
- `/new`: Clear previous context and start a new conversation thread.
|
|
52
|
+
- `/stop`: Stop all running commands and drop any queued messages.
|
|
53
|
+
- `/interrupt [message]`: Interrupt the current thinking, batching together all queued messages and sending them immediately.
|
|
54
|
+
- `/pending`: View any pending permission requests from your agent.
|
|
55
|
+
- `/approve [id]`: Approve a specific pending agent request.
|
|
56
|
+
- `/reject [id]`: Reject a specific pending agent request.
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
- `clawmini cron add <name> [--cron <expr> | --every <duration> | --at <iso-time>] [-m, --message <text>]`: Create a new scheduled job. Supports standard cron expressions, recurring intervals (e.g., `10m`), or one-off executions at a specific time.
|
|
64
|
-
- `clawmini cron delete <name> [--chat <id>]`: Remove an existing scheduled job.
|
|
58
|
+
### Guides & Integrations
|
|
65
59
|
|
|
66
|
-
|
|
60
|
+
- [Discord Integration Setup](./docs/guides/discord_adapter_setup.md)
|
|
61
|
+
- [Configuring Permission Requests](./docs/guides/sandbox_policies.md)
|
|
67
62
|
|
|
68
|
-
|
|
63
|
+
## How It Works
|
|
69
64
|
|
|
70
|
-
|
|
65
|
+
**User** ↔️ **Web UI / CLI** ↔️ **Daemon** ↔️ **Sandbox / Environment** ↔️ **Agent**
|
|
71
66
|
|
|
72
|
-
|
|
67
|
+
The daemon securely authenticates with the Agent API using dynamically generated HMAC tokens (`CLAW_API_TOKEN`), allowing sandboxed agents to operate safely without direct access to the host's Unix socket.
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
### Built-in Environments
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
- `cladding` (most secure): A container-based sandbox using [cladding](https://github.com/dstoc/cladding)
|
|
72
|
+
- `macos`: A macOS sandbox environment that restricts write-access to the workspace.
|
|
73
|
+
- `macos-proxy`: A more constrained macOS sandbox that limits network access to an allowlist.
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
### Extensible Pipeline (Routers)
|
|
79
76
|
|
|
80
|
-
|
|
77
|
+
Process user messages before they reach an agent. Dynamically alter content, target specific agents, or expand slash commands (e.g., `/new` to clear context, `/foo` to expand a command script).
|
|
81
78
|
|
|
82
|
-
|
|
83
|
-
- `@clawmini/slash-command`: Expands slash commands (e.g., `/foo`) with the contents of matching files in your `.clawmini/commands/` directory.
|
|
79
|
+
## Next Steps: Build Autonomous Workflows
|
|
84
80
|
|
|
85
|
-
|
|
81
|
+
Once you're comfortable, Clawmini offers powerful tools for advanced users:
|
|
86
82
|
|
|
87
|
-
|
|
83
|
+
- **The `gemini-claw` Template:** Scaffold a complete autonomous assistant with built-in memory management (`MEMORY.md`), identity (`SOUL.md`), and heartbeat checks (`HEARTBEAT.md`) for proactive tasking.
|
|
84
|
+
- **`clawmini-lite`:** Deploy agents into heavily restricted containers. Export the minimal, zero-dependency client (`clawmini export-lite`) to securely authenticate with the daemon, allowing the agent to log actions and request permissions without host access.
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
---
|
|
90
87
|
|
|
91
|
-
|
|
88
|
+
## Documentation & References
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
- `gemini-cladding`: A template configured to run the `gemini` CLI wrapped inside `cladding` for enhanced security.
|
|
95
|
-
- `gemini-claw-cladding`: A comprehensive template that sets up an autonomous personal assistant workspace (OpenClaw). It includes the `gemini-cladding` setup plus a full suite of scaffolding files like `GEMINI.md`, `SOUL.md`, `MEMORY.md`, and `HEARTBEAT.md` to establish the agent's identity, memory, and proactive capabilities.
|
|
90
|
+
For a full list of commands for managing chats, messages, agents, background jobs, and environments, please see the [CLI Command Reference](./docs/CLI_REFERENCE.md).
|
|
96
91
|
|
|
97
92
|
## Development Setup
|
|
98
93
|
|
|
@@ -103,35 +98,25 @@ Clawmini is a monorepo consisting of a Node.js TypeScript CLI/Daemon and an embe
|
|
|
103
98
|
- Node.js (v18+)
|
|
104
99
|
- npm
|
|
105
100
|
|
|
106
|
-
### Setup
|
|
101
|
+
### Setup & Scripts
|
|
107
102
|
|
|
108
103
|
```bash
|
|
109
|
-
# Install dependencies
|
|
104
|
+
# Install dependencies and build the project
|
|
110
105
|
npm install
|
|
111
|
-
|
|
112
|
-
# Build the CLI, Daemon, and statically compile the Web UI
|
|
113
106
|
npm run build
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Development Scripts
|
|
117
107
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
# Watch mode for the CLI
|
|
108
|
+
# Development Watch Modes
|
|
122
109
|
npm run dev:cli
|
|
123
|
-
|
|
124
|
-
# Watch mode for the Daemon
|
|
125
110
|
npm run dev:daemon
|
|
126
111
|
|
|
127
|
-
#
|
|
112
|
+
# Linting & Testing
|
|
128
113
|
npm run format
|
|
129
114
|
npm run lint
|
|
130
115
|
npm run check
|
|
131
116
|
npm run test
|
|
132
117
|
```
|
|
133
118
|
|
|
134
|
-
|
|
119
|
+
**Architecture Notes:**
|
|
135
120
|
|
|
136
121
|
- **Separation of Concerns:** The daemon (`src/daemon`) acts as the stateful orchestrator and queue manager, while the CLI (`src/cli`) is simply a thin TRPC client connecting via a UNIX socket.
|
|
137
|
-
- **Web UI:** The `web/` directory is a SvelteKit application built with `@sveltejs/adapter-static`. Running `npm run build` bundles the web UI into `dist/web`, which is
|
|
122
|
+
- **Web UI:** The `web/` directory is a SvelteKit application built with `@sveltejs/adapter-static`. Running `npm run build` bundles the web UI into `dist/web`, which is served statically by the `clawmini web` Node.js server. Real-time updates use Server-Sent Events (SSE) tailing local `.clawmini/chats/:id/chat.jsonl` files.
|
|
@@ -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-CSgfo_2J.mjs").then((n) => n._);
|
|
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 { a as getAgent, b as writeChatSettings, d as isValidAgentId, f as listAgents,
|
|
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-Dl7WXyaH.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";
|
|
@@ -56,6 +54,7 @@ const initCmd = new Command("init").description("Initialize a new .clawmini sett
|
|
|
56
54
|
"@clawmini/slash-new",
|
|
57
55
|
"@clawmini/slash-stop",
|
|
58
56
|
"@clawmini/slash-interrupt",
|
|
57
|
+
"@clawmini/slash-policies",
|
|
59
58
|
"@clawmini/slash-command"
|
|
60
59
|
],
|
|
61
60
|
api: true
|
|
@@ -97,7 +96,10 @@ async function getDaemonClient(options = {}) {
|
|
|
97
96
|
logFile
|
|
98
97
|
]
|
|
99
98
|
}).unref();
|
|
100
|
-
|
|
99
|
+
for (let i = 0; i < 50; i++) {
|
|
100
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
101
|
+
if (fs.existsSync(socketPath)) break;
|
|
102
|
+
}
|
|
101
103
|
if (!fs.existsSync(socketPath)) throw new Error("Failed to start daemon.");
|
|
102
104
|
}
|
|
103
105
|
return createTRPCClient({ links: [httpLink({
|