@inceptionstack/roundhouse 0.5.8 → 0.5.10
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/package.json +1 -1
- package/src/cli/cron-commands.ts +16 -2
- package/src/cron/runner.ts +26 -5
- package/src/cron/scheduler.ts +15 -6
- package/src/gateway/gateway.ts +6 -0
- package/src/gateway/tools.md +114 -0
package/package.json
CHANGED
package/src/cli/cron-commands.ts
CHANGED
|
@@ -105,9 +105,23 @@ export async function cronAdd(store: CronStore, positional: string[], flags: Rec
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export async function cronList(store: CronStore, _positional: string[], flags: Record<string, string>): Promise<void> {
|
|
108
|
-
|
|
108
|
+
let jobs = await store.listJobs();
|
|
109
|
+
|
|
110
|
+
// Hide completed one-shot jobs unless --all flag is set
|
|
111
|
+
if (!flags.all) {
|
|
112
|
+
const filtered: typeof jobs = [];
|
|
113
|
+
for (const j of jobs) {
|
|
114
|
+
if (j.schedule.type === "once") {
|
|
115
|
+
const state = await store.getState(j.id);
|
|
116
|
+
if (state.totalRuns > 0) continue;
|
|
117
|
+
}
|
|
118
|
+
filtered.push(j);
|
|
119
|
+
}
|
|
120
|
+
jobs = filtered;
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
if (jobs.length === 0) {
|
|
110
|
-
console.log("No cron jobs
|
|
124
|
+
console.log("No active cron jobs.");
|
|
111
125
|
return;
|
|
112
126
|
}
|
|
113
127
|
if (flags.json) {
|
package/src/cron/runner.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getAgentFactory } from "../agents/registry";
|
|
9
|
-
// TODO: route through TransportAdapter.notify() when multi-transport lands
|
|
10
9
|
import { sendTelegramToMany } from "../transports/telegram/notify";
|
|
10
|
+
import { sendIpc } from "../ipc";
|
|
11
11
|
import { CronStore, generateRunId } from "./store";
|
|
12
12
|
import { buildTemplateContext, renderTemplate } from "./template";
|
|
13
13
|
import type { CronJobConfig, CronRunRecord } from "./types";
|
|
@@ -20,6 +20,7 @@ export class CronRunner {
|
|
|
20
20
|
constructor(
|
|
21
21
|
private store: CronStore,
|
|
22
22
|
private agentConfig?: GatewayConfig["agent"],
|
|
23
|
+
private notifyFn?: (text: string) => Promise<void>,
|
|
23
24
|
) {}
|
|
24
25
|
|
|
25
26
|
async runJob(
|
|
@@ -127,10 +128,9 @@ export class CronRunner {
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
private async notify(job: CronJobConfig, record: CronRunRecord): Promise<void> {
|
|
131
|
+
// Apply onlyOn filter if configured (for both routes)
|
|
130
132
|
const tg = job.notify?.telegram;
|
|
131
|
-
if (!tg
|
|
132
|
-
|
|
133
|
-
if (!shouldNotify(tg.onlyOn, record.status)) return;
|
|
133
|
+
if (tg?.onlyOn && !shouldNotify(tg.onlyOn, record.status)) return;
|
|
134
134
|
|
|
135
135
|
const icon = runStatusIcon(record.status);
|
|
136
136
|
const dur = `${(record.durationMs / 1000).toFixed(1)}s`;
|
|
@@ -145,6 +145,27 @@ export class CronRunner {
|
|
|
145
145
|
body = `${header}\nError: ${record.error.slice(0, NOTIFY_MAX_ERROR_CHARS)}`;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
// Route 1: Explicit Telegram chat IDs configured on the job
|
|
149
|
+
if (tg?.chatIds?.length) {
|
|
150
|
+
await sendTelegramToMany(tg.chatIds, body);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Route 2: Direct callback from gateway (no loopback socket)
|
|
155
|
+
if (this.notifyFn) {
|
|
156
|
+
try {
|
|
157
|
+
await this.notifyFn(body);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.warn(`[cron] ${job.id} notify callback failed:`, (err as Error).message);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Route 3: Fallback to IPC (for CLI-triggered runs outside gateway)
|
|
165
|
+
try {
|
|
166
|
+
await sendIpc({ type: "notify", text: body });
|
|
167
|
+
} catch {
|
|
168
|
+
console.warn(`[cron] ${job.id} IPC notify failed (gateway not running?)`);
|
|
169
|
+
}
|
|
149
170
|
}
|
|
150
171
|
}
|
package/src/cron/scheduler.ts
CHANGED
|
@@ -41,9 +41,9 @@ export class CronSchedulerService {
|
|
|
41
41
|
private lastHeartbeatAt = 0; // 0 = fires on first tick after startup (intentional catch-up)
|
|
42
42
|
private tickMs: number;
|
|
43
43
|
|
|
44
|
-
constructor(private opts?: { tickMs?: number; agentConfig?: GatewayConfig["agent"]; notifyChatIds?: number[] }) {
|
|
44
|
+
constructor(private opts?: { tickMs?: number; agentConfig?: GatewayConfig["agent"]; notifyChatIds?: number[]; notifyFn?: (text: string) => Promise<void> }) {
|
|
45
45
|
this.store = new CronStore();
|
|
46
|
-
this.runner = new CronRunner(this.store, this.opts?.agentConfig);
|
|
46
|
+
this.runner = new CronRunner(this.store, this.opts?.agentConfig, this.opts?.notifyFn);
|
|
47
47
|
this.queue = new PQueue({ concurrency: 1 });
|
|
48
48
|
this.tickMs = this.opts?.tickMs ?? TICK_MS;
|
|
49
49
|
}
|
|
@@ -88,10 +88,19 @@ export class CronSchedulerService {
|
|
|
88
88
|
|
|
89
89
|
async listJobs(): Promise<Array<{ job: CronJobConfig; state: CronJobState }>> {
|
|
90
90
|
await this.reload();
|
|
91
|
-
return this.jobs
|
|
92
|
-
job
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
return this.jobs
|
|
92
|
+
.filter((job) => {
|
|
93
|
+
// Hide completed one-shot jobs (already fired, won't run again)
|
|
94
|
+
if (job.schedule.type === "once") {
|
|
95
|
+
const state = this.states.get(job.id);
|
|
96
|
+
if (state && state.totalRuns > 0) return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
})
|
|
100
|
+
.map((job) => ({
|
|
101
|
+
job,
|
|
102
|
+
state: this.states.get(job.id) ?? emptyState(job.id),
|
|
103
|
+
}));
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
async trigger(jobId: string): Promise<void> {
|
package/src/gateway/gateway.ts
CHANGED
|
@@ -325,6 +325,12 @@ export class Gateway {
|
|
|
325
325
|
this.cronScheduler = new CronSchedulerService({
|
|
326
326
|
agentConfig: this.config.agent,
|
|
327
327
|
notifyChatIds: this.config.chat.notifyChatIds,
|
|
328
|
+
notifyFn: async (text: string) => {
|
|
329
|
+
const chatIds = this.config.chat.notifyChatIds;
|
|
330
|
+
if (chatIds?.length && this.transport) {
|
|
331
|
+
await this.transport.notify(chatIds, text);
|
|
332
|
+
}
|
|
333
|
+
},
|
|
328
334
|
});
|
|
329
335
|
try {
|
|
330
336
|
await this.cronScheduler.start();
|
package/src/gateway/tools.md
CHANGED
|
@@ -52,3 +52,117 @@ Users can also manage jobs via Telegram:
|
|
|
52
52
|
- `/crons trigger <id>` — run now
|
|
53
53
|
- `/crons pause <id>` — disable
|
|
54
54
|
- `/crons resume <id>` — enable
|
|
55
|
+
|
|
56
|
+
## roundhouse message
|
|
57
|
+
|
|
58
|
+
Send a message to the user via all active transports (Telegram, etc.) without spawning an agent turn.
|
|
59
|
+
|
|
60
|
+
**Usage:**
|
|
61
|
+
```bash
|
|
62
|
+
roundhouse message "Hello from the server!"
|
|
63
|
+
roundhouse message --session main "Targeted to primary chat"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Git & GitHub (gh CLI)
|
|
67
|
+
|
|
68
|
+
Use `gh` CLI for all GitHub operations. It handles authentication automatically.
|
|
69
|
+
|
|
70
|
+
**Common patterns:**
|
|
71
|
+
```bash
|
|
72
|
+
# Push branches
|
|
73
|
+
git push origin <branch>
|
|
74
|
+
|
|
75
|
+
# Create PRs
|
|
76
|
+
gh pr create --base main --head <branch> --title "..." --body "..."
|
|
77
|
+
|
|
78
|
+
# Merge PRs
|
|
79
|
+
gh pr merge <number> --squash --admin
|
|
80
|
+
|
|
81
|
+
# Check CI status
|
|
82
|
+
gh pr checks <number>
|
|
83
|
+
gh run list --limit 5
|
|
84
|
+
|
|
85
|
+
# Create releases / tags
|
|
86
|
+
git tag v1.2.3 && git push origin v1.2.3
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Prefer `gh` over raw git for:**
|
|
90
|
+
- PR creation and merging
|
|
91
|
+
- CI/workflow status checks
|
|
92
|
+
- Release creation
|
|
93
|
+
- Repository settings
|
|
94
|
+
|
|
95
|
+
## mcporter (MCP Server CLI)
|
|
96
|
+
|
|
97
|
+
Call tools from configured MCP servers (AWS APIs, docs, infrastructure).
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
mcporter list # show available servers
|
|
101
|
+
mcporter list <server> --schema # show tools + parameters
|
|
102
|
+
mcporter call <server>.<tool> key=val # call a tool
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Examples:**
|
|
106
|
+
```bash
|
|
107
|
+
mcporter call 'aws-mcp.sts_GetCallerIdentity()'
|
|
108
|
+
mcporter call aws-mcp.s3_ListBuckets
|
|
109
|
+
mcporter call 'aws-documentation.search(query: "Lambda timeout")'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> Ensure PATH includes `~/.local/bin` for `mcporter`/`uvx` discovery.
|
|
113
|
+
|
|
114
|
+
## playwright-cli (Browser Automation)
|
|
115
|
+
|
|
116
|
+
Headless browser automation for testing web UIs, scraping, screenshots.
|
|
117
|
+
|
|
118
|
+
**Core workflow:** open → snapshot → interact → close
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
playwright-cli open "https://example.com" # launch + navigate
|
|
122
|
+
playwright-cli snapshot # accessibility tree with [ref=eN]
|
|
123
|
+
playwright-cli click e5 # click element by ref
|
|
124
|
+
playwright-cli fill e3 "search query" # type into input
|
|
125
|
+
playwright-cli screenshot # save viewport PNG
|
|
126
|
+
playwright-cli close # close browser
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Other commands:**
|
|
130
|
+
```bash
|
|
131
|
+
playwright-cli requests # list network requests
|
|
132
|
+
playwright-cli request <index> # show request details
|
|
133
|
+
playwright-cli cookie-list # list cookies
|
|
134
|
+
playwright-cli eval "document.title" # run JS in page
|
|
135
|
+
playwright-cli pdf # save page as PDF
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Use for:** E2E testing, visual verification, form automation, web scraping.
|
|
139
|
+
**NOT for:** API-only testing (use curl), static file reading.
|
|
140
|
+
|
|
141
|
+
## codex exec
|
|
142
|
+
|
|
143
|
+
Delegate tasks to Codex CLI (architecture design, parallel research, code review).
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
codex exec "Design a retry mechanism with exponential backoff for this module"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Good for: brainstorming, getting a second opinion, architecture decisions, reducing bikeshedding.
|
|
150
|
+
|
|
151
|
+
## Memory Management
|
|
152
|
+
|
|
153
|
+
Durable state files the agent can read and update:
|
|
154
|
+
|
|
155
|
+
- `~/MEMORY.md` — stable facts, preferences, project context (edit existing entries, don't append duplicates)
|
|
156
|
+
- `~/daily/YYYY-MM-DD/front-page.md` — today's work log, decisions, open loops
|
|
157
|
+
- `~/daily/YYYY-MM-DD/articles/` — detailed write-ups for durable topics
|
|
158
|
+
|
|
159
|
+
## AWS CLI
|
|
160
|
+
|
|
161
|
+
Full AWS access via instance role (us-east-1). No credentials needed.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
aws sts get-caller-identity
|
|
165
|
+
aws s3 ls
|
|
166
|
+
aws logs tail /aws/lambda/<name> --since 1h
|
|
167
|
+
aws cloudformation describe-stacks --stack-name <name>
|
|
168
|
+
```
|