shraga 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -24,7 +24,8 @@ Give it its own machine, its own keys, its own identity. Onboard it once, then d
24
24
  - ⭐ **Multi-user by design.** One Shraga serves a whole team, each with their own sessions,
25
25
  tools, and permissions.
26
26
  - ⭐ **A shared brain that compounds.** It onboards, learns your codebase and conventions, and
27
- carries that knowledge forward across sessions and teammates.
27
+ carries that knowledge forward via skills and sessions. The full org-wide capture is the
28
+ north star ([the shared brain](./docs/shared-brain.md)) — it opens in stages.
28
29
  - **Chat with an agent** that has your workspace, shell, and tools.
29
30
  - **Sessions** you can fork, resume, and revisit.
30
31
  - **Skills**: reusable procedures you teach it once (files in `data/skills/`).
@@ -32,10 +33,71 @@ Give it its own machine, its own keys, its own identity. Onboard it once, then d
32
33
  - **Schedules**: run agent jobs on a cron or in response to events.
33
34
  - **Programmatic API + MCP endpoint**: drive the agent from scripts or from claude.ai.
34
35
 
36
+ ## What's in the box
37
+
38
+ Shraga ships as a small core (this repo, the `shraga` npm package — "CE") plus two ways to grow it.
39
+ Capabilities fall into three tiers:
40
+
41
+ - **Built-in** — ships and runs in CE. Nothing to install; some need credentials configured.
42
+ - **Optional overlay** — an external add-on module loaded at startup via `SHRAGA_OVERLAY`. Not in
43
+ this repo; you bring or build it.
44
+ - **Bring-your-own** — you extend the core yourself through its seams (MCP, engine, skills, features).
45
+
46
+ ### Built-in (ships in CE)
47
+
48
+ | Capability | What you get | Setup |
49
+ |-----------|--------------|-------|
50
+ | **Agent chat & sessions** | Chat with an agent that has your workspace, shell, and tools; fork, resume, and revisit sessions. | none |
51
+ | **Slack** | The agent is **active on Slack** — it replies in threads, manages reactions, handles uploaded files, resolves `@mentions`, and runs polls. Via the public [`mcp-slack-use`](https://github.com/Livshitz/mcp-slack-use) package. | Slack app + tokens |
52
+ | **Scheduler** | Cron schedules and event-triggered tasks that run agent jobs unattended. | none |
53
+ | **Events** | Generic event-bus ingress (`POST /api/events/:source`) that fires matching schedules. | none |
54
+ | **Push notifications** | Web and native push. | none |
55
+ | **Artifacts** | Render HTML artifacts from a session. (PNG export is overlay-only — see below.) | none |
56
+ | **MCP — both directions** | *Consume* external MCP servers to give the agent tools, **and** *expose* Shraga itself as an MCP endpoint (`POST /mcp`) so claude.ai and other clients can drive it. | per-server config |
57
+ | **Engine seam** | Pluggable agent runtime. Claude Code is the built-in engine. | none |
58
+ | **Auth** | Local username/password by default; Firebase optional (`AUTH_PROVIDER=firebase`). | none / Firebase config |
59
+ | **CLI** | `shraga` server bin plus `shraga user add <email> <password>` to seed a local user. | none |
60
+
61
+ ### Optional overlay (add-on module, not in CE)
62
+
63
+ Loaded only when you point `SHRAGA_OVERLAY` at an external module. **None of these ship in this
64
+ repo** — a bare self-host does not get them out of the box:
65
+
66
+ | Capability | Notes |
67
+ |-----------|-------|
68
+ | **Email / Gmail** | Inbound email → agent sessions. **Not built-in** — overlay only. |
69
+ | **GitHub bot** | Issues/PRs → agent sessions with GH-native trust tiers. |
70
+ | **Voice** | Voice input/output. |
71
+ | **Fleet / multi-instance** | Blue-green and multi-instance orchestration. |
72
+ | **PNG artifact export** | Puppeteer-rendered PNGs of artifacts. |
73
+
74
+ ### Bring-your-own (extend it yourself)
75
+
76
+ The core is deliberately small and grows through documented seams (see
77
+ [`AGENTS.md`](./AGENTS.md#the-extension-seams)):
78
+
79
+ | Seam | Use it for |
80
+ |------|-----------|
81
+ | **MCP servers** | Give the agent new tools, per-user or globally. |
82
+ | **Skills** (`data/skills/`) | Reusable procedures you teach the agent once. |
83
+ | **Engines** (`src/server/engine/`) | Swap or add agent runtimes behind the engine seam. |
84
+ | **Server features** (`src/server/features.ts`) | Mount new server-side surfaces via `registerFeature`. |
85
+ | **Client slots** (`src/client/lib/slots.tsx`) | Inject UI into typed render slots without touching core. |
86
+ | **Route extensions** (`data/extensions/*.ext.ts`) | Drop-in public routes (webhooks, OAuth callbacks) per deployment. |
87
+ | **`SHRAGA_OVERLAY`** | Load a whole external add-on module at startup (the overlay tier above). |
88
+
35
89
  ## Quickstart
36
90
 
37
91
  Requires [Bun](https://bun.sh) ≥ 1.0.
38
92
 
93
+ **Fastest — run from npm:**
94
+
95
+ ```bash
96
+ bunx shraga # serve on http://localhost:3032, state in ./data/
97
+ ```
98
+
99
+ **From source (to develop or customize):**
100
+
39
101
  ```bash
40
102
  # 1. Install & configure
41
103
  bun install
@@ -67,7 +67,7 @@ summary: |
67
67
  tools_used:
68
68
  - stripe.get_disputes
69
69
  - stripe.get_charges_by_id
70
- - mcp-slack.post_slack_message
70
+ - mcp-slack-use.post_slack_message
71
71
  tool_call_count: 15
72
72
  tool_failures:
73
73
  - tool: stripe.get_disputes
@@ -1,9 +1,13 @@
1
- // Example: a vendor webhook → event-trigger bridge.
1
+ // Example: a vendor webhook → event-trigger bridge, via the generic helper.
2
2
  //
3
3
  // A vendor (Stripe here) can't send shraga auth, so its webhook can't hit the
4
- // generic POST /api/events/:source endpoint. Instead this extension exposes a
5
- // PUBLIC route, verifies the vendor's OWN signature, then calls ctx.emitEvent()
6
- // which fires any enabled schedule with trigger { kind:'event', source:'stripe' }.
4
+ // generic POST /api/events/:source endpoint. Instead this extension declares a
5
+ // webhook as DATA: ctx.registerWebhook({ source, verify }) mounts a PUBLIC route
6
+ // that verifies the vendor's OWN signature and, on success, emits the event —
7
+ // firing any enabled schedule with trigger { kind:'event', source:'stripe' }.
8
+ //
9
+ // The ONLY per-vendor code is `verify`. Dispatch, dedup (by event id), the route,
10
+ // and typing are all generic (src/server/events/webhook.ts).
7
11
  //
8
12
  // Pair it with a schedule (see the scheduler skill):
9
13
  // { "trigger": { "kind":"event", "source":"stripe", "match":{ "type":"invoice.paid" } },
@@ -11,15 +15,24 @@
11
15
  //
12
16
  // DORMANT until STRIPE_WEBHOOK_SECRET is set — no route is registered without it,
13
17
  // so this ships harmlessly enabled. Swap the verifier for any vendor's scheme.
14
- import type { Express, Request, Response } from 'express';
18
+ import type { Express, Request } from 'express';
15
19
  import { createHmac, timingSafeEqual } from 'node:crypto';
16
20
 
21
+ // Minimal local shape of the loader's ctx (see src/server/extensions.ts).
22
+ interface WebhookOptions {
23
+ source: string;
24
+ verify: (req: Request, raw: string) => boolean | unknown;
25
+ normalize?: (req: Request) => unknown;
26
+ eventId?: (payload: unknown, req: Request) => string | undefined;
27
+ path?: string;
28
+ }
17
29
  interface ExtensionContext {
18
30
  dataPath: (p: string) => string;
19
31
  requireAuth: any;
20
32
  log: (...a: unknown[]) => void;
21
33
  app: Express;
22
34
  emitEvent: (source: string, payload: unknown, opts?: { id?: string }) => void;
35
+ registerWebhook: (opts: WebhookOptions) => string;
23
36
  }
24
37
 
25
38
  /** Minimal Stripe `Stripe-Signature` verification (t=…,v1=…) over the raw body. */
@@ -31,28 +44,23 @@ function verifyStripeSig(raw: string, header: string, secret: string): boolean {
31
44
  try { return timingSafeEqual(Buffer.from(expected), Buffer.from(v1)); } catch { return false; }
32
45
  }
33
46
 
34
- export default function register(app: Express, ctx: ExtensionContext) {
47
+ export default function register(_app: Express, ctx: ExtensionContext) {
35
48
  const secret = process.env.STRIPE_WEBHOOK_SECRET;
36
49
  if (!secret) {
37
50
  ctx.log('stripe-webhook extension dormant (set STRIPE_WEBHOOK_SECRET to enable)');
38
51
  return;
39
52
  }
40
53
 
41
- // PUBLIC route — Stripe authenticates via its own signature, not shraga auth.
42
- // The server's global express.json() already parsed the body AND stashed the exact
43
- // bytes on req.rawBody (see index.ts) verify the signature against those. Adding
44
- // our own express.raw here would be too late: the stream is already consumed.
45
- app.post('/webhooks/stripe', (req: Request, res: Response) => {
46
- const rawBuf = (req as any).rawBody;
47
- const raw = rawBuf instanceof Buffer ? rawBuf.toString('utf8') : '';
48
- const sig = String(req.headers['stripe-signature'] ?? '');
49
- if (!verifyStripeSig(raw, sig, secret)) return res.status(400).json({ error: 'bad signature' });
50
-
51
- // evt.id dedupes Stripe's retries; evt.type is what schedules `match` on.
52
- const evt = req.body; // already parsed by the global JSON body parser
53
- ctx.emitEvent('stripe', evt, { id: evt?.id });
54
- res.json({ received: true });
54
+ // Stripe authenticates via its own signature, not shraga auth. The raw request
55
+ // bytes are read from req.rawBody (stashed by the global express.json in index.ts);
56
+ // `verify` returns true iff the signature matches. Payload = the parsed body, and
57
+ // its `id` dedupes Stripe's retries both handled generically by registerWebhook.
58
+ const path = ctx.registerWebhook({
59
+ source: 'stripe',
60
+ // Keep the original public path (Stripe's dashboard points at /webhooks/stripe).
61
+ path: '/webhooks/stripe',
62
+ verify: (req, raw) => verifyStripeSig(raw, String(req.headers['stripe-signature'] ?? ''), secret),
55
63
  });
56
64
 
57
- ctx.log('stripe-webhook extension ready — POST /webhooks/stripe → emitEvent("stripe")');
65
+ ctx.log(`stripe-webhook extension ready — POST ${path} → emitEvent("stripe")`);
58
66
  }
@@ -19,7 +19,7 @@ if (argv[0] === '--uid') {
19
19
  }
20
20
  const prompt =
21
21
  argv.join(' ').trim() ||
22
- 'Call mcp-slack tool get_slack_channels with limit=2 only. Output the JSON ok field and first channel name, nothing else.';
22
+ 'Call mcp-slack-use tool get_slack_channels with limit=2 only. Output the JSON ok field and first channel name, nothing else.';
23
23
 
24
24
  if (!process.env.ANTHROPIC_API_KEY?.trim()) {
25
25
  console.error('Missing ANTHROPIC_API_KEY — cannot run Claude Agent SDK.');
@@ -12,7 +12,7 @@ Dedup applies only when **you** decide to reach out (not when fulfilling a user
12
12
 
13
13
  1. **Read the comms log** — `Read data/comms-log.jsonl` (tail last 50-100 lines if large). If the file exceeds 500 lines, only read the last 100.
14
14
  2. **Check for recent similar outreach** — same recipient + similar topic within the last 24h = skip or significantly alter the message
15
- 3. **For Slack** — the mcp-slack skill already requires reading channel history before posting (rule #6). That check + this log together cover both same-session and cross-session duplicates.
15
+ 3. **For Slack** — the mcp-slack-use skill already requires reading channel history before posting (rule #6). That check + this log together cover both same-session and cross-session duplicates.
16
16
 
17
17
  ### What counts as a duplicate (proactive only)
18
18
 
@@ -83,7 +83,7 @@ Print a summary table:
83
83
 
84
84
  ```
85
85
  MCP Sync Results:
86
- mcp-slack up to date
86
+ mcp-slack-use up to date
87
87
  mcp-firebase pulled 3 commits
88
88
  mcp-pdf cloned (NEW)
89
89
  mcp-cursor pushed 1, pulled 2
@@ -92,7 +92,7 @@ Missing env vars (add to .env):
92
92
  mcp-stripe STRIPE_SECRET_KEY
93
93
  mcp-gmail GOOGLE_SERVICE_ACCOUNT, GMAIL_USER_EMAIL
94
94
 
95
- All configured: mcp-slack, mcp-firebase, mcp-google-drive
95
+ All configured: mcp-slack-use, mcp-firebase, mcp-google-drive
96
96
  ```
97
97
 
98
98
  ## Rules
@@ -85,7 +85,7 @@ List checkpoints with `git -C "$APP_DIR" log --oneline`; reset to any of them.
85
85
  - `src/server/mcp.ts` — MCP server injection and config
86
86
  - `src/server/skills.ts` — skill loading and injection
87
87
  - `defaults/` — canonical source for runtime-seeded dirs (see Architecture skill § "Defaults → Runtime Seeding Pattern")
88
- - `vendor/` — vendored MCP servers (mcp-firebase, mcp-github, mcp-slack, etc.)
88
+ - `vendor/` — vendored MCP servers (mcp-firebase, mcp-github, mcp-slack-use, etc.)
89
89
  - `data/` — runtime data, git-tracked behavioral config via data-sync (skills, mcps, workspace, schedules, contacts, agent-config, whitelist)
90
90
 
91
91
  ## Where one-off / custom code goes (NOT main `src/`)