dilaya-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +145 -0
  2. package/dist/index.js +3543 -0
  3. package/package.json +37 -0
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # dilaya
2
+
3
+ A **dumb (AI-free) poller** that wakes a Claude agent only when there's work.
4
+
5
+ Each Dilaya app (a Postgres schema) can have **one agent**: a prompt that runs on demand
6
+ instead of an AI session sitting idle 24/7. This CLI is the local half of that loop:
7
+
8
+ 1. It asks the app's MCP server one cheap question on a fixed interval — *"is there work?"*
9
+ (a DynamoDB-backed read; Aurora stays scaled to zero, no model tokens spent).
10
+ 2. Only when the answer is **yes** does it launch an interactive `claude` inside a detached
11
+ **tmux** session, running from the agent's project dir.
12
+ 3. The agent does all available work, optionally has a back-and-forth via the `await-work`
13
+ tool, decides how it should next be woken (`set-agent-mode`), then marks itself **idle**.
14
+ The poller reaps the idle session.
15
+
16
+ Because the agent runs in tmux (not headless `claude -p`), you can **attach to the live
17
+ session** to watch it work or message it: `dilaya agent attach`.
18
+
19
+ You pay for Claude only while there's actual work. The poller itself is free.
20
+
21
+ ## Why a separate CLI
22
+
23
+ The long-lived poll token must **never enter the LLM's context**. So the agent (in Claude
24
+ Code) only ever handles a *single-use, 15-minute setup token*; the CLI exchanges that for a
25
+ long-lived, opaque poll token that it stores locally (`chmod 600`) and that only ever
26
+ reveals "work or not". The user never has to touch the CLI by hand — Claude Code drives the
27
+ whole install via `get-agent-setup-instructions`.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm i -g dilaya-cli # or from source: npm i -g <path-to>/dilaya-cli
33
+ dilaya --version # package is `dilaya-cli`; the command it installs is `dilaya`
34
+ ```
35
+
36
+ Requires Node ≥ 18, plus **`claude` and `tmux`** on `PATH` (the agent runs inside tmux).
37
+
38
+ ## Usage
39
+
40
+ Normally you don't run these yourself — ask the app's agent to "install the agent loop on
41
+ this machine" and Claude Code runs them for you. For reference:
42
+
43
+ ```bash
44
+ # 1. Exchange a setup token (from the MCP tool get-agent-setup-token) for a poll token.
45
+ dilaya agent setup --schema myapp \
46
+ --url https://<host>/myapp/agent/token \
47
+ --setup-token <single-use-token> \
48
+ --project-dir ~/.dilaya-agents/myapp \
49
+ --interval 30s
50
+
51
+ # 2. Install + start it as a background service (launchd on macOS, systemd-user on Linux).
52
+ dilaya agent install --schema myapp --interval 30s
53
+
54
+ # Inspect / control
55
+ dilaya agent status --schema myapp # service + poller + tmux session state
56
+ dilaya agent attach --schema myapp # attach to the live agent (type to talk; Ctrl-b then d to detach)
57
+ dilaya agent logs --schema myapp # tail the agent log
58
+ dilaya agent stop --schema myapp # stop poller (and kill any running session)
59
+ dilaya agent uninstall --schema myapp # stop + remove the service
60
+
61
+ # What the service actually runs (foreground poll loop):
62
+ dilaya agent run --schema myapp
63
+ ```
64
+
65
+ ## How a wake works
66
+
67
+ `poll` returns `{ shouldWake, mode, lifecycle }`. When there's work and no session is live,
68
+ the poller starts one interactive `claude` (no `-p`) inside tmux, from the project dir:
69
+
70
+ - **`mode: "new"`** — start a fresh Claude session
71
+ (`claude --permission-mode auto --session-id <uuid> /dilaya-agent`).
72
+ Cheaper; the agent starts clean. Used when the agent last finished its task.
73
+ - **`mode: "continue"`** — resume the previous session (`--resume <id>`), keeping context.
74
+ Used when the agent was mid-task or expecting a reply.
75
+
76
+ The agent itself picks the next mode by calling `set-agent-mode` before it goes idle.
77
+
78
+ Interactive `claude` never self-exits, so the poller decides when a run is over from
79
+ `lifecycle`:
80
+
81
+ - **`running`** — the agent called `begin-agent-run`; the poller leaves it alone. It **never
82
+ time-kills** a running agent (coding work can take hours).
83
+ - **`idle`** — the agent called `set-agent-mode`; the poller reaps the tmux session after a
84
+ short grace (~12s) — **unless you're attached** (attached sessions are never reaped).
85
+
86
+ Only one session per schema exists at a time: the loop checks for a live session before
87
+ starting another, so a second agent can never run concurrently. A session that starts but
88
+ never reports `running` (e.g. `claude` failed to launch) is reaped after a startup timeout
89
+ (~5 min) and retried.
90
+
91
+ ## Files it writes
92
+
93
+ Per schema, under `~/.config/dilaya/<schema>/`:
94
+
95
+ | File | Purpose |
96
+ | --- | --- |
97
+ | `config.json` | schema, poll URL, **poll token** (chmod 600), project dir, interval, last session id |
98
+ | `poller.pid` | liveness of the poll loop |
99
+ | `agent.log` | poll + agent activity (what `logs` tails) |
100
+
101
+ The running agent itself lives in a **detached tmux session** (`dilaya-<schema>` on a
102
+ dedicated tmux socket `dilaya`), not a file — `dilaya agent attach` connects to it.
103
+
104
+ Service definition: `~/Library/LaunchAgents/com.dilaya.agent.<schema>.plist` (macOS) or
105
+ `~/.config/systemd/user/dilaya-agent-<schema>.service` (Linux).
106
+
107
+ ## Caveats
108
+
109
+ - **Login-scoped.** Starts at login (not pre-login boot); laptop sleep pauses polling.
110
+ On Linux run `loginctl enable-linger` to keep the service alive after logout.
111
+ - **Requires tmux** — the agent runs inside it (`install` preflights it). The poller talks to
112
+ tmux on a dedicated socket (`-L dilaya`), so it never touches your default tmux server.
113
+ - The spawned `claude` uses your existing MCP auth; if that token expires, ticks fail until
114
+ you re-login.
115
+ - The poller runs `claude --permission-mode auto` (interactive, inside tmux), so the project
116
+ dir's `.claude/settings.local.json` must allowlist every tool the agent uses — auto mode
117
+ runs allowlisted tools without prompting, and deny rules still win. Override the mode with
118
+ `dilaya agent setup --permission-mode <mode>`.
119
+ - One agent per schema; run `setup`/`install` once per app you want a loop for.
120
+
121
+ ## Build (from source)
122
+
123
+ ```bash
124
+ npm install
125
+ npm run typecheck
126
+ npm run build # esbuild → dist/index.js (with shebang)
127
+ ```
128
+
129
+ ## Releasing
130
+
131
+ Publishing to npm is automated. To cut a release:
132
+
133
+ 1. Bump `version` in `package.json`, then commit and push to `main`.
134
+ 2. Create a **GitHub Release** whose tag matches that version (`v0.1.0` or `0.1.0`).
135
+
136
+ The `Publish to npm` workflow ([.github/workflows/publish.yml](.github/workflows/publish.yml))
137
+ then verifies the tag matches `package.json`, type-checks, and runs `npm publish`. It needs
138
+ an `NPM_TOKEN` repository secret — an npm **automation** token (it bypasses 2FA in CI):
139
+
140
+ ```bash
141
+ gh secret set NPM_TOKEN --repo hereya/dilaya-cli # paste the npm automation token
142
+ ```
143
+
144
+ The GitHub repo is private; the npm package (`dilaya-cli`, unscoped) is published **public**
145
+ so `npm i -g dilaya-cli` works for everyone. It installs the `dilaya` command.