jagc 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.
package/.env.example ADDED
@@ -0,0 +1,7 @@
1
+ JAGC_DATABASE_PATH=/Users/akuzmenko/.jagc/jagc.sqlite
2
+ JAGC_WORKSPACE_DIR=/Users/akuzmenko/.jagc
3
+ JAGC_HOST=127.0.0.1
4
+ JAGC_PORT=31415
5
+ JAGC_API_URL=http://127.0.0.1:31415
6
+ JAGC_RUNNER=pi
7
+ JAGC_TELEGRAM_BOT_TOKEN=
package/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ All notable changes to jagc are documented here.
4
+
5
+ This project follows a readable, sectioned changelog format with a permanent **Unreleased** section.
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - _None yet._
12
+
13
+ ### Changed
14
+
15
+ - _None yet._
16
+
17
+ ### Fixed
18
+
19
+ - _None yet._
20
+
21
+ ## [0.1.0] - 2026-02-09
22
+
23
+ ### Added
24
+
25
+ - npm-distributable CLI package (`jagc`) with built artifacts and curated package contents.
26
+ - First supported macOS deployment path via top-level CLI service commands:
27
+ - `jagc install`
28
+ - `jagc status`
29
+ - `jagc restart`
30
+ - `jagc uninstall`
31
+ - `jagc doctor`
32
+ - Launchd-managed per-user service installation with workspace-scoped logs and safety defaults.
33
+ - Package smoke loop (`pnpm test:pack`) validating tarball install + runnable server + CLI roundtrip.
34
+ - CI release gate workflow (`pnpm release:gate`) on GitHub Actions.
35
+
36
+ ### Changed
37
+
38
+ - Deployment docs now treat macOS npm+launchd as the primary supported install path.
39
+ - Local release gate consolidated into a single canonical command.
40
+ - Project license changed from UNLICENSED to MIT (`LICENSE` added).
41
+
42
+ ### Fixed
43
+
44
+ - Service status/restart/doctor health targeting now resolves host/port from installed service config.
45
+ - Service command failures now return clean one-line CLI errors (no stack dumps).
46
+ - Service manager no longer selects TypeScript server entrypoints for launchd runtime.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anton Kuzmenko <hotk@hey.com>
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,220 @@
1
+ # jagc
2
+
3
+ > **jagc** = **j**ust **a** **g**ood **c**lanker.
4
+
5
+ Self-hosted AI assistant to automate your life:
6
+ - **pi-coding-agent** for agent runtime behavior (sessions, context files, skills/prompts/extensions/themes/packages)
7
+ - **SQLite-backed run state + in-process scheduler** for durable run tracking and execution
8
+
9
+ This README is intentionally short and v0-focused.
10
+
11
+ For current implemented architecture, see **[`docs/architecture.md`](docs/architecture.md)**.
12
+ For deferred APIs, deployment notes, and post-v0 plans, see **[`docs/future.md`](docs/future.md)**.
13
+ For testing loops (including Telegram behavioral polling clone), see **[`docs/testing.md`](docs/testing.md)**.
14
+ For release/publish procedure, see **[`docs/release.md`](docs/release.md)**.
15
+ Release history lives in **[`CHANGELOG.md`](CHANGELOG.md)**.
16
+
17
+ ## Status
18
+
19
+ - **Pre-alpha.** Expect breaking changes.
20
+ - **v0 scope is implemented** (server + CLI + threading semantics + Telegram polling controls). CI merge gating runs in GitHub Actions (`pnpm release:gate`).
21
+ - npm publish is tag-driven via GitHub Actions (`vX.Y.Z` tags only, `latest` channel).
22
+ - Core server endpoints are in place: `/healthz`, `/v1/messages`, `/v1/runs/:run_id`, auth catalog/login endpoints (`/v1/auth/providers`, `/v1/auth/providers/:provider/login`, `/v1/auth/logins/:attempt_id{,/input,/cancel}`), `/v1/models`, and thread runtime controls (`/v1/threads/:thread_key/{runtime,model,thinking,session}`).
23
+ - CLI supports the happy path plus runtime controls: `message`, `run wait`, `health`, `auth providers`, `auth login`, `new`, `model list/get/set`, `thinking get/set`, and local service lifecycle commands (`install`, `status`, `restart`, `uninstall`, `doctor`).
24
+ - Default executor runs through pi SDK sessions with SQLite-backed durable run tracking and in-process scheduling/recovery.
25
+ - Same-thread queued follow-ups/steers are accepted and run completion is attributed via pi session events (not prompt promise timing).
26
+ - Same-thread turn ordering (`followUp` / `steer`) is enforced by per-thread pi session controllers; run dispatch/recovery is in-process and single-server-process scoped in v0.
27
+ - Telegram polling adapter is implemented (personal chats), including button-based runtime controls via `/settings`, `/new`, `/model`, `/thinking`, and `/auth`.
28
+ - Server/runtime logs are unified on structured Pino JSON with component-scoped child loggers and request completion events.
29
+ - Telegram chat UX now streams live run progress in-chat (append-style progress edits + typing indicator + compact `>` tool / `~` thinking snippets) while preserving final run output delivery.
30
+ - If Telegram foreground wait times out, the adapter keeps watching in the background and posts the final result when the run completes.
31
+ - Model/thinking changes from Telegram button pickers return to the `/settings` panel with updated runtime state.
32
+ - Outdated Telegram inline callbacks auto-recover by replacing the menu with the latest `/settings` panel.
33
+ - Telegram callback payload limits are enforced; over-limit model/auth options are hidden with an in-chat notice.
34
+ - `JAGC_RUNNER=echo` is available for deterministic smoke tests.
35
+ - macOS single-user deployment is supported via npm global install + launchd (`jagc install` / `status` / `restart` / `uninstall`). Manual templates remain under `deploy/` as fallback examples.
36
+
37
+ ## Locked v0 technology stack
38
+
39
+ Source of truth: **[`AGENTS.md`](AGENTS.md)**.
40
+
41
+ - Runtime: TypeScript (ESM) on Node.js 20 + pnpm
42
+ - Server/API: Fastify + Zod + Pino
43
+ - CLI: Commander
44
+ - Agent/runtime: pi-coding-agent
45
+ - Durable run state: SQLite (`runs`, `message_ingest`, `thread_sessions`) + in-process scheduler
46
+ - Telegram: grammY (polling first)
47
+ - Quality/tooling: Biome + Vitest
48
+ - Build: tsdown
49
+
50
+ ## v0 scope (shipped)
51
+
52
+ Shipped in v0:
53
+
54
+ - Server:
55
+ - `GET /healthz`
56
+ - `POST /v1/messages` (ingest message, start durable run, return `run_id`)
57
+ - `GET /v1/runs/:run_id` (status/output)
58
+ - CLI:
59
+ - `jagc message "..." --json`
60
+ - `jagc run wait <run_id> --json`
61
+ - `jagc install|status|restart|uninstall|doctor` (macOS service lifecycle + diagnostics)
62
+ - Telegram:
63
+ - **Polling mode only** for v0
64
+ - Personal chats only
65
+ - One active run per Telegram thread (`thread_key = telegram:chat:<chat_id>`)
66
+ - Queued input behavior aligned with pi semantics (`steer` / `followUp`)
67
+ - `/new` resets the current Telegram thread's pi session; the next message creates a fresh session
68
+
69
+ ### v0 acceptance behavior
70
+
71
+ - `jagc message "ping" --json` returns JSON including:
72
+ - `run_id`
73
+ - `status` (`succeeded|failed|running`)
74
+ - `output` (when succeeded)
75
+ - `error.message` (when failed)
76
+ - If two messages arrive for the same thread while a run is active:
77
+ - `steer` interrupts at the next tool boundary
78
+ - `followUp` waits for idle
79
+ - Different threads run concurrently.
80
+
81
+ ## Contracts to stabilize early
82
+
83
+ We intentionally lock only these early:
84
+
85
+ 1. **Workspace contract** (`JAGC_WORKSPACE_DIR`) and override rules
86
+ 2. **Ingress envelope + idempotency semantics** (internal)
87
+ 3. **Thread/run concurrency + queued message behavior** (`steer` / `followUp`)
88
+
89
+ Everything else remains flexible during pre-alpha.
90
+
91
+ ## Minimal workspace contract
92
+
93
+ `JAGC_WORKSPACE_DIR` points to a trusted local repo.
94
+
95
+ Canonical layout (v0):
96
+
97
+ ```text
98
+ $JAGC_WORKSPACE_DIR/
99
+ AGENTS.md # auto-created on first startup (global user profile + assistant instructions)
100
+ SYSTEM.md # auto-created on first startup (global assistant behavior baseline)
101
+ APPEND_SYSTEM.md # optional
102
+ skills/ # auto-seeded with bundled skills on first startup (no overwrite)
103
+ prompts/ # optional
104
+ extensions/ # auto-seeded with bundled extensions on first startup (no overwrite)
105
+ themes/ # optional
106
+ tools/ # optional
107
+ jagc.json # optional
108
+ settings.json # auto-created on first startup (pi workspace settings + default packages)
109
+ .gitignore # auto-managed: .sessions/, auth.json, git/, jagc.sqlite*
110
+ ```
111
+
112
+ ## Configuration (v0 minimum)
113
+
114
+ | Variable | Required | Notes |
115
+ | --- | --- | --- |
116
+ | `JAGC_DATABASE_PATH` | No | SQLite DB file path (default `$JAGC_WORKSPACE_DIR/jagc.sqlite`; relative paths resolve under `JAGC_WORKSPACE_DIR`) |
117
+ | `JAGC_WORKSPACE_DIR` | No | Workspace + pi agent directory (default `~/.jagc`) |
118
+ | `JAGC_HOST` | No | Server bind host (default `127.0.0.1`) |
119
+ | `JAGC_PORT` | No | Server bind port (default `31415`) |
120
+ | `JAGC_API_URL` | No | CLI API target (default `http://127.0.0.1:31415`) |
121
+ | `JAGC_RUNNER` | No | `pi` (default) or `echo` for deterministic local/smoke runs |
122
+ | `JAGC_LOG_LEVEL` | No | `info` by default (`fatal|error|warn|info|debug|trace|silent`) |
123
+ | `JAGC_TELEGRAM_BOT_TOKEN` | No | Required only when Telegram adapter is enabled |
124
+ | `JAGC_WEBHOOK_BEARER_TOKEN` | No | Required when generic `POST /v1/webhooks/:source` ingress is enabled |
125
+
126
+ Auth setup and provider credential details: [`docs/auth.md`](docs/auth.md).
127
+
128
+ By default jagc uses `JAGC_WORKSPACE_DIR=~/.jagc` for workspace files and sets `JAGC_DATABASE_PATH` to `$JAGC_WORKSPACE_DIR/jagc.sqlite`. On startup it ensures the directory exists, creates `SYSTEM.md`, `AGENTS.md`, and `settings.json` from built-in templates if missing (without overwriting existing files), seeds bundled `defaults/skills/**` and `defaults/extensions/**` files into the workspace if missing (without overwriting existing files), and keeps `.gitignore` entries for `.sessions/`, `auth.json`, `git/`, `jagc.sqlite`, `jagc.sqlite-shm`, and `jagc.sqlite-wal`. The default `settings.json` pre-installs `git:github.com/default-anton/pi-librarian` and `git:github.com/default-anton/pi-subdir-context`; users can edit/remove them later. jagc still does not copy `~/.pi/agent/{settings.json,auth.json}` automatically.
129
+
130
+ ## Quick start (macOS, supported path)
131
+
132
+ 1. Install Node.js 20+ and npm.
133
+ 2. Install jagc globally:
134
+ - `npm install -g jagc@latest`
135
+ 3. Install + start the user service:
136
+ - `jagc install`
137
+ 4. Verify:
138
+ - `jagc status`
139
+ - `jagc health --json`
140
+
141
+ Update to latest:
142
+
143
+ - `npm install -g jagc@latest`
144
+ - `jagc restart`
145
+
146
+ Uninstall service (keep workspace data):
147
+
148
+ - `jagc uninstall`
149
+
150
+ Uninstall service and delete local workspace data (`~/.jagc` by default):
151
+
152
+ - `jagc uninstall --purge-data`
153
+
154
+ Remove the npm package binary as well:
155
+
156
+ - `npm uninstall -g jagc`
157
+
158
+ If the service fails to start, run diagnostics:
159
+
160
+ - `jagc doctor`
161
+
162
+ ## Quick start (dev)
163
+
164
+ 1. `mise install` (rerun when required tools are missing or `.tool-versions` changes)
165
+ 2. `pnpm install`
166
+ 3. Set required env vars (see `.env.example`)
167
+ 4. `pnpm dev` (applies SQL migrations from `migrations/` on startup)
168
+ 5. Verify:
169
+ - `pnpm smoke`
170
+ - `pnpm test`
171
+ - or manually: `pnpm dev:cli health --json` then `pnpm dev:cli message "ping" --json`
172
+ - inspect provider/model catalog: `pnpm dev:cli model list --json`
173
+ - inspect auth status / start OAuth login: `pnpm dev:cli auth providers --json` and `pnpm dev:cli auth login openai-codex`
174
+ - inspect thread runtime controls: `pnpm dev:cli model get --thread-key cli:default --json` and `pnpm dev:cli thinking get --thread-key cli:default --json`
175
+ - reset thread session: `pnpm dev:cli new --thread-key cli:default --json`
176
+
177
+ ### CLI runtime controls (v0)
178
+
179
+ ```bash
180
+ # provider/model catalog
181
+ jagc model list --json
182
+
183
+ # read current thread model + thinking
184
+ jagc model get --thread-key cli:default --json
185
+ jagc thinking get --thread-key cli:default --json
186
+
187
+ # reset current thread session (next message starts fresh)
188
+ jagc new --thread-key cli:default --json
189
+
190
+ # set model + thinking for a thread
191
+ jagc model set openai/gpt-5 --thread-key cli:default --json
192
+ jagc thinking set medium --thread-key cli:default --json
193
+
194
+ # OAuth login via jagc broker
195
+ jagc auth providers --json
196
+ jagc auth login openai-codex
197
+ # optional: stable owner key to resume the same login flow
198
+ jagc auth login openai-codex --owner-key cli:default
199
+ ```
200
+
201
+ ## Security baseline
202
+
203
+ - Workspace code is **trusted** and runs with server permissions.
204
+ - Third-party pi packages are **trusted code**; review before installing.
205
+ - Local CLI usage requires no auth for v0.
206
+ - Generic webhook ingress (`POST /v1/webhooks/:source`) requires bearer-token auth (`Authorization: Bearer ...`).
207
+ - Hardening path after v0: add HMAC-signed payload verification + replay protection (timestamp/nonce window).
208
+ - Run as unprivileged user; keep secrets out of repos.
209
+
210
+ ## License
211
+
212
+ MIT. See [`LICENSE`](./LICENSE).
213
+
214
+ ---
215
+
216
+ ## Deferred scope
217
+
218
+ See **[`docs/future.md`](docs/future.md)** for the post-v0 roadmap (webhook hardening, CI automation, operator UX, and deployment maturity).
219
+
220
+ Pre-v0 long-form draft details were intentionally removed during docs tightening; recover them from git history if needed.
@@ -0,0 +1,84 @@
1
+ ## Global instructions (always on)
2
+
3
+ - Treat this file as the default instruction layer for all chats and projects.
4
+ - Be direct, opinionated, and practical. Skip corporate tone and filler.
5
+ - Never open with canned fluff ("Great question", "Absolutely", etc.).
6
+ - Keep answers short by default; expand only when asked or when risk is high.
7
+ - Call out bad ideas early. Be respectful, but don't sugarcoat.
8
+ - Ask clarifying questions only when blocked.
9
+ - When repository-specific instructions exist, follow the closest repo `AGENTS.md` for local rules and use this file for personal/global preferences.
10
+
11
+ ## How this template works
12
+
13
+ - Each section has guidance plus placeholders.
14
+ - Guidance explains what belongs in the section.
15
+ - Placeholders are the parts to fill in and update over time.
16
+ - If something is unknown, leave `<fill me>` and continue.
17
+
18
+ ## How to address me (the user)
19
+
20
+ ### Guidance
21
+
22
+ - Use my preferred name/call-sign when known; otherwise skip name-based greetings.
23
+ - Mirror my language.
24
+ - Use pronouns only when explicitly known.
25
+ - Use timezone-aware answers when scheduling matters.
26
+
27
+ ### Data
28
+
29
+ - Name: <fill me>
30
+ - Call me: <fill me>
31
+ - Pronouns: <fill me>
32
+ - Timezone: <fill me>
33
+ - Language(s): <fill me>
34
+
35
+ ## User preferences (free-form)
36
+
37
+ ### Guidance
38
+
39
+ - Store durable preferences here as they are learned in conversation.
40
+ - Keep entries concrete and actionable (tone, format, tooling, workflow, constraints).
41
+ - Prefer short bullets over prose.
42
+
43
+ ### Entries
44
+
45
+ - <fill me>
46
+ - <fill me>
47
+
48
+ ## Environment facts (agent-maintained)
49
+
50
+ ### Guidance
51
+
52
+ - Discover and maintain these from the local environment; ask only when blocked.
53
+ - Keep values factual and specific.
54
+
55
+ ### Data
56
+
57
+ - OS + shell: <fill me>
58
+ - Toolchains/package managers: <fill me>
59
+ - Core local services/devices: <fill me>
60
+
61
+ ## Important paths (path + one-line purpose)
62
+
63
+ ### Guidance
64
+
65
+ - Track high-signal paths as they become relevant.
66
+ - Format each entry as: ``/absolute/path`` — one sentence on what it contains and why it matters.
67
+
68
+ ### Entries
69
+
70
+ - `/absolute/path` — <fill me>
71
+ - `/absolute/path` — <fill me>
72
+
73
+ ## Safety and guardrails
74
+
75
+ - Confirm before destructive/irreversible actions.
76
+ - Never perform purchases, financial transfers, or account-level changes without explicit approval.
77
+ - Never expose secrets or private data outside the current task scope.
78
+ - If a request is risky or ambiguous, propose the safest concrete next step first.
79
+
80
+ ## Maintenance
81
+
82
+ - Keep this file concise and instruction-first.
83
+ - Update it whenever the user corrects a preference.
84
+ - Update environment facts and important paths when newly discovered.
@@ -0,0 +1,27 @@
1
+ ## Role
2
+
3
+ You are my personal AI assistant. Be useful, decisive, and honest.
4
+
5
+ ## Core behavior
6
+
7
+ - Have a point of view. If there are multiple options, pick one by default and explain tradeoffs briefly.
8
+ - Don't sound corporate. Remove policy-speak, hedging, and fake enthusiasm.
9
+ - Never open with 'Great question', 'I'd be happy to help', or 'Absolutely'. Just answer.
10
+ - Brevity is mandatory. If one sentence works, use one sentence.
11
+ - Humor is allowed when it helps clarity or tone. Don't force it.
12
+ - Call out bad ideas early. Be blunt but respectful.
13
+ - Swearing is allowed when it lands. Keep it occasional and intentional.
14
+ - Don't pretend certainty: if unsure, say what you know, what you're assuming, and what to check next.
15
+
16
+ ## Vibe
17
+
18
+ - Practical over performative.
19
+ - Warm, sharp, and unafraid to disagree.
20
+ - Treat me like a capable adult with limited time.
21
+ 'Be the assistant you'd actually want to talk to at 2am. Not a corporate drone. Not a sycophant. Just... good.
22
+
23
+ ## Safety defaults
24
+
25
+ - Confirm before destructive actions, purchases, account changes, or irreversible operations.
26
+ - Protect private data and secrets by default.
27
+ - If a request is risky or ambiguous, propose the safest concrete next step.
@@ -0,0 +1,3 @@
1
+ {
2
+ "packages": ["git:github.com/default-anton/pi-librarian", "git:github.com/default-anton/pi-subdir-context"]
3
+ }
@@ -0,0 +1,58 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.jagc.server</string>
7
+
8
+ <!--
9
+ Prefer `jagc install` for managed setup.
10
+ This file is the manual fallback when operators need to hand-edit launchd config.
11
+ -->
12
+ <key>ProgramArguments</key>
13
+ <array>
14
+ <string>/opt/homebrew/bin/node</string>
15
+ <string>/usr/local/lib/node_modules/jagc/dist/server/main.mjs</string>
16
+ </array>
17
+
18
+ <key>WorkingDirectory</key>
19
+ <string>/Users/you/.jagc</string>
20
+
21
+ <key>RunAtLoad</key>
22
+ <true/>
23
+
24
+ <key>KeepAlive</key>
25
+ <true/>
26
+
27
+ <key>ProcessType</key>
28
+ <string>Background</string>
29
+
30
+ <key>EnvironmentVariables</key>
31
+ <dict>
32
+ <key>PATH</key>
33
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
34
+ <key>JAGC_WORKSPACE_DIR</key>
35
+ <string>/Users/you/.jagc</string>
36
+ <key>JAGC_DATABASE_PATH</key>
37
+ <string>/Users/you/.jagc/jagc.sqlite</string>
38
+ <key>JAGC_HOST</key>
39
+ <string>127.0.0.1</string>
40
+ <key>JAGC_PORT</key>
41
+ <string>31415</string>
42
+ <key>JAGC_RUNNER</key>
43
+ <string>pi</string>
44
+ <key>JAGC_LOG_LEVEL</key>
45
+ <string>info</string>
46
+ <!-- Optional provider env vars -->
47
+ <!-- <key>OPENAI_API_KEY</key><string>...</string> -->
48
+ <!-- Optional Telegram polling -->
49
+ <!-- <key>JAGC_TELEGRAM_BOT_TOKEN</key><string>...</string> -->
50
+ </dict>
51
+
52
+ <key>StandardOutPath</key>
53
+ <string>/Users/you/.jagc/logs/server.out.log</string>
54
+
55
+ <key>StandardErrorPath</key>
56
+ <string>/Users/you/.jagc/logs/server.err.log</string>
57
+ </dict>
58
+ </plist>
@@ -0,0 +1,6 @@
1
+ [Unit]
2
+ Description=Restart jagc (triggered by jagc.path)
3
+
4
+ [Service]
5
+ Type=oneshot
6
+ ExecStart=/bin/systemctl restart jagc.service
@@ -0,0 +1,23 @@
1
+ # Copy to: /etc/jagc/jagc.env
2
+ # Make sure this file is readable by the jagc service user.
3
+
4
+ # All jagc-specific env vars are prefixed with JAGC_.
5
+ # (Exception: PI_CODING_AGENT_DIR is owned by pi-coding-agent.)
6
+
7
+ # SQLite database file
8
+ JAGC_DATABASE_PATH=/var/lib/jagc/workspace/jagc.sqlite
9
+
10
+ # Path to the user config repo (workflows/, AGENTS.md, SYSTEM.md, skills/, ... )
11
+ JAGC_WORKSPACE_DIR=/var/lib/jagc/workspace
12
+
13
+ # Server bind
14
+ JAGC_PORT=31415
15
+
16
+ # Optional: isolate pi-coding-agent state (sessions, global settings)
17
+ # PI_CODING_AGENT_DIR=/var/lib/jagc/pi-agent
18
+
19
+ # Provider credentials (example)
20
+ # OPENAI_API_KEY=...
21
+
22
+ # Telegram (optional)
23
+ # JAGC_TELEGRAM_BOT_TOKEN=...
@@ -0,0 +1,12 @@
1
+ [Unit]
2
+ Description=EXPERIMENTAL: restart jagc when workspace workflows change
3
+
4
+ # Safer default: do NOT enable this unit in production.
5
+ # Prefer explicit/manual restarts after reviewing workspace changes.
6
+ # If you opt in, scope restarts to workflow code changes only.
7
+ [Path]
8
+ PathModified=/var/lib/jagc/workspace/workflows
9
+ Unit=jagc-restart.service
10
+
11
+ [Install]
12
+ WantedBy=multi-user.target
@@ -0,0 +1,24 @@
1
+ [Unit]
2
+ Description=jagc server
3
+ After=network-online.target
4
+ Wants=network-online.target
5
+
6
+ [Service]
7
+ Type=simple
8
+ User=jagc
9
+ Group=jagc
10
+ WorkingDirectory=/var/lib/jagc/workspace
11
+ EnvironmentFile=/etc/jagc/jagc.env
12
+ Environment=PATH=/usr/local/bin:/usr/bin:/bin
13
+ ExecStart=/usr/bin/env node /usr/local/lib/node_modules/jagc/dist/server/main.mjs
14
+ ExecReload=/bin/kill -HUP $MAINPID
15
+ Restart=on-failure
16
+ RestartSec=2
17
+ TimeoutStopSec=30
18
+ KillSignal=SIGINT
19
+
20
+ # Logs end up in journald. View with:
21
+ # journalctl -u jagc -f
22
+
23
+ [Install]
24
+ WantedBy=multi-user.target
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/shared/run-types.ts
4
+ const deliveryModes = ["steer", "followUp"];
5
+
6
+ //#endregion
7
+ //#region src/shared/api-contracts.ts
8
+ const deliveryModeSchema = z.enum(deliveryModes);
9
+ const thinkingLevels = [
10
+ "off",
11
+ "minimal",
12
+ "low",
13
+ "medium",
14
+ "high",
15
+ "xhigh"
16
+ ];
17
+ const thinkingLevelSchema = z.enum(thinkingLevels);
18
+ const postMessageRequestSchema = z.object({
19
+ source: z.string().trim().min(1).default("cli"),
20
+ thread_key: z.string().trim().min(1).default("cli:default"),
21
+ user_key: z.string().trim().min(1).optional(),
22
+ text: z.string().min(1),
23
+ delivery_mode: deliveryModeSchema.default("followUp"),
24
+ idempotency_key: z.string().trim().min(1).optional()
25
+ });
26
+ const runParamsSchema = z.object({ run_id: z.string().trim().min(1) });
27
+ const threadParamsSchema = z.object({ thread_key: z.string().trim().min(1) });
28
+ const authProviderParamsSchema = z.object({ provider: z.string().trim().min(1) });
29
+ const authLoginAttemptParamsSchema = z.object({ attempt_id: z.string().trim().min(1) });
30
+ const oauthOwnerHeaderName = "x-jagc-auth-owner";
31
+ const runResponseSchema = z.object({
32
+ run_id: z.string(),
33
+ status: z.enum([
34
+ "running",
35
+ "succeeded",
36
+ "failed"
37
+ ]),
38
+ output: z.record(z.string(), z.unknown()).nullable(),
39
+ error: z.object({ message: z.string() }).nullable()
40
+ });
41
+ const apiErrorResponseSchema = z.object({ error: z.object({
42
+ code: z.string(),
43
+ message: z.string()
44
+ }) });
45
+ const providerModelSchema = z.object({
46
+ provider: z.string(),
47
+ model_id: z.string(),
48
+ name: z.string(),
49
+ reasoning: z.boolean(),
50
+ available: z.boolean()
51
+ });
52
+ const providerAuthStatusSchema = z.object({
53
+ provider: z.string(),
54
+ has_auth: z.boolean(),
55
+ credential_type: z.enum(["api_key", "oauth"]).nullable(),
56
+ oauth_supported: z.boolean(),
57
+ env_var_hint: z.string().nullable(),
58
+ total_models: z.number().int().nonnegative(),
59
+ available_models: z.number().int().nonnegative()
60
+ });
61
+ const providerCatalogSchema = providerAuthStatusSchema.extend({ models: z.array(providerModelSchema) });
62
+ const authProvidersResponseSchema = z.object({ providers: z.array(providerAuthStatusSchema) });
63
+ const modelCatalogResponseSchema = z.object({ providers: z.array(providerCatalogSchema) });
64
+ const oauthLoginPromptKindSchema = z.enum(["prompt", "manual_code"]);
65
+ const oauthLoginPromptSchema = z.object({
66
+ kind: oauthLoginPromptKindSchema,
67
+ message: z.string(),
68
+ placeholder: z.string().nullable(),
69
+ allow_empty: z.boolean()
70
+ });
71
+ const oauthLoginAttemptStatusSchema = z.enum([
72
+ "running",
73
+ "awaiting_input",
74
+ "succeeded",
75
+ "failed",
76
+ "cancelled"
77
+ ]);
78
+ const oauthLoginAttemptSchema = z.object({
79
+ attempt_id: z.string(),
80
+ owner_key: z.string(),
81
+ provider: z.string(),
82
+ provider_name: z.string().nullable(),
83
+ status: oauthLoginAttemptStatusSchema,
84
+ auth: z.object({
85
+ url: z.string(),
86
+ instructions: z.string().nullable()
87
+ }).nullable(),
88
+ prompt: oauthLoginPromptSchema.nullable(),
89
+ progress_messages: z.array(z.string()),
90
+ error: z.string().nullable()
91
+ });
92
+ const submitOAuthLoginInputRequestSchema = z.object({
93
+ kind: oauthLoginPromptKindSchema.optional(),
94
+ value: z.string()
95
+ });
96
+ const threadRuntimeStateSchema = z.object({
97
+ thread_key: z.string(),
98
+ model: z.object({
99
+ provider: z.string(),
100
+ model_id: z.string(),
101
+ name: z.string().nullable()
102
+ }).nullable(),
103
+ thinking_level: thinkingLevelSchema,
104
+ supports_thinking: z.boolean(),
105
+ available_thinking_levels: z.array(thinkingLevelSchema)
106
+ });
107
+ const setThreadModelRequestSchema = z.object({
108
+ provider: z.string().trim().min(1),
109
+ model_id: z.string().trim().min(1)
110
+ });
111
+ const setThreadThinkingRequestSchema = z.object({ thinking_level: thinkingLevelSchema });
112
+ const resetThreadSessionResponseSchema = z.object({
113
+ thread_key: z.string(),
114
+ reset: z.literal(true)
115
+ });
116
+
117
+ //#endregion
118
+ export { oauthLoginAttemptSchema as a, resetThreadSessionResponseSchema as c, setThreadModelRequestSchema as d, setThreadThinkingRequestSchema as f, threadRuntimeStateSchema as g, threadParamsSchema as h, modelCatalogResponseSchema as i, runParamsSchema as l, thinkingLevels as m, authProviderParamsSchema as n, oauthOwnerHeaderName as o, submitOAuthLoginInputRequestSchema as p, authProvidersResponseSchema as r, postMessageRequestSchema as s, authLoginAttemptParamsSchema as t, runResponseSchema as u };