freeturtle 0.1.14 → 0.1.15

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
@@ -2,15 +2,58 @@
2
2
 
3
3
  An open-source framework for deploying autonomous AI CEOs that run onchain businesses.
4
4
 
5
- ## Quick Start
5
+ ## Getting Started
6
+
7
+ ### 1. Set Up a Server
8
+
9
+ You need a Linux server that runs 24/7. We recommend [Oracle Cloud's free ARM instance](docs/oracle-cloud-setup.md) — 2 CPUs, 12 GB RAM, always free.
10
+
11
+ > **New to servers?** The [Oracle Cloud setup guide](docs/oracle-cloud-setup.md) walks through everything from account creation to SSH. Paste it into an AI chat (ChatGPT, Claude, etc.) and ask it to guide you step by step.
12
+
13
+ ### 2. Create Accounts for Your CEO
14
+
15
+ Before running init, create a separate identity for your CEO:
16
+
17
+ - **Google account** — use it to sign up for everything below
18
+ - **Farcaster** — the account your CEO will post from
19
+ - **Neynar** — API access for Farcaster (sign up at [dev.neynar.com](https://dev.neynar.com))
20
+ - **GitHub** (optional) — if your CEO will manage repos, give it its own account
21
+
22
+ The CEO is effectively a team member who needs its own accounts.
23
+
24
+ ### 3. Install and Run
6
25
 
7
26
  ```bash
8
- pnpm install -g freeturtle
27
+ # Install Node.js and pnpm (if not already installed)
28
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
29
+ sudo apt-get install -y nodejs
30
+ sudo npm install -g pnpm
31
+
32
+ # Install FreeTurtle
33
+ sudo pnpm install -g freeturtle
34
+
35
+ # Set up your CEO (interactive wizard)
9
36
  freeturtle init
37
+
38
+ # Start the daemon
10
39
  freeturtle start
40
+
41
+ # Keep it running after reboot (Linux)
42
+ freeturtle install-service
11
43
  ```
12
44
 
13
- The setup wizard walks you through everything: naming your AI CEO, connecting Farcaster, Telegram, GitHub, a database, and onchain data.
45
+ The setup wizard walks you through naming your AI CEO, picking an LLM provider, connecting Farcaster, Telegram, GitHub, and more.
46
+
47
+ ### 4. Set Up Webhooks (optional)
48
+
49
+ If you want your CEO to auto-respond to Farcaster mentions and replies, you need webhooks. This requires HTTPS, which means a domain and a reverse proxy.
50
+
51
+ The quickest path:
52
+ 1. Get a free subdomain at [duckdns.org](https://www.duckdns.org)
53
+ 2. Install Caddy (`sudo apt install -y caddy`) — it handles HTTPS automatically
54
+ 3. Run `freeturtle webhooks` to register with Neynar
55
+
56
+ See the [Oracle Cloud setup guide](docs/oracle-cloud-setup.md#setting-up-webhooks-farcaster-mentionsreplies) for full instructions.
14
57
 
15
58
  ## What It Does
16
59
 
@@ -35,7 +78,7 @@ FreeTurtle is a Node.js daemon that mostly sleeps and wakes up when:
35
78
  3. The **founder sends a message** via Terminal or Telegram
36
79
  4. A **webhook event** arrives (e.g. someone mentions the CEO on Farcaster)
37
80
 
38
- All three route to the same **task runner**, which:
81
+ All four route to the same **task runner**, which:
39
82
 
40
83
  1. Loads `soul.md` (the CEO's identity and voice)
41
84
  2. Loads recent memory (posting log, post queue)
@@ -80,6 +123,24 @@ All three route to the same **task runner**, which:
80
123
  └──────────────────────────────────────────────────────┘
81
124
  ```
82
125
 
126
+ ## CLI Commands
127
+
128
+ ```bash
129
+ freeturtle init # Set up a new AI CEO
130
+ freeturtle start # Start the daemon
131
+ freeturtle start --chat # Start with interactive terminal chat
132
+ freeturtle status # Show daemon status
133
+ freeturtle send "message" # Send a message to the running CEO
134
+ freeturtle setup # Reconfigure LLM provider
135
+ freeturtle connect farcaster # Set up Farcaster signer
136
+ freeturtle webhooks # Set up Neynar webhooks (mentions, replies, watched users/channels)
137
+ freeturtle approvals # List pending approval requests
138
+ freeturtle approve <id> # Approve a pending action
139
+ freeturtle reject <id> # Reject a pending action
140
+ freeturtle update # Update to the latest version
141
+ freeturtle install-service # Install as a systemd service (Linux)
142
+ ```
143
+
83
144
  ## Modules
84
145
 
85
146
  ### Workspace (always loaded)
@@ -189,7 +250,7 @@ Controls the daemon. Markdown format:
189
250
  ```markdown
190
251
  ## LLM
191
252
  - provider: claude_api
192
- - model: claude-sonnet-4-5-20250514
253
+ - model: claude-sonnet-4-5
193
254
  - max_tokens: 4096
194
255
 
195
256
  ## Cron
@@ -236,46 +297,19 @@ DATABASE_URL=postgres://...
236
297
  RPC_URL=https://mainnet.base.org
237
298
  ```
238
299
 
239
- ## CLI Commands
240
-
241
- ```bash
242
- freeturtle init # Set up a new AI CEO
243
- freeturtle start # Start the daemon
244
- freeturtle start --chat # Start with interactive terminal chat
245
- freeturtle status # Show daemon status
246
- freeturtle send "message" # Send a message to the running CEO
247
- freeturtle setup # Reconfigure LLM provider
248
- freeturtle connect farcaster # Set up Farcaster signer
249
- freeturtle webhooks # Set up Neynar webhooks (mentions, replies, watched users/channels)
250
- freeturtle approvals # List pending approval requests
251
- freeturtle approve <id> # Approve a pending action
252
- freeturtle reject <id> # Reject a pending action
253
- freeturtle update # Update to the latest version
254
- freeturtle install-service # Install as a systemd service (Linux)
255
- ```
256
-
257
- ## Hosting
258
-
259
- We recommend [Oracle Cloud's free ARM instance](docs/oracle-cloud-setup.md) — 4 CPUs, 24 GB RAM, always free. The setup guide walks through account creation, instance setup, networking, and installing FreeTurtle as a system service.
260
-
261
- ```bash
262
- # On your server
263
- pnpm install -g freeturtle
264
- freeturtle init
265
- freeturtle start
266
- freeturtle install-service # auto-restart on reboot
267
- ```
300
+ ## Self-Modification
268
301
 
269
- ## Before You Begin
302
+ FreeTurtle CEOs can modify their own behavior at runtime. Everything that defines the CEO — identity, voice, goals, config, memory — is a file in the workspace, and the CEO has tools to read and write those files.
270
303
 
271
- **Create a separate account for your CEO.** Start with a Google account, then use it to sign up for:
304
+ Examples of what you can tell your CEO:
272
305
 
273
- - Farcaster (the account your CEO will post from)
274
- - Neynar (API access for Farcaster)
275
- - GitHub (if your CEO will manage repos)
276
- - Any other services your CEO needs
306
+ - **"Be more direct and honest"** — CEO edits the Voice section of `soul.md` (requires your approval)
307
+ - **"Remember that @rish posts interesting stuff"** — CEO writes a note to `workspace/memory/notes.md`
308
+ - **"Change posting to every 4 hours"** — CEO edits the cron schedule in `config.md` (requires approval, takes effect on restart)
309
+ - **"Add a goal about growing the Discord"** — CEO edits the Goals section of `soul.md` (requires approval)
310
+ - **"Write a brief on this week's engagement"** — CEO writes to `workspace/strategy/`
277
311
 
278
- The CEO is effectively a team member who needs its own accounts. Identity separation keeps things clean.
312
+ Changes to core files (`soul.md`, `config.md`, `.env`) always require founder approval. Memory and notes writes go through freely.
279
313
 
280
314
  ## Policy & Approvals
281
315
 
@@ -305,9 +339,9 @@ Add a `## Policy` section to `config.md`:
305
339
  ```
306
340
 
307
341
  **Allowlist rules:**
308
- - Not set allow all (no restriction)
309
- - Empty list deny everything
310
- - Populated list only allow listed values
342
+ - Not set allow all (no restriction)
343
+ - Empty list deny everything
344
+ - Populated list only allow listed values
311
345
 
312
346
  ### Approval Flow
313
347
 
@@ -392,20 +426,6 @@ Your `.env` file contains API keys and tokens. FreeTurtle automatically sets it
392
426
 
393
427
  Your cloud provider (Oracle, AWS, GCP) has full access to the underlying infrastructure — they can technically read any file on your VM. This is true of all cloud computing and is covered by their terms of service. For most use cases this is fine. If this is unacceptable for your threat model, run FreeTurtle on hardware you physically control.
394
428
 
395
- ## Self-Modification
396
-
397
- FreeTurtle CEOs can modify their own behavior at runtime. Everything that defines the CEO — identity, voice, goals, config, memory — is a file in the workspace, and the CEO has tools to read and write those files.
398
-
399
- Examples of what you can tell your CEO:
400
-
401
- - **"Be more direct and honest"** → CEO edits the Voice section of `soul.md` (requires your approval)
402
- - **"Remember that @rish posts interesting stuff"** → CEO writes a note to `workspace/memory/notes.md`
403
- - **"Change posting to every 4 hours"** → CEO edits the cron schedule in `config.md` (requires approval, takes effect on restart)
404
- - **"Add a goal about growing the Discord"** → CEO edits the Goals section of `soul.md` (requires approval)
405
- - **"Write a brief on this week's engagement"** → CEO writes to `workspace/strategy/`
406
-
407
- Changes to core files (`soul.md`, `config.md`, `.env`) always require founder approval. Memory and notes writes go through freely.
408
-
409
429
  ## The Two-Turtle Vision (v0.2)
410
430
 
411
431
  The current v0.1 is a single-process CEO. v0.2 will split it into two:
@@ -15,14 +15,14 @@ const program = new Command();
15
15
  program
16
16
  .name("freeturtle")
17
17
  .description("An open-source framework for deploying autonomous AI CEOs that run onchain businesses.")
18
- .version("0.1.14");
18
+ .version("0.1.15");
19
19
  program
20
20
  .command("hello")
21
21
  .description("Verify the CLI is working")
22
22
  .action(() => {
23
23
  console.log(" \x1b[38;2;94;255;164m _____ ____\x1b[0m");
24
24
  console.log(" \x1b[38;2;94;255;164m/ \\ | o |\x1b[0m");
25
- console.log(" \x1b[38;2;94;255;164m| |/ ___\\|\x1b[0m FreeTurtle v0.1.14");
25
+ console.log(" \x1b[38;2;94;255;164m| |/ ___\\|\x1b[0m FreeTurtle v0.1.15");
26
26
  console.log(" \x1b[38;2;94;255;164m|_________/\x1b[0m");
27
27
  console.log(" \x1b[38;2;94;255;164m|_|_| |_|_|\x1b[0m");
28
28
  });
@@ -125,6 +125,14 @@ connect
125
125
  const { connectFarcaster } = await import("../src/cli/connect-farcaster.js");
126
126
  await connectFarcaster(opts.dir);
127
127
  });
128
+ connect
129
+ .command("gmail")
130
+ .description("Connect Gmail for reading and sending emails")
131
+ .option("--dir <path>", "Workspace directory", DEFAULT_DIR)
132
+ .action(async (opts) => {
133
+ const { connectGmail } = await import("../src/cli/connect-gmail.js");
134
+ await connectGmail(opts.dir);
135
+ });
128
136
  program
129
137
  .command("webhooks")
130
138
  .description("Set up Neynar webhooks for Farcaster mentions")
@@ -1 +1 @@
1
- {"version":3,"file":"freeturtle.js","sourceRoot":"","sources":["../../bin/freeturtle.ts"],"names":[],"mappings":";AAEA,mGAAmG;AACnG,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAwB,CAAC;AACnE,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,KAAa,EAAE,GAAG,IAAe,EAAW,EAAE;IAC7D,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,YAAY,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;QAC7F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;AAClC,CAAC,CAAwB,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CACV,wFAAwF,CACzF;KACA,OAAO,CAAC,QAAQ,CAAC,CAAC;AAErB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,2BAA2B,CAAC;KACxC,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;IAC/D,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;IACpF,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,qBAAqB,CAAC;KAClC,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACvD,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACrD,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,6BAA6B,CAAC;KAC1C,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,QAAQ,EAAE,gCAAgC,EAAE,KAAK,CAAC;KACzD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACzD,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAC3D,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC9B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACvD,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAC3D,MAAM,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,iBAAiB,CAAC;KAC1B,WAAW,CAAC,sDAAsD,CAAC;KACnE,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,+BAA+B,CAAC,CAAC;IAC5E,MAAM,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;IACzB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IAC/D,MAAM,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,aAAa,CAAC;KACtB,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;KAC/C,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;IACzB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IAC9D,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IACrE,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEL,MAAM,OAAO,GAAG,OAAO;KACpB,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,2BAA2B,CAAC,CAAC;AAE5C,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,+CAA+C,CAAC;KAC5D,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,iCAAiC,CAAC,CAAC;IAC7E,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,+CAA+C,CAAC;KAC5D,MAAM,CAAC,cAAc,EAAE,qBAAqB,EAAE,WAAW,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACpE,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"freeturtle.js","sourceRoot":"","sources":["../../bin/freeturtle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface GmailConnectResult {
2
+ clientId: string;
3
+ clientSecret: string;
4
+ email: string;
5
+ }
6
+ export declare function connectGmail(dir: string): Promise<GmailConnectResult | null>;
@@ -0,0 +1,128 @@
1
+ import * as p from "@clack/prompts";
2
+ import { readFile, writeFile, chmod, mkdir } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { runGoogleOAuthFlow, createGoogleOAuth2Client, } from "../oauth/google.js";
5
+ import { GmailClient } from "../modules/gmail/client.js";
6
+ export async function connectGmail(dir) {
7
+ p.intro("Gmail Setup");
8
+ p.note([
9
+ "We recommend creating a dedicated Google account for your",
10
+ "CEO so emails come from its own identity. You can use this",
11
+ "same Google account to create a GitHub account for the CEO.",
12
+ ].join("\n"), "Recommendation");
13
+ const email = await p.text({
14
+ message: "Gmail address for the CEO",
15
+ placeholder: "ceo@gmail.com",
16
+ validate: (v) => {
17
+ if (!v?.trim())
18
+ return "Required";
19
+ if (!v.includes("@"))
20
+ return "Must be a valid email address";
21
+ return undefined;
22
+ },
23
+ });
24
+ if (p.isCancel(email)) {
25
+ p.cancel("Cancelled.");
26
+ return null;
27
+ }
28
+ p.note([
29
+ "To connect Gmail, you need a Google Cloud OAuth client:",
30
+ "",
31
+ "1. Go to console.cloud.google.com/apis/credentials",
32
+ "2. Create a project (or use an existing one)",
33
+ "3. Enable the Gmail API:",
34
+ " APIs & Services > Library > search 'Gmail API' > Enable",
35
+ "4. Create OAuth 2.0 Client ID:",
36
+ " Credentials > Create Credentials > OAuth client ID",
37
+ " Application type: Desktop app",
38
+ "5. Copy the Client ID and Client Secret",
39
+ "",
40
+ "Tip: paste these instructions into an AI chat and ask it",
41
+ "to walk you through step by step.",
42
+ ].join("\n"), "Google Cloud Setup");
43
+ const clientId = await p.text({
44
+ message: "OAuth Client ID",
45
+ validate: (v) => (v?.trim() ? undefined : "Required"),
46
+ });
47
+ if (p.isCancel(clientId)) {
48
+ p.cancel("Cancelled.");
49
+ return null;
50
+ }
51
+ const clientSecret = await p.text({
52
+ message: "OAuth Client Secret",
53
+ validate: (v) => (v?.trim() ? undefined : "Required"),
54
+ });
55
+ if (p.isCancel(clientSecret)) {
56
+ p.cancel("Cancelled.");
57
+ return null;
58
+ }
59
+ p.note("A browser window will open for Gmail authorization.\nSign in with the CEO's Google account and grant access.", "OAuth Flow");
60
+ const s = p.spinner();
61
+ s.start("Waiting for browser authorization...");
62
+ let refreshToken;
63
+ try {
64
+ refreshToken = await runGoogleOAuthFlow(clientId.trim(), clientSecret.trim());
65
+ s.stop("Authorization successful!");
66
+ }
67
+ catch (err) {
68
+ s.stop("Authorization failed");
69
+ p.log.error(`OAuth failed: ${err instanceof Error ? err.message : "Unknown error"}`);
70
+ return null;
71
+ }
72
+ // Test connection
73
+ s.start("Testing Gmail connection...");
74
+ try {
75
+ const auth = createGoogleOAuth2Client({
76
+ clientId: clientId.trim(),
77
+ clientSecret: clientSecret.trim(),
78
+ refreshToken,
79
+ });
80
+ const client = new GmailClient(auth);
81
+ const sendAs = await client.getSendAs();
82
+ const primary = sendAs.find((a) => a.isPrimary) || sendAs[0];
83
+ const displayName = primary?.displayName;
84
+ s.stop(displayName
85
+ ? `Connected as ${displayName} (${primary?.email})`
86
+ : `Connected as ${primary?.email || email}`);
87
+ }
88
+ catch (err) {
89
+ s.stop("Connection test failed");
90
+ p.log.warn(`Could not verify Gmail access: ${err instanceof Error ? err.message : "Unknown error"}`);
91
+ p.log.warn("The credentials were saved — you can test later with: freeturtle status");
92
+ }
93
+ // Save to .env
94
+ const envPath = join(dir, ".env");
95
+ let envContent = "";
96
+ try {
97
+ envContent = await readFile(envPath, "utf-8");
98
+ }
99
+ catch {
100
+ // No existing .env
101
+ }
102
+ const envVars = {
103
+ GOOGLE_CLIENT_ID: clientId.trim(),
104
+ GOOGLE_CLIENT_SECRET: clientSecret.trim(),
105
+ GOOGLE_GMAIL_REFRESH_TOKEN: refreshToken,
106
+ };
107
+ for (const [key, value] of Object.entries(envVars)) {
108
+ const regex = new RegExp(`^${key}=.*$`, "m");
109
+ if (regex.test(envContent)) {
110
+ envContent = envContent.replace(regex, `${key}=${value}`);
111
+ }
112
+ else {
113
+ envContent +=
114
+ `${envContent.endsWith("\n") || envContent === "" ? "" : "\n"}${key}=${value}\n`;
115
+ }
116
+ }
117
+ await mkdir(dirname(envPath), { recursive: true });
118
+ await writeFile(envPath, envContent, "utf-8");
119
+ await chmod(envPath, 0o600);
120
+ p.log.success("Gmail credentials saved to .env");
121
+ p.outro("Gmail connected!");
122
+ return {
123
+ clientId: clientId.trim(),
124
+ clientSecret: clientSecret.trim(),
125
+ email: email.trim(),
126
+ };
127
+ }
128
+ //# sourceMappingURL=connect-gmail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-gmail.js","sourceRoot":"","sources":["../../../src/cli/connect-gmail.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,10 @@ export declare function testGitHub(token: string): Promise<void>;
10
10
  * Test a Postgres connection URL by connecting and immediately disconnecting.
11
11
  */
12
12
  export declare function testDatabase(url: string): Promise<void>;
13
+ /**
14
+ * Test Gmail OAuth credentials by calling the profile endpoint.
15
+ */
16
+ export declare function testGmail(clientId: string, clientSecret: string, refreshToken: string): Promise<void>;
13
17
  /**
14
18
  * Test an EVM RPC URL by calling eth_blockNumber.
15
19
  */
@@ -40,6 +40,16 @@ export async function testDatabase(url) {
40
40
  await client.end().catch(() => { });
41
41
  }
42
42
  }
43
+ /**
44
+ * Test Gmail OAuth credentials by calling the profile endpoint.
45
+ */
46
+ export async function testGmail(clientId, clientSecret, refreshToken) {
47
+ const { createGoogleOAuth2Client } = await import("../oauth/google.js");
48
+ const { GmailClient } = await import("../modules/gmail/client.js");
49
+ const auth = createGoogleOAuth2Client({ clientId, clientSecret, refreshToken });
50
+ const client = new GmailClient(auth);
51
+ await client.getProfile();
52
+ }
43
53
  /**
44
54
  * Test an EVM RPC URL by calling eth_blockNumber.
45
55
  */
@@ -1 +1 @@
1
- {"version":3,"file":"connection-tests.js","sourceRoot":"","sources":["../../../src/cli/connection-tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC9C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,+BAA+B,KAAK,QAAQ,CAAC,CAAC;IACtE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA0C,CAAC;IACzE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,WAAW,IAAI,SAAS,EAAE,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAa;IAC5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,6BAA6B,EAAE;QACrD,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,MAAM,EAAE,6BAA6B;SACtC;KACF,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc;IAC9C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;QAC9B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,iBAAiB;YACzB,MAAM,EAAE,EAAE;YACV,EAAE,EAAE,CAAC;SACN,CAAC;KACH,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,cAAc,GAAG,CAAC,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;IACnE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"connection-tests.js","sourceRoot":"","sources":["../../../src/cli/connection-tests.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { createInterface } from "node:readline";
5
5
  import { runSetup } from "../setup.js";
6
6
  import { LLMClient } from "../llm.js";
7
7
  import { connectFarcaster } from "./connect-farcaster.js";
8
+ import { connectGmail } from "./connect-gmail.js";
8
9
  import { scanForSecrets, redactSecrets, condenseDocs } from "./intake.js";
9
10
  import { testTelegram, testGitHub, testDatabase, testOnchain } from "./connection-tests.js";
10
11
  import { createWebhook, channelToUrl } from "../webhooks/neynar.js";
@@ -15,7 +16,7 @@ const TURTLE = `
15
16
  \x1b[38;2;94;255;164m|_________/\x1b[0m
16
17
  \x1b[38;2;94;255;164m|_|_| |_|_|\x1b[0m
17
18
 
18
- \x1b[1mFreeTurtle\x1b[0m \x1b[2mv0.1.14\x1b[0m
19
+ \x1b[1mFreeTurtle\x1b[0m \x1b[2mv0.1.15\x1b[0m
19
20
  `;
20
21
  const HATCH_FRAMES = [
21
22
  " 🥚",
@@ -172,6 +173,11 @@ export async function runInit(dir) {
172
173
  telegram: false,
173
174
  telegramToken: "",
174
175
  telegramOwner: "",
176
+ gmail: false,
177
+ googleClientId: "",
178
+ googleClientSecret: "",
179
+ googleRefreshToken: "",
180
+ gmailEmail: "",
175
181
  github: false,
176
182
  githubToken: "",
177
183
  database: false,
@@ -379,6 +385,9 @@ export async function runInit(dir) {
379
385
  " sudo systemctl restart caddy",
380
386
  "",
381
387
  "Your webhook URL will be: https://yourname.duckdns.org/webhook",
388
+ "",
389
+ "Tip: paste these instructions into an AI chat (ChatGPT, Claude,",
390
+ "etc.) and ask it to walk you through step by step.",
382
391
  ].join("\n"), "Webhooks");
383
392
  const enable = await p.confirm({
384
393
  message: "Set up webhooks?",
@@ -546,7 +555,37 @@ export async function runInit(dir) {
546
555
  }
547
556
  return true;
548
557
  },
549
- // 9. GitHub
558
+ // Gmail
559
+ async () => {
560
+ const enable = await p.confirm({
561
+ message: "Connect Gmail? (read and send emails)",
562
+ initialValue: state.gmail,
563
+ });
564
+ if (p.isCancel(enable))
565
+ return false;
566
+ state.gmail = enable;
567
+ if (enable) {
568
+ const result = await connectGmail(dir);
569
+ if (result) {
570
+ state.googleClientId = result.clientId;
571
+ state.googleClientSecret = result.clientSecret;
572
+ state.gmailEmail = result.email;
573
+ // Read the refresh token that connectGmail saved to .env
574
+ try {
575
+ const envContent = await readFile(join(dir, ".env"), "utf-8");
576
+ const match = envContent.match(/^GOOGLE_GMAIL_REFRESH_TOKEN=(.+)$/m);
577
+ if (match)
578
+ state.googleRefreshToken = match[1];
579
+ }
580
+ catch { /* ignore */ }
581
+ }
582
+ else {
583
+ state.gmail = false;
584
+ }
585
+ }
586
+ return true;
587
+ },
588
+ // GitHub
550
589
  async () => {
551
590
  const enable = await p.confirm({
552
591
  message: "Connect GitHub? (issues and file commits)",
@@ -768,6 +807,7 @@ export async function runInit(dir) {
768
807
  const modules = [
769
808
  state.farcaster && "Farcaster",
770
809
  state.telegram && "Telegram",
810
+ state.gmail && "Gmail",
771
811
  state.github && "GitHub",
772
812
  state.database && "Database",
773
813
  state.onchain && "Onchain",
@@ -861,6 +901,9 @@ ${state.founderName}.
861
901
  "### database",
862
902
  `- enabled: ${state.database}`,
863
903
  "",
904
+ "### gmail",
905
+ `- enabled: ${state.gmail}`,
906
+ "",
864
907
  "### github",
865
908
  `- enabled: ${state.github}`,
866
909
  "",
@@ -895,6 +938,12 @@ ${state.founderName}.
895
938
  envLines.push(`TELEGRAM_BOT_TOKEN=${state.telegramToken}`);
896
939
  if (state.telegramOwner)
897
940
  envLines.push(`TELEGRAM_OWNER_ID=${state.telegramOwner}`);
941
+ if (state.googleClientId)
942
+ envLines.push(`GOOGLE_CLIENT_ID=${state.googleClientId}`);
943
+ if (state.googleClientSecret)
944
+ envLines.push(`GOOGLE_CLIENT_SECRET=${state.googleClientSecret}`);
945
+ if (state.googleRefreshToken)
946
+ envLines.push(`GOOGLE_GMAIL_REFRESH_TOKEN=${state.googleRefreshToken}`);
898
947
  if (state.githubToken)
899
948
  envLines.push(`GITHUB_TOKEN=${state.githubToken}`);
900
949
  if (state.dbUrl)