poke-gate 0.1.1 → 0.1.5

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.
@@ -32,12 +32,13 @@ class GateService: ObservableObject {
32
32
 
33
33
  func runPokeLogin() {
34
34
  let fullPath = shellPath()
35
+ let npxBin = findNpx()
35
36
  let proc = Process()
36
37
  proc.executableURL = URL(fileURLWithPath: "/bin/zsh")
37
- proc.arguments = ["-c", "npx -y poke@latest login"]
38
+ proc.arguments = ["-c", "\(npxBin) -y poke@latest login"]
38
39
  proc.environment = ["HOME": NSHomeDirectory(), "PATH": fullPath]
39
40
  try? proc.run()
40
- appendLog("Launched poke login — check your browser.")
41
+ appendLog("Launched poke login (npx: \(npxBin)) — check your browser.")
41
42
  }
42
43
 
43
44
  func autoStartIfNeeded() {
@@ -90,19 +91,82 @@ class GateService: ObservableObject {
90
91
  }
91
92
  }
92
93
 
93
- private func shellPath() -> String {
94
- let loginShell = ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
95
- let pathProc = Process()
96
- let pathPipe = Pipe()
97
- pathProc.executableURL = URL(fileURLWithPath: loginShell)
98
- pathProc.arguments = ["-ilc", "echo $PATH"]
99
- pathProc.standardOutput = pathPipe
100
- pathProc.standardError = FileHandle.nullDevice
101
- pathProc.environment = ["HOME": NSHomeDirectory()]
102
- try? pathProc.run()
103
- pathProc.waitUntilExit()
104
- let data = pathPipe.fileHandleForReading.readDataToEndOfFile()
105
- return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
94
+ func shellPath() -> String {
95
+ let home = NSHomeDirectory()
96
+ let fallback = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin"
97
+
98
+ // Try multiple shells/strategies to get PATH
99
+ let strategies: [(String, [String])] = [
100
+ ("/bin/zsh", ["-ilc", "echo $PATH"]),
101
+ ("/bin/zsh", ["-lc", "echo $PATH"]),
102
+ ("/bin/bash", ["-lc", "echo $PATH"]),
103
+ ]
104
+
105
+ for (shell, args) in strategies {
106
+ let proc = Process()
107
+ let pipe = Pipe()
108
+ proc.executableURL = URL(fileURLWithPath: shell)
109
+ proc.arguments = args
110
+ proc.standardOutput = pipe
111
+ proc.standardError = FileHandle.nullDevice
112
+ proc.environment = ["HOME": home]
113
+ do {
114
+ try proc.run()
115
+ proc.waitUntilExit()
116
+ if proc.terminationStatus == 0 {
117
+ let data = pipe.fileHandleForReading.readDataToEndOfFile()
118
+ if let path = String(data: data, encoding: .utf8)?
119
+ .trimmingCharacters(in: .whitespacesAndNewlines),
120
+ !path.isEmpty {
121
+ return path
122
+ }
123
+ }
124
+ } catch {
125
+ continue
126
+ }
127
+ }
128
+
129
+ // Fallback: build PATH from common locations
130
+ var paths = fallback.split(separator: ":").map(String.init)
131
+
132
+ let commonDirs = [
133
+ "\(home)/.nvm/versions/node",
134
+ "\(home)/.volta/bin",
135
+ "\(home)/.fnm/aliases/default/bin",
136
+ "\(home)/.local/bin",
137
+ "\(home)/.cargo/bin",
138
+ "/opt/homebrew/bin",
139
+ "/usr/local/bin",
140
+ ]
141
+
142
+ for dir in commonDirs {
143
+ if FileManager.default.fileExists(atPath: dir) {
144
+ if dir.contains(".nvm") {
145
+ // Find the latest node version in nvm
146
+ if let versions = try? FileManager.default.contentsOfDirectory(atPath: dir) {
147
+ if let latest = versions.sorted().last {
148
+ let binPath = "\(dir)/\(latest)/bin"
149
+ if !paths.contains(binPath) { paths.insert(binPath, at: 0) }
150
+ }
151
+ }
152
+ } else if !paths.contains(dir) {
153
+ paths.insert(dir, at: 0)
154
+ }
155
+ }
156
+ }
157
+
158
+ return paths.joined(separator: ":")
159
+ }
160
+
161
+ private func findNpx() -> String {
162
+ let path = shellPath()
163
+ for dir in path.split(separator: ":") {
164
+ let npxPath = "\(dir)/npx"
165
+ if FileManager.default.isExecutableFile(atPath: npxPath) {
166
+ return npxPath
167
+ }
168
+ }
169
+ return "npx"
106
170
  }
107
171
 
108
172
  private func launchProcess() {
@@ -112,15 +176,17 @@ class GateService: ObservableObject {
112
176
  appendLog("Starting poke-gate…")
113
177
 
114
178
  let fullPath = shellPath()
179
+ let npxBin = findNpx()
180
+
181
+ appendLog("Using npx at: \(npxBin)")
115
182
 
116
183
  let proc = Process()
117
184
  let pipe = Pipe()
118
185
 
119
186
  proc.executableURL = URL(fileURLWithPath: "/bin/zsh")
120
- proc.arguments = ["-c", "npx -y poke-gate --verbose"]
187
+ proc.arguments = ["-c", "\(npxBin) -y poke-gate --verbose"]
121
188
  proc.environment = ProcessInfo.processInfo.environment.merging(
122
189
  [
123
- "POKE_API_KEY": resolveToken() ?? "",
124
190
  "PATH": fullPath,
125
191
  ],
126
192
  uniquingKeysWith: { _, new in new }
@@ -23,6 +23,11 @@ struct Poke_macOS_GateApp: App {
23
23
  }
24
24
  .windowResizability(.contentSize)
25
25
 
26
+ Window("Agents", id: "agents") {
27
+ AgentsView()
28
+ }
29
+ .defaultSize(width: 700, height: 480)
30
+
26
31
  Window("About", id: "about") {
27
32
  AboutView()
28
33
  }
@@ -110,6 +115,11 @@ struct PopoverContent: View {
110
115
  openWindow(id: "logs")
111
116
  }
112
117
 
118
+ ActionButton(icon: "bolt.fill", label: "Agents") {
119
+ NSApp.activate(ignoringOtherApps: true)
120
+ openWindow(id: "agents")
121
+ }
122
+
113
123
  ActionButton(icon: "gearshape", label: "Settings") {
114
124
  NSApp.activate(ignoringOtherApps: true)
115
125
  openWindow(id: "settings")
@@ -259,12 +259,15 @@
259
259
  ENABLE_PREVIEWS = YES;
260
260
  GENERATE_INFOPLIST_FILE = YES;
261
261
  INFOPLIST_FILE = "Poke macOS Gate/Info.plist";
262
+ INFOPLIST_KEY_CFBundleDisplayName = "Poke macOS Gate";
263
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
262
264
  INFOPLIST_KEY_NSHumanReadableCopyright = "";
263
265
  LD_RUNPATH_SEARCH_PATHS = (
264
266
  "$(inherited)",
265
267
  "@executable_path/../Frameworks",
266
268
  );
267
- MARKETING_VERSION = 0.1.0;
269
+ MACOSX_DEPLOYMENT_TARGET = 26.0;
270
+ MARKETING_VERSION = 0.1.5;
268
271
  PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
269
272
  PRODUCT_NAME = "$(TARGET_NAME)";
270
273
  REGISTER_APP_GROUPS = YES;
@@ -291,12 +294,15 @@
291
294
  ENABLE_PREVIEWS = YES;
292
295
  GENERATE_INFOPLIST_FILE = YES;
293
296
  INFOPLIST_FILE = "Poke macOS Gate/Info.plist";
297
+ INFOPLIST_KEY_CFBundleDisplayName = "Poke macOS Gate";
298
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
294
299
  INFOPLIST_KEY_NSHumanReadableCopyright = "";
295
300
  LD_RUNPATH_SEARCH_PATHS = (
296
301
  "$(inherited)",
297
302
  "@executable_path/../Frameworks",
298
303
  );
299
- MARKETING_VERSION = 0.1.0;
304
+ MACOSX_DEPLOYMENT_TARGET = 26.0;
305
+ MARKETING_VERSION = 0.1.5;
300
306
  PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
301
307
  PRODUCT_NAME = "$(TARGET_NAME)";
302
308
  REGISTER_APP_GROUPS = YES;
@@ -0,0 +1,75 @@
1
+ import { defineConfig } from 'vitepress'
2
+ import { withMermaid } from 'vitepress-plugin-mermaid'
3
+
4
+ export default withMermaid(
5
+ defineConfig({
6
+ title: 'Poke Gate',
7
+ description: 'Let your Poke AI assistant access your machine',
8
+ head: [
9
+ ['link', { rel: 'icon', href: '/logo.png' }],
10
+ ],
11
+ themeConfig: {
12
+ logo: '/logo.png',
13
+ nav: [
14
+ { text: 'Guide', link: '/getting-started' },
15
+ { text: 'Agents', link: '/agents/' },
16
+ { text: 'CLI', link: '/cli' },
17
+ {
18
+ text: 'Download',
19
+ items: [
20
+ { text: 'macOS App', link: 'https://github.com/f/poke-gate/releases/latest' },
21
+ { text: 'Homebrew', link: '/getting-started#homebrew' },
22
+ { text: 'npm', link: 'https://www.npmjs.com/package/poke-gate' },
23
+ ]
24
+ }
25
+ ],
26
+ sidebar: [
27
+ {
28
+ text: 'Guide',
29
+ items: [
30
+ { text: 'Getting Started', link: '/getting-started' },
31
+ { text: 'How It Works', link: '/how-it-works' },
32
+ { text: 'Tools', link: '/tools' },
33
+ ]
34
+ },
35
+ {
36
+ text: 'Agents',
37
+ items: [
38
+ { text: 'Overview', link: '/agents/' },
39
+ { text: 'Creating Agents', link: '/agents/creating' },
40
+ { text: 'Installing Agents', link: '/agents/installing' },
41
+ { text: 'Community Agents', link: '/agents/community' },
42
+ { text: 'Beeper Example', link: '/agents/beeper' },
43
+ { text: 'Sharing Agents', link: '/agents/sharing' },
44
+ ]
45
+ },
46
+ {
47
+ text: 'Reference',
48
+ items: [
49
+ { text: 'macOS App', link: '/macos-app' },
50
+ { text: 'CLI Reference', link: '/cli' },
51
+ { text: 'Security', link: '/security' },
52
+ ]
53
+ }
54
+ ],
55
+ socialLinks: [
56
+ { icon: 'github', link: 'https://github.com/f/poke-gate' },
57
+ { icon: 'npm', link: 'https://www.npmjs.com/package/poke-gate' },
58
+ ],
59
+ footer: {
60
+ message: 'Community project — not affiliated with Poke or The Interaction Company.',
61
+ copyright: 'Released under the MIT License.',
62
+ },
63
+ editLink: {
64
+ pattern: 'https://github.com/f/poke-gate/edit/main/docs/:path',
65
+ text: 'Edit this page on GitHub',
66
+ },
67
+ },
68
+ mermaid: {
69
+ theme: 'neutral',
70
+ themeVariables: {
71
+ fontSize: '13px',
72
+ },
73
+ },
74
+ })
75
+ )
@@ -0,0 +1,107 @@
1
+ # Beeper Agent
2
+
3
+ The Beeper agent fetches messages from the last hour via [Beeper Desktop](https://beeper.com)'s local API, groups them by sender, and sends a summary to your Poke agent.
4
+
5
+ ## What it does
6
+
7
+ Every hour:
8
+
9
+ 1. Calls Beeper's local API at `http://localhost:23373`
10
+ 2. Searches for messages from the last 60 minutes
11
+ 3. Filters out messages you sent (only shows incoming)
12
+ 4. Groups messages by sender name
13
+ 5. Formats a summary with sender name, message count, and last 3 messages
14
+ 6. Sends the summary to Poke via `sendMessage`
15
+
16
+ ## Prerequisites
17
+
18
+ - [Beeper Desktop](https://beeper.com) running on your machine
19
+ - Beeper API token (find it in Beeper Desktop > Settings > API)
20
+ - Signed in to Poke (`npx poke login`)
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ npx poke-gate agent get beeper
26
+ ```
27
+
28
+ When prompted, paste your Beeper token:
29
+
30
+ ```
31
+ BEEPER_TOKEN (Find it in Beeper Desktop > Settings > API): <paste>
32
+ ```
33
+
34
+ ## Test
35
+
36
+ ```bash
37
+ npx poke-gate run-agent beeper
38
+ ```
39
+
40
+ Expected output:
41
+
42
+ ```
43
+ [agents] Running agent: beeper (beeper.1h.js)
44
+ [agents] [beeper] Fetching messages from the last hour...
45
+ [agents] [beeper] Found 42 messages
46
+ [agents] [beeper] Sending summary to Poke...
47
+ [agents] [beeper] Summary sent to Poke.
48
+ [agents] [beeper] completed
49
+ ```
50
+
51
+ ## What Poke receives
52
+
53
+ Your Poke agent gets a message like:
54
+
55
+ > Messages from the last hour (3 people):
56
+ >
57
+ > Alice (5 messages):
58
+ > - Hey, are you free for lunch?
59
+ > - The meeting got moved to 3pm
60
+ > - Can you review my PR?
61
+ >
62
+ > Bob (2 messages):
63
+ > - Deployed the fix
64
+ > - All tests passing now
65
+ >
66
+ > Mom (1 messages):
67
+ > - Don't forget dinner tonight!
68
+
69
+ ## Configuration
70
+
71
+ ### Env variables
72
+
73
+ | Variable | Required | Description |
74
+ |----------|----------|-------------|
75
+ | `BEEPER_TOKEN` | yes | Beeper Desktop API token |
76
+ | `BEEPER_BASE_URL` | no | Override default `http://localhost:23373` |
77
+
78
+ Edit: `~/.config/poke-gate/agents/.env.beeper`
79
+
80
+ ### Change the interval
81
+
82
+ Rename the file to change how often it runs:
83
+
84
+ ```bash
85
+ # Every 30 minutes
86
+ mv ~/.config/poke-gate/agents/beeper.1h.js ~/.config/poke-gate/agents/beeper.30m.js
87
+ ```
88
+
89
+ Or use the macOS Agents editor.
90
+
91
+ ## Frontmatter
92
+
93
+ ```javascript
94
+ /**
95
+ * @agent beeper
96
+ * @name Beeper Message Digest
97
+ * @description Fetches messages from the last hour via Beeper Desktop and sends a summary to Poke.
98
+ * @interval 1h
99
+ * @env BEEPER_TOKEN - Beeper Desktop local API token (Settings > API)
100
+ * @env BEEPER_BASE_URL - (optional) Override default http://localhost:23373
101
+ * @author f
102
+ */
103
+ ```
104
+
105
+ ## Source
106
+
107
+ [View on GitHub](https://github.com/f/poke-gate/blob/main/examples/agents/beeper.1h.js)
@@ -0,0 +1,77 @@
1
+ # Community Agents
2
+
3
+ Ready-to-use agents you can install with a single command. All agents are open source and included in the [Poke Gate repository](https://github.com/f/poke-gate/tree/main/examples/agents).
4
+
5
+ ## Beeper Message Digest
6
+
7
+ Fetches messages from the last hour via [Beeper Desktop](https://beeper.com)'s local API, groups them by sender, and sends a summary to Poke. Great for staying on top of conversations across all your messaging platforms without checking each one.
8
+
9
+ | | |
10
+ |---|---|
11
+ | **File** | `beeper.1h.js` |
12
+ | **Interval** | Every hour |
13
+ | **Requires** | Beeper Desktop running, API token |
14
+
15
+ ```bash
16
+ npx poke-gate agent get beeper
17
+ ```
18
+
19
+ [Full documentation →](/agents/beeper)
20
+
21
+ ---
22
+
23
+ ## Screen Time Report
24
+
25
+ Sends a daily summary of your Mac usage — currently running apps, uptime, and top processes. Poke learns your work patterns and can answer questions like "what was I doing yesterday?" or "how long have I been working today?".
26
+
27
+ | | |
28
+ |---|---|
29
+ | **File** | `screentime.24h.js` |
30
+ | **Interval** | Every 24 hours |
31
+ | **Requires** | Nothing — works out of the box |
32
+
33
+ ```bash
34
+ npx poke-gate agent get screentime
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Battery Guardian
40
+
41
+ Monitors your battery and alerts you via Poke when it drops below 20% on battery power. Only alerts once per discharge cycle — won't spam you. Resets when you plug in.
42
+
43
+ | | |
44
+ |---|---|
45
+ | **File** | `battery.30m.js` |
46
+ | **Interval** | Every 30 minutes |
47
+ | **Requires** | Nothing — works out of the box |
48
+
49
+ ```bash
50
+ npx poke-gate agent get battery
51
+ ```
52
+
53
+ ::: tip Custom threshold
54
+ Set `BATTERY_THRESHOLD` in `.env.battery` to change the alert level (default: 20%).
55
+ :::
56
+
57
+ ---
58
+
59
+ ## WiFi Logger
60
+
61
+ Tracks which WiFi network you're on and notifies Poke when you switch networks or disconnect. This gives Poke passive context about your location — it knows if you're at home, at the office, or at a cafe without you telling it.
62
+
63
+ | | |
64
+ |---|---|
65
+ | **File** | `wifi.30m.js` |
66
+ | **Interval** | Every 30 minutes |
67
+ | **Requires** | Nothing — works out of the box |
68
+
69
+ ```bash
70
+ npx poke-gate agent get wifi
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Want more?
76
+
77
+ Check the [Sharing Agents](/agents/sharing) page for ideas and instructions on contributing your own agent to the community.
@@ -0,0 +1,132 @@
1
+ # Creating Agents
2
+
3
+ This guide walks you through creating an agent from scratch.
4
+
5
+ ## Step 1: Create the file
6
+
7
+ Agents live in `~/.config/poke-gate/agents/`. Create a file with the naming convention `name.interval.js`:
8
+
9
+ ```bash
10
+ touch ~/.config/poke-gate/agents/hello.1h.js
11
+ ```
12
+
13
+ This creates an agent called "hello" that runs every hour.
14
+
15
+ ## Step 2: Add frontmatter
16
+
17
+ Start with the frontmatter block. This is optional but recommended — it's displayed in the macOS Agents editor.
18
+
19
+ ```javascript
20
+ /**
21
+ * @agent hello
22
+ * @name Hello World
23
+ * @description Sends a greeting to Poke every hour.
24
+ * @interval 1h
25
+ * @author you
26
+ */
27
+ ```
28
+
29
+ ## Step 3: Write your logic
30
+
31
+ Agents are standard Node.js ESM scripts. They can import the Poke SDK and any globally installed packages.
32
+
33
+ ```javascript
34
+ /**
35
+ * @agent hello
36
+ * @name Hello World
37
+ * @description Sends a greeting to Poke every hour.
38
+ * @interval 1h
39
+ */
40
+
41
+ import { Poke, getToken } from "poke";
42
+
43
+ const token = getToken();
44
+ if (!token) {
45
+ console.error("Not signed in. Run: npx poke login");
46
+ process.exit(1);
47
+ }
48
+
49
+ const poke = new Poke({ apiKey: token });
50
+ await poke.sendMessage("Hello! This is an automated message from my Hello agent.");
51
+
52
+ console.log("Sent greeting to Poke.");
53
+ ```
54
+
55
+ ## Step 4: Add env variables (optional)
56
+
57
+ If your agent needs secrets (API tokens, URLs, etc.), create a `.env.<name>` file:
58
+
59
+ ```bash
60
+ nano ~/.config/poke-gate/agents/.env.hello
61
+ ```
62
+
63
+ ```env
64
+ # My custom config
65
+ MY_API_KEY=secret_123
66
+ ```
67
+
68
+ Then read them in your script:
69
+
70
+ ```javascript
71
+ const apiKey = process.env.MY_API_KEY;
72
+ ```
73
+
74
+ ## Step 5: Test it
75
+
76
+ Run your agent manually:
77
+
78
+ ```bash
79
+ npx poke-gate run-agent hello
80
+ ```
81
+
82
+ You should see:
83
+
84
+ ```
85
+ [agents] Running agent: hello (hello.1h.js)
86
+ [agents] [hello] Sent greeting to Poke.
87
+ [agents] [hello] completed
88
+ ```
89
+
90
+ ## Step 6: Let it run
91
+
92
+ Start Poke Gate normally. Your agent will be discovered and scheduled:
93
+
94
+ ```bash
95
+ npx poke-gate
96
+ ```
97
+
98
+ ```
99
+ [agents] Found 1 agent(s):
100
+ Hello World (every 1h)
101
+ [agents] Running agent: hello (hello.1h.js)
102
+ ```
103
+
104
+ ## Tips
105
+
106
+ - **Keep agents fast.** They have a 5-minute timeout. If your agent takes longer, it'll be killed.
107
+ - **Use `console.log`** for debugging. Output appears in the Poke Gate logs.
108
+ - **Handle errors gracefully.** If your agent throws, it logs the error and continues to the next scheduled run.
109
+ - **Change the interval** by renaming the file (e.g. `hello.1h.js` → `hello.30m.js`) or using the macOS Agents editor.
110
+
111
+ ## Template
112
+
113
+ Here's a minimal template to copy:
114
+
115
+ ```javascript
116
+ /**
117
+ * @agent my-agent
118
+ * @name My Agent
119
+ * @description What this agent does.
120
+ * @interval 1h
121
+ */
122
+
123
+ import { Poke, getToken } from "poke";
124
+
125
+ const poke = new Poke({ apiKey: getToken() });
126
+
127
+ // Your logic here
128
+ const result = "Something useful";
129
+
130
+ await poke.sendMessage(result);
131
+ console.log("Done.");
132
+ ```
@@ -0,0 +1,85 @@
1
+ # Agents
2
+
3
+ Agents are scheduled scripts that **push information from your computer to Poke**. They run in the background, gather data from local sources (APIs, files, services), and send it to your Poke agent — so Poke learns about what's happening on your machine without you asking.
4
+
5
+ Think of it this way: **Tools** let Poke pull from your machine (you ask, Poke acts). **Agents** let your machine push to Poke (your computer tells Poke, Poke learns and replies).
6
+
7
+ ::: tip Secure and deterministic
8
+ Agents are **push-only** — they send data to Poke, but Poke cannot reach back into the agent or your computer through them. Each agent is a plain JavaScript file that you write and control. It runs the same way every time, with no AI decision-making involved. The agent doesn't "try" to access your machine — it only does exactly what the script says. This makes agents predictable, auditable, and safe.
9
+ :::
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ subgraph YourMac ["Your Mac"]
14
+ Agent["Agent script"]
15
+ Local["Local data source"]
16
+ Local -->|reads| Agent
17
+ end
18
+
19
+ Agent -->|sendMessage| Poke["Poke Agent"]
20
+ Poke -->|replies| You["You"]
21
+ ```
22
+
23
+ **Example:** A Beeper agent runs every hour, fetches your unread messages, and sends a digest to Poke. Now Poke knows who messaged you — and can answer "did anyone text me?" without needing your machine in real time.
24
+
25
+ ## How agents work
26
+
27
+ 1. You place a `.js` file in `~/.config/poke-gate/agents/`
28
+ 2. The filename defines the schedule: `name.interval.js`
29
+ 3. When Poke Gate connects, it discovers all agents and starts their timers
30
+ 4. Each agent runs once immediately, then repeats on schedule
31
+ 5. Agents use the Poke SDK to send messages — pushing data to your agent
32
+
33
+ ## Naming convention
34
+
35
+ ```
36
+ <name>.<interval>.js
37
+ ```
38
+
39
+ | File | Runs |
40
+ |------|------|
41
+ | `beeper.1h.js` | Every hour |
42
+ | `backup.2h.js` | Every 2 hours |
43
+ | `health.10m.js` | Every 10 minutes |
44
+ | `cleanup.30m.js` | Every 30 minutes |
45
+ | `digest.24h.js` | Every 24 hours |
46
+
47
+ **Intervals:** `Nm` (minutes) or `Nh` (hours). Minimum is **10 minutes**.
48
+
49
+ ## Frontmatter
50
+
51
+ Each agent starts with a JSDoc-style frontmatter block:
52
+
53
+ ```javascript
54
+ /**
55
+ * @agent beeper
56
+ * @name Beeper Message Digest
57
+ * @description Fetches messages from the last hour and sends a summary.
58
+ * @interval 1h
59
+ * @env BEEPER_TOKEN - Beeper Desktop local API token
60
+ * @author f
61
+ */
62
+ ```
63
+
64
+ The `@name` and `@description` are shown in the macOS app's Agents editor and in the scheduler logs.
65
+
66
+ ## Per-agent env files
67
+
68
+ Each agent can have a `.env.<name>` file in the same directory:
69
+
70
+ ```
71
+ ~/.config/poke-gate/agents/.env.beeper
72
+ ```
73
+
74
+ ```env
75
+ BEEPER_TOKEN=your_token_here
76
+ BEEPER_BASE_URL=http://localhost:23373
77
+ ```
78
+
79
+ Variables are injected into the agent's environment automatically. The agent reads them via `process.env.BEEPER_TOKEN`.
80
+
81
+ ## What's next?
82
+
83
+ - [Creating Agents](/agents/creating) — write your first agent from scratch
84
+ - [Installing Agents](/agents/installing) — download community agents
85
+ - [Beeper Example](/agents/beeper) — full walkthrough of a real agent