@routstr/cocod 0.0.16

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.
@@ -0,0 +1,21 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - master
8
+
9
+ jobs:
10
+ checks:
11
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: oven-sh/setup-bun@v2
16
+ - name: Install dependencies
17
+ run: bun install --frozen-lockfile
18
+ - name: Typecheck
19
+ run: bun run lint
20
+ - name: Tests
21
+ run: bun test
@@ -0,0 +1,78 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ publish:
10
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: oven-sh/setup-bun@v2
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ registry-url: "https://registry.npmjs.org"
20
+ - name: Configure git user
21
+ run: |
22
+ git config user.name "github-actions[bot]"
23
+ git config user.email "github-actions[bot]@users.noreply.github.com"
24
+ - name: Bump version
25
+ run: bun pm version patch --no-git-tag-version
26
+ - name: Sync SKILL version pins
27
+ run: |
28
+ VERSION=$(bun -e 'const pkg = JSON.parse(await Bun.file("package.json").text()); console.log(pkg.version)')
29
+ VERSION="$VERSION" bun -e '
30
+ const version = process.env.VERSION;
31
+ if (!version) {
32
+ throw new Error("Missing VERSION env var");
33
+ }
34
+
35
+ const skillPath = "SKILL.md";
36
+ const skill = await Bun.file(skillPath).text();
37
+
38
+ if (!/^metadata:\s*$/m.test(skill)) {
39
+ throw new Error("SKILL.md is missing metadata field");
40
+ }
41
+
42
+ if (!/^\s{2}skill_version:\s*.+$/m.test(skill)) {
43
+ throw new Error("SKILL.md is missing metadata.skill_version field");
44
+ }
45
+
46
+ if (!/^\s{2}requires_cocod_version:\s*.+$/m.test(skill)) {
47
+ throw new Error("SKILL.md is missing metadata.requires_cocod_version field");
48
+ }
49
+
50
+ const next = skill
51
+ .replace(/^\s{2}skill_version:\s*.+$/m, ` skill_version: ${version}`)
52
+ .replace(/^\s{2}requires_cocod_version:\s*.+$/m, ` requires_cocod_version: ${version}`);
53
+
54
+ await Bun.write(skillPath, next);
55
+ '
56
+ - name: Pack tarball
57
+ run: bun pm pack
58
+ - name: Publish to npm
59
+ env:
60
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
61
+ run: npm publish --access public *.tgz
62
+ - name: Commit version bump
63
+ run: |
64
+ VERSION=$(bun -e 'console.log(require("./package.json").version)')
65
+ git add package.json bun.lock SKILL.md
66
+ git commit -m "chore(release): v$VERSION [skip ci]"
67
+ git push
68
+ - name: Publish to skill registry
69
+ env:
70
+ TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
71
+ run: |
72
+ VERSION=$(bun -e 'console.log(require("./package.json").version)')
73
+ SLUG=$(bun -e 'console.log(require("./package.json").name)')
74
+ curl -X POST "https://www.clawhub.ai/api/v1/skills" \
75
+ -H "Authorization: Bearer $TOKEN" \
76
+ -H "Accept: application/json" \
77
+ -F "payload={\"slug\":\"$SLUG\",\"displayName\":\"Cocod\",\"version\":\"$VERSION\",\"changelog\":\"Release v$VERSION\",\"tags\":[\"latest\"]}" \
78
+ -F "files=@SKILL.md;filename=SKILL.md"
package/.prettierrc ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "printWidth": 100,
3
+ "tabWidth": 2,
4
+ "useTabs": false,
5
+ "semi": true,
6
+ "singleQuote": false,
7
+ "trailingComma": "all",
8
+ "bracketSpacing": true,
9
+ "arrowParens": "always"
10
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,244 @@
1
+ # AGENTS.md
2
+
3
+ This repository is a small Bun + TypeScript CLI/daemon.
4
+ Agents should optimize for: minimal diffs, strict types, Bun-native APIs, and predictable CLI UX.
5
+
6
+ ## Ground Rules (Repo Policy)
7
+
8
+ - Runtime: Bun (not Node). Prefer Bun APIs over Node/polyfills.
9
+ - Module system: ESM (`"type": "module"` in `package.json`).
10
+ - TypeScript is the linter: `tsc --noEmit` is the primary check.
11
+ - No Cursor/Copilot rule files were found (`.cursor/rules/**`, `.cursorrules`, `.github/copilot-instructions.md`).
12
+ - Also follow `CLAUDE.md` (Bun defaults, preferred APIs, testing conventions).
13
+
14
+ ## Project Shape
15
+
16
+ - `src/index.ts`: entrypoint for the `cocod` binary (shebang: `#!/usr/bin/env bun`).
17
+ - `src/cli.ts` + `src/cli-shared.ts`: Commander-based CLI.
18
+ - `src/daemon.ts`: background daemon implemented with `Bun.serve()` on a UNIX socket.
19
+ - `src/routes.ts`: HTTP route handlers for the daemon (endpoints like /balance, /receive, /init, etc.).
20
+ - `src/utils/`:
21
+ - `state.ts`: DaemonStateManager and wallet state logic
22
+ - `wallet.ts`: Wallet initialization helpers
23
+ - `crypto.ts`: Mnemonic encryption/decryption
24
+ - `config.ts`: Configuration paths, env vars, and types
25
+ - IPC: CLI talks to daemon via `fetch()` with Bun's `RequestInit.unix` option.
26
+
27
+ Key paths/env:
28
+
29
+ - Socket: `COCOD_SOCKET` (default `~/.cocod/cocod.sock`).
30
+ - PID file: `COCOD_PID` (default `~/.cocod/cocod.pid`).
31
+ - Wallet config: `~/.cocod/config.json` (generated; do not commit).
32
+
33
+ ## Commands
34
+
35
+ All commands run from repo root (`/home/egge/projects/cocod`).
36
+
37
+ ### Install
38
+
39
+ ```sh
40
+ bun install
41
+ ```
42
+
43
+ ### Run the CLI (foreground)
44
+
45
+ - Entrypoint:
46
+
47
+ ```sh
48
+ bun src/index.ts --help
49
+ ```
50
+
51
+ - Via npm-style script (use `--` to pass args):
52
+
53
+ ```sh
54
+ bun run start -- --help
55
+ ```
56
+
57
+ - Common commands:
58
+
59
+ ```sh
60
+ bun src/index.ts balance
61
+ bun src/index.ts ping
62
+ bun src/index.ts mint list
63
+ ```
64
+
65
+ ### Start the daemon
66
+
67
+ The daemon can be started explicitly, but the CLI also auto-starts it when needed.
68
+
69
+ ```sh
70
+ bun run daemon
71
+ ```
72
+
73
+ ### Build / bundle
74
+
75
+ There is no required build step (the CLI runs directly via Bun + TypeScript).
76
+
77
+ - Optional: produce a bundled artifact:
78
+
79
+ ```sh
80
+ bun build src/index.ts --outdir dist --target bun
81
+ ```
82
+
83
+ ### Lint / typecheck
84
+
85
+ This repo currently uses TypeScript as the main lint gate.
86
+
87
+ ```sh
88
+ bun run lint
89
+ ```
90
+
91
+ If you need to run tsc directly:
92
+
93
+ ```sh
94
+ bunx tsc --noEmit
95
+ ```
96
+
97
+ ### Tests
98
+
99
+ Bun's test runner is the expected choice.
100
+
101
+ - Run all tests:
102
+
103
+ ```sh
104
+ bun test
105
+ ```
106
+
107
+ - Run a single test file:
108
+
109
+ ```sh
110
+ bun test path/to/file.test.ts
111
+ ```
112
+
113
+ - Run a single test by name (recommended when adding tests):
114
+
115
+ ```sh
116
+ bun test -t "ping returns pong"
117
+ ```
118
+
119
+ Notes:
120
+
121
+ - Prefer test files named `*.test.ts` and colocated near the code they test.
122
+ - Use `import { test, expect } from "bun:test";`.
123
+
124
+ ## Code Style
125
+
126
+ There is a formatter config at `.prettierrc`. Match existing style and avoid drive-by reformatting.
127
+
128
+ ### Formatting
129
+
130
+ - Indentation: 2 spaces.
131
+ - Quotes: double quotes for strings.
132
+ - Semicolons: use them consistently (match surrounding file).
133
+ - Line length: keep lines reasonably short; wrap long function signatures.
134
+
135
+ ### Imports
136
+
137
+ - Order:
138
+ 1. external packages
139
+ 2. blank line
140
+ 3. local relative imports
141
+ - Prefer `import type { ... }` for type-only imports.
142
+ - Prefer named imports; avoid default imports unless the package is default-first.
143
+ - Keep relative imports extensionless (match current code). Only include `.js` when required by ESM packages.
144
+
145
+ ### Types and strictness
146
+
147
+ `tsconfig.json` enables strict TypeScript plus `noUncheckedIndexedAccess`.
148
+
149
+ - Avoid `any`. Use `unknown` at boundaries and narrow.
150
+ - Validate untrusted inputs (CLI args, request bodies, env vars).
151
+ - When indexing objects, handle `undefined` explicitly (e.g., `balance[mintUrl] || 0`).
152
+ - Prefer explicit return types on exported functions and non-trivial helpers.
153
+ - Use discriminated unions / literal types for protocol-like payloads.
154
+
155
+ ### Naming
156
+
157
+ - Files: kebab-case for multiword modules (e.g., `cli-shared.ts`).
158
+ - Types/interfaces: `PascalCase`.
159
+ - Functions/variables: `camelCase`.
160
+ - Constants: `UPPER_SNAKE_CASE` for configuration-like values.
161
+ - CLI commands: lowercase; nouns/verbs consistent with existing Commander usage.
162
+
163
+ ### Error handling
164
+
165
+ General:
166
+
167
+ - Only `process.exit()` from true CLI entrypoints.
168
+ - Prefer throwing `Error` (or subclasses) from library-ish functions.
169
+ - In `catch`, treat the error as `unknown`; derive a safe message:
170
+ - `error instanceof Error ? error.message : String(error)`.
171
+
172
+ Daemon (`src/daemon.ts`):
173
+
174
+ - Return JSON with either `{ output: string }` or `{ error: string }`.
175
+ - Use proper HTTP status codes for failures (e.g., 400 for bad input, 404 unknown endpoint, 500 unexpected).
176
+ - Do not swallow errors silently; if you intentionally suppress errors (e.g., delete stale files), add a short comment.
177
+
178
+ CLI (`src/cli-shared.ts`):
179
+
180
+ - Print user-facing errors to stderr (`console.error`).
181
+ - Exit with code 1 for expected failures.
182
+ - For daemon connectivity issues, prefer actionable messages (socket path, how to start daemon).
183
+
184
+ ### Bun-specific guidance (from `CLAUDE.md`)
185
+
186
+ - Use `bun <file>` / `bun run <script>` (not `node`, `ts-node`, `npm`).
187
+ - Use `bun test` (not jest/vitest).
188
+ - Use `Bun.serve()` routes/websocket support (not express).
189
+ - Prefer `Bun.file` for file IO; Bun loads `.env` automatically (avoid `dotenv`).
190
+ - Prefer Bun-native DB/network libs where applicable:
191
+ - `bun:sqlite` for SQLite (avoid `better-sqlite3`)
192
+ - `Bun.sql` for Postgres (avoid `pg`)
193
+ - `Bun.redis` for Redis (avoid `ioredis`)
194
+ - built-in `WebSocket` (avoid `ws`)
195
+
196
+ ## Editing Expectations for Agents
197
+
198
+ - Keep diffs surgical; do not reformat unrelated code.
199
+ - Preserve CLI UX and backward compatibility of command names/flags unless explicitly requested.
200
+ - Avoid committing/generated artifacts (e.g., `coco.db`, sockets, pid files, `.env`).
201
+ - When adding new commands/routes:
202
+ - update Commander wiring in `src/cli.ts`
203
+ - add/update the route handler in `src/routes.ts` (and ensure it returns the `{ output | error }` shape)
204
+ - keep the CLI/daemon contract explicit (method, path, request/response types).
205
+ - update `docs/daemon-api.json` to keep the route contract in sync.
206
+
207
+ ## Agent Playbook
208
+
209
+ Use these repeatable recipes to keep changes predictable.
210
+
211
+ ### Add or update a CLI command
212
+
213
+ 1. Update command wiring in `src/cli.ts`.
214
+ 2. Add/update daemon route in `src/routes.ts`.
215
+ 3. Keep route responses to `{ output: ... }` on success and `{ error: string }` on failure.
216
+ 4. Return explicit status codes:
217
+ - 400 invalid input
218
+ - 401 auth/passphrase failure
219
+ - 403 locked wallet for unlocked-only endpoints
220
+ - 404 unknown endpoint
221
+ - 409 state conflict
222
+ - 500 unexpected runtime failure
223
+ 5. Update docs:
224
+ - `README.md` command tables/examples
225
+ - `docs/daemon-api.json` endpoint contract
226
+ 6. Add/adjust tests and run checks (`bun run lint`, `bun test`).
227
+
228
+ ### Validate request bodies
229
+
230
+ - Treat request bodies as untrusted.
231
+ - Validate required fields before calling wallet methods.
232
+ - Return 400 with actionable messages for malformed JSON, missing fields, wrong types, or invalid ranges.
233
+
234
+ ### Keep docs and runtime aligned
235
+
236
+ - If you change command names, env var behavior, or defaults, update docs in the same patch.
237
+ - Prefer one source of truth for path constants (`src/utils/config.ts`).
238
+ - Do not leave placeholders in user-facing docs.
239
+
240
+ ## Quick Debugging Checklist
241
+
242
+ - CLI can't connect: verify `COCOD_SOCKET` matches daemon and the socket exists.
243
+ - Daemon won't start: check for stale `~/.cocod/cocod.sock` and `~/.cocod/cocod.pid`.
244
+ - Type errors: run `bun run lint` and fix strictness issues (especially `undefined` from indexing).
package/CLAUDE.md ADDED
@@ -0,0 +1,105 @@
1
+ Default to using Bun instead of Node.js.
2
+
3
+ - Use `bun <file>` instead of `node <file>` or `ts-node <file>`
4
+ - Use `bun test` instead of `jest` or `vitest`
5
+ - Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
6
+ - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
7
+ - Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
8
+ - Use `bunx <package> <command>` instead of `npx <package> <command>`
9
+ - Bun automatically loads .env, so don't use dotenv.
10
+
11
+ ## APIs
12
+
13
+ - `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
14
+ - `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
15
+ - `Bun.redis` for Redis. Don't use `ioredis`.
16
+ - `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
17
+ - `WebSocket` is built-in. Don't use `ws`.
18
+ - Prefer `Bun.file` over `node:fs`'s readFile/writeFile
19
+ - Bun.$`ls` instead of execa.
20
+
21
+ ## Testing
22
+
23
+ Use `bun test` to run tests.
24
+
25
+ ```ts#index.test.ts
26
+ import { test, expect } from "bun:test";
27
+
28
+ test("hello world", () => {
29
+ expect(1).toBe(1);
30
+ });
31
+ ```
32
+
33
+ ## Frontend
34
+
35
+ Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
36
+
37
+ Server:
38
+
39
+ ```ts#index.ts
40
+ import index from "./index.html"
41
+
42
+ Bun.serve({
43
+ routes: {
44
+ "/": index,
45
+ "/api/users/:id": {
46
+ GET: (req) => {
47
+ return new Response(JSON.stringify({ id: req.params.id }));
48
+ },
49
+ },
50
+ },
51
+ // optional websocket support
52
+ websocket: {
53
+ open: (ws) => {
54
+ ws.send("Hello, world!");
55
+ },
56
+ message: (ws, message) => {
57
+ ws.send(message);
58
+ },
59
+ close: (ws) => {
60
+ // handle close
61
+ }
62
+ },
63
+ development: {
64
+ hmr: true,
65
+ console: true,
66
+ }
67
+ })
68
+ ```
69
+
70
+ HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
71
+
72
+ ```html#index.html
73
+ <html>
74
+ <body>
75
+ <h1>Hello, world!</h1>
76
+ <script type="module" src="./frontend.tsx"></script>
77
+ </body>
78
+ </html>
79
+ ```
80
+
81
+ With the following `frontend.tsx`:
82
+
83
+ ```tsx#frontend.tsx
84
+ import React from "react";
85
+ import { createRoot } from "react-dom/client";
86
+
87
+ // import .css files directly and it works
88
+ import './index.css';
89
+
90
+ const root = createRoot(document.body);
91
+
92
+ export default function Frontend() {
93
+ return <h1>Hello, world!</h1>;
94
+ }
95
+
96
+ root.render(<Frontend />);
97
+ ```
98
+
99
+ Then, run index.ts
100
+
101
+ ```sh
102
+ bun --hot ./index.ts
103
+ ```
104
+
105
+ For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Egge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # cocod
2
+
3
+ `cocod` is a Cashu wallet CLI with a local daemon.
4
+
5
+ If you like simple tools: run commands in your terminal, and let the daemon handle wallet state in the background.
6
+
7
+ ## What it does
8
+
9
+ - Initialize and secure a Cashu wallet
10
+ - Check balances and transaction history
11
+ - Send and receive Cashu tokens
12
+ - Send and receive Lightning payments (BOLT11)
13
+ - Handle HTTP 402 payments with `X-Cashu`
14
+ - Manage trusted mints
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ bun install --global cocod
20
+ ```
21
+
22
+ Or from source:
23
+
24
+ ```bash
25
+ git clone <repository-url>
26
+ cd cocod
27
+ bun install
28
+ ```
29
+
30
+ ## Quick start
31
+
32
+ ```bash
33
+ # Check daemon status
34
+ cocod status
35
+
36
+ # Create a wallet (auto-generates mnemonic)
37
+ cocod init
38
+
39
+ # If encrypted during init, unlock it
40
+ cocod unlock "your-passphrase"
41
+
42
+ # Check balance
43
+ cocod balance
44
+ ```
45
+
46
+ ## Most common commands
47
+
48
+ ```bash
49
+ # Receive
50
+ cocod receive cashu "cashuA..."
51
+ cocod receive bolt11 1000
52
+
53
+ # Send
54
+ cocod send cashu 500
55
+ cocod send bolt11 "lnbc..."
56
+
57
+ # Mints
58
+ cocod mints add https://mint.example.com/Bitcoin
59
+ cocod mints list
60
+
61
+ # History
62
+ cocod history --limit 10
63
+ cocod history --watch
64
+
65
+ # Logs
66
+ cocod logs
67
+ cocod logs --follow
68
+ cocod logs --path
69
+ ```
70
+
71
+ ## NPC (Lightning Address)
72
+
73
+ ```bash
74
+ # Your NPC address
75
+ cocod npc address
76
+
77
+ # Check username price, then confirm purchase
78
+ cocod npc username myname
79
+ cocod npc username myname --confirm
80
+ ```
81
+
82
+ ## HTTP 402 / X-Cashu
83
+
84
+ ```bash
85
+ # Inspect request from a 402 response
86
+ cocod x-cashu parse "<encoded-x-cashu-request>"
87
+
88
+ # Settle and get header value for retry
89
+ cocod x-cashu handle "<encoded-x-cashu-request>"
90
+ ```
91
+
92
+ ## How it works
93
+
94
+ - CLI: `src/cli.ts`
95
+ - Daemon: `src/daemon.ts`
96
+ - Routes: `src/routes.ts`
97
+ - IPC transport: HTTP over UNIX socket
98
+
99
+ Defaults:
100
+
101
+ - Socket: `~/.cocod/cocod.sock` (or `COCOD_SOCKET`)
102
+ - PID file: `~/.cocod/cocod.pid` (or `COCOD_PID`)
103
+ - Daemon log: `~/.cocod/daemon.log` (or `COCOD_LOG_FILE`)
104
+ - Config: `~/.cocod/config.json`
105
+ - Database: `~/.cocod/coco.db`
106
+
107
+ Logging defaults:
108
+
109
+ - Structured JSON logs are written to `~/.cocod/daemon.log`
110
+ - Rotation keeps 5 files at 5 MiB each by default
111
+ - Override with `COCOD_LOG_LEVEL`, `COCOD_LOG_MAX_BYTES`, and `COCOD_LOG_MAX_FILES`
112
+
113
+ ## Development
114
+
115
+ ```bash
116
+ # Run CLI from source
117
+ bun src/index.ts --help
118
+
119
+ # Run daemon directly
120
+ bun run daemon
121
+
122
+ # Typecheck
123
+ bun run lint
124
+
125
+ # Tests
126
+ bun test
127
+
128
+ # Isolated daemon smoke test
129
+ bun run smoke:daemon
130
+ ```
131
+
132
+ ## Docs
133
+
134
+ - [API and command reference](docs/API.md)
135
+ - [Machine-readable daemon contract](docs/daemon-api.json)
136
+
137
+ ## License
138
+
139
+ MIT