@inceptionstack/roundhouse 0.4.4 → 0.4.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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/pi/extensions/web-search.ts +8 -1
- package/src/agents/kiro/kiro-adapter.ts +2 -1
- package/src/cli/cli.ts +12 -1
- package/src/cli/setup.ts +32 -17
- package/src/config.ts +13 -0
- package/src/gateway.ts +1 -5
package/README.md
CHANGED
|
@@ -178,7 +178,7 @@ Without a config file, defaults are used with env vars (`TELEGRAM_BOT_TOKEN`, `B
|
|
|
178
178
|
|
|
179
179
|
| Field | Description |
|
|
180
180
|
|-------|-------------|
|
|
181
|
-
| `agent.type` | Agent backend: `"pi"`
|
|
181
|
+
| `agent.type` | Agent backend: `"pi"`, `"kiro"` |
|
|
182
182
|
| `agent.cwd` | Working directory for the agent |
|
|
183
183
|
| `agent.sessionDir` | Override session storage path |
|
|
184
184
|
| `chat.botUsername` | Bot display name for Chat SDK |
|
|
@@ -492,7 +492,7 @@ No other changes needed — the gateway's unified handler covers all platforms.
|
|
|
492
492
|
| `src/agents/base-adapter.ts` | Abstract base class — adapter interface contract |
|
|
493
493
|
| `src/agents/registry.ts` | Agent type → factory registry |
|
|
494
494
|
| `src/config.ts` | Shared config loading, defaults, env overrides |
|
|
495
|
-
| `test/` | Unit tests (vitest,
|
|
495
|
+
| `test/` | Unit + integration tests (vitest, 311 passing) |
|
|
496
496
|
|
|
497
497
|
## CI/CD
|
|
498
498
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { Type } from "typebox";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
3
|
|
|
4
4
|
export default function (pi: ExtensionAPI) {
|
|
5
5
|
pi.registerTool({
|
|
@@ -46,6 +46,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
46
46
|
}),
|
|
47
47
|
signal: controller.signal,
|
|
48
48
|
});
|
|
49
|
+
} catch (err: any) {
|
|
50
|
+
clearTimeout(timeout);
|
|
51
|
+
const msg = err.name === "AbortError" ? "Request timed out (30s)" : `Network error: ${err.message}`;
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: "text", text: msg }],
|
|
54
|
+
details: { query: params.query, error: err.name },
|
|
55
|
+
};
|
|
49
56
|
} finally {
|
|
50
57
|
clearTimeout(timeout);
|
|
51
58
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { resolve } from "node:path";
|
|
15
15
|
import type { AgentAdapterFactory, AgentMessage, AgentResponse, AgentStreamEvent, AdapterInfo } from "../../types.js";
|
|
16
|
+
import { ROUNDHOUSE_VERSION } from "../../config.js";
|
|
16
17
|
import { BaseAdapter } from "../base-adapter.js";
|
|
17
18
|
import { spawnKiroCli, shutdownProcess, getKiroCliVersion, type AcpProcess, type InitializeResult, type SessionNewResult } from "./acp/index.js";
|
|
18
19
|
import { SessionStore, type SessionEntry } from "./session.js";
|
|
@@ -188,7 +189,7 @@ class KiroAdapter extends BaseAdapter {
|
|
|
188
189
|
|
|
189
190
|
await this.mainProcess.client.call<InitializeResult>("initialize", {
|
|
190
191
|
protocolVersion: "1.0",
|
|
191
|
-
clientInfo: { name: "roundhouse", version:
|
|
192
|
+
clientInfo: { name: "roundhouse", version: ROUNDHOUSE_VERSION },
|
|
192
193
|
});
|
|
193
194
|
|
|
194
195
|
if (!this.reaperInterval) {
|
package/src/cli/cli.ts
CHANGED
|
@@ -72,7 +72,12 @@ async function cmdStart() {
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// No systemd service — fall back to foreground
|
|
75
|
+
// No systemd service — fall back to foreground. Check config before launching.
|
|
76
|
+
if (!(await fileExists(CONFIG_PATH))) {
|
|
77
|
+
console.error("No config found. Run 'roundhouse setup --telegram' first.");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
console.log("No systemd service found. Running in foreground (use Ctrl+C to stop)...");
|
|
77
82
|
if (process.platform !== "darwin") {
|
|
78
83
|
console.log(" Tip: run 'roundhouse install' to set up the systemd daemon.\n");
|
|
@@ -83,6 +88,12 @@ async function cmdStart() {
|
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
async function cmdRun() {
|
|
91
|
+
// Guard: check config exists before launching gateway
|
|
92
|
+
if (!(await fileExists(CONFIG_PATH))) {
|
|
93
|
+
console.error("No config found. Run 'roundhouse setup --telegram' first.");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
process.env.ROUNDHOUSE_CONFIG = CONFIG_PATH;
|
|
87
98
|
|
|
88
99
|
// Load .env file so secrets (TELEGRAM_BOT_TOKEN, etc.) are available
|
package/src/cli/setup.ts
CHANGED
|
@@ -443,7 +443,7 @@ async function stepValidateToken(opts: SetupOptions): Promise<BotInfo> {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
async function stepStopGateway(): Promise<void> {
|
|
446
|
-
step("
|
|
446
|
+
step("④", "Checking for running gateway...");
|
|
447
447
|
|
|
448
448
|
if (platform() !== "linux") {
|
|
449
449
|
ok("Not Linux — skipping service check");
|
|
@@ -464,7 +464,7 @@ async function stepStopGateway(): Promise<void> {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
async function stepInstallPackages(opts: SetupOptions, agent: AgentDefinition): Promise<void> {
|
|
467
|
-
step("
|
|
467
|
+
step("⑤", "Installing packages...");
|
|
468
468
|
|
|
469
469
|
// Roundhouse
|
|
470
470
|
const rhInstalled = whichSync("roundhouse");
|
|
@@ -596,12 +596,12 @@ async function stepInstallPackages(opts: SetupOptions, agent: AgentDefinition):
|
|
|
596
596
|
|
|
597
597
|
async function stepStoreSecrets(opts: SetupOptions, botInfo: BotInfo): Promise<void> {
|
|
598
598
|
if (!opts.psst) {
|
|
599
|
-
step("
|
|
599
|
+
step("⑧", "Storing secrets...");
|
|
600
600
|
ok("Skipped (default — use --with-psst to enable)");
|
|
601
601
|
return;
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
-
step("
|
|
604
|
+
step("⑧", "Storing secrets in psst...");
|
|
605
605
|
|
|
606
606
|
const secrets: [string, string][] = [
|
|
607
607
|
["TELEGRAM_BOT_TOKEN", opts.botToken],
|
|
@@ -640,7 +640,7 @@ async function stepStoreSecrets(opts: SetupOptions, botInfo: BotInfo): Promise<v
|
|
|
640
640
|
// ── Bundle install ──────────────────────────────────────────────────
|
|
641
641
|
|
|
642
642
|
async function stepInstallBundle(opts: SetupOptions): Promise<void> {
|
|
643
|
-
step("⑥
|
|
643
|
+
step("⑥", "Installing bundle (skills + CLI tools)...");
|
|
644
644
|
|
|
645
645
|
const bundleLog: ProvisionLog = {
|
|
646
646
|
info: (msg) => log(` ${msg}`),
|
|
@@ -657,7 +657,7 @@ async function stepConfigure(
|
|
|
657
657
|
pairResult: PairResult | null,
|
|
658
658
|
agent: AgentDefinition,
|
|
659
659
|
): Promise<void> {
|
|
660
|
-
step("
|
|
660
|
+
step("⑨", "Configuring...");
|
|
661
661
|
|
|
662
662
|
await mkdir(ROUNDHOUSE_DIR, { recursive: true });
|
|
663
663
|
|
|
@@ -766,7 +766,7 @@ async function stepConfigure(
|
|
|
766
766
|
}
|
|
767
767
|
|
|
768
768
|
async function stepPair(opts: SetupOptions, botInfo: BotInfo): Promise<PairResult | null> {
|
|
769
|
-
step("
|
|
769
|
+
step("⑦", "Pairing with Telegram...");
|
|
770
770
|
|
|
771
771
|
// Skip if chat IDs already known
|
|
772
772
|
if (opts.notifyChatIds.length > 0) {
|
|
@@ -822,13 +822,13 @@ async function stepPair(opts: SetupOptions, botInfo: BotInfo): Promise<PairResul
|
|
|
822
822
|
}
|
|
823
823
|
|
|
824
824
|
async function stepRegisterCommands(opts: SetupOptions): Promise<void> {
|
|
825
|
-
step("
|
|
825
|
+
step("⑩", "Registering bot commands...");
|
|
826
826
|
await registerBotCommands(opts.botToken);
|
|
827
827
|
ok(`${BOT_COMMANDS.length} commands registered with Telegram`);
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
async function stepInstallSystemd(opts: SetupOptions): Promise<void> {
|
|
831
|
-
step("
|
|
831
|
+
step("⑩b", "Installing systemd service...");
|
|
832
832
|
|
|
833
833
|
if (!opts.systemd) {
|
|
834
834
|
ok("Skipped (--no-systemd)");
|
|
@@ -873,7 +873,7 @@ async function stepInstallSystemd(opts: SetupOptions): Promise<void> {
|
|
|
873
873
|
}
|
|
874
874
|
|
|
875
875
|
async function stepPostflight(): Promise<void> {
|
|
876
|
-
step("
|
|
876
|
+
step("⑪", "Postflight checks...");
|
|
877
877
|
|
|
878
878
|
if (platform() === "linux") {
|
|
879
879
|
if (isServiceActive()) {
|
|
@@ -894,6 +894,11 @@ async function stepPostflight(): Promise<void> {
|
|
|
894
894
|
if (!whichSync("ffmpeg")) {
|
|
895
895
|
warn("ffmpeg not found (install for voice support)");
|
|
896
896
|
}
|
|
897
|
+
|
|
898
|
+
if (!process.env.TAVILY_API_KEY) {
|
|
899
|
+
warn("TAVILY_API_KEY not set — web search extension won't work");
|
|
900
|
+
log(" Get a free key at https://tavily.com and add to ~/.roundhouse/.env");
|
|
901
|
+
}
|
|
897
902
|
}
|
|
898
903
|
|
|
899
904
|
// ── BotFather Guide ──────────────────────────────────
|
|
@@ -951,11 +956,11 @@ async function runInteractiveTelegramSetup(opts: SetupOptions): Promise<void> {
|
|
|
951
956
|
// Step 5: Install packages
|
|
952
957
|
await stepInstallPackages(opts, agent);
|
|
953
958
|
|
|
954
|
-
// Step
|
|
959
|
+
// Step 6: Install bundle (skills + CLI tools)
|
|
955
960
|
await stepInstallBundle(opts);
|
|
956
961
|
|
|
957
|
-
// Step
|
|
958
|
-
step("
|
|
962
|
+
// Step 7: Pair via Telegram
|
|
963
|
+
step("⑦", "Pairing with Telegram...");
|
|
959
964
|
const nonce = createPairingNonce();
|
|
960
965
|
const pairingLink = createPairingLink(botInfo.username, nonce);
|
|
961
966
|
log(`\n Open this link to pair:\n`);
|
|
@@ -964,6 +969,16 @@ async function runInteractiveTelegramSetup(opts: SetupOptions): Promise<void> {
|
|
|
964
969
|
log(` Or send /start ${nonce} to @${botInfo.username}`);
|
|
965
970
|
log("");
|
|
966
971
|
|
|
972
|
+
// Auto-open the pairing link on macOS
|
|
973
|
+
if (process.platform === "darwin") {
|
|
974
|
+
try {
|
|
975
|
+
execFileSync("open", [pairingLink], { stdio: "ignore" });
|
|
976
|
+
log(" (Opened in Telegram — switch to the app to complete pairing)");
|
|
977
|
+
} catch { /* ignore if open fails */ }
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
log(" Waiting for you to tap the link in Telegram...");
|
|
981
|
+
|
|
967
982
|
const pairResult = await pairTelegram(
|
|
968
983
|
opts.botToken, botInfo.username, opts.users,
|
|
969
984
|
300_000, log, { nonce, showLink: false },
|
|
@@ -977,17 +992,17 @@ async function runInteractiveTelegramSetup(opts: SetupOptions): Promise<void> {
|
|
|
977
992
|
}
|
|
978
993
|
}
|
|
979
994
|
|
|
980
|
-
// Step
|
|
995
|
+
// Step 8: Store secrets
|
|
981
996
|
await stepStoreSecrets(opts, botInfo);
|
|
982
997
|
|
|
983
|
-
// Step
|
|
998
|
+
// Step 9: Write config
|
|
984
999
|
await stepConfigure(opts, botInfo, pairResult, agent);
|
|
985
1000
|
|
|
986
|
-
// Step
|
|
1001
|
+
// Step 10: Register commands + install service
|
|
987
1002
|
await stepRegisterCommands(opts);
|
|
988
1003
|
await stepInstallSystemd(opts);
|
|
989
1004
|
|
|
990
|
-
// Step
|
|
1005
|
+
// Step 11: Verify
|
|
991
1006
|
await stepPostflight();
|
|
992
1007
|
|
|
993
1008
|
// Done!
|
package/src/config.ts
CHANGED
|
@@ -8,8 +8,21 @@
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import { readFile, access } from "node:fs/promises";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
11
14
|
import type { GatewayConfig } from "./types";
|
|
12
15
|
|
|
16
|
+
// ── Version ──────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const __configDir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
/** Roundhouse package version (read from package.json at startup) */
|
|
21
|
+
export const ROUNDHOUSE_VERSION: string = (() => {
|
|
22
|
+
try { return JSON.parse(readFileSync(join(__configDir, "..", "package.json"), "utf8")).version; }
|
|
23
|
+
catch { return "unknown"; }
|
|
24
|
+
})();
|
|
25
|
+
|
|
13
26
|
// ── Path constants ───────────────────────────────────
|
|
14
27
|
|
|
15
28
|
/** New canonical config root */
|
package/src/gateway.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { isTelegramThread, postTelegramHtml, handleTelegramHtmlStream } from "./
|
|
|
13
13
|
import { SttService, enrichAttachmentsWithTranscripts, DEFAULT_STT_CONFIG } from "./voice/stt-service";
|
|
14
14
|
import { sendTelegramToMany } from "./notify/telegram";
|
|
15
15
|
import { runDoctor, formatDoctorTelegram, createDoctorContext } from "./cli/doctor/runner";
|
|
16
|
-
import { ROUNDHOUSE_DIR } from "./config";
|
|
16
|
+
import { ROUNDHOUSE_DIR, ROUNDHOUSE_VERSION } from "./config";
|
|
17
17
|
import { CronSchedulerService } from "./cron/scheduler";
|
|
18
18
|
import { isBuiltinJob } from "./cron/helpers";
|
|
19
19
|
import { formatSchedule, formatRunCounts, jobEnabledIcon } from "./cron/format";
|
|
@@ -65,10 +65,6 @@ import { join, dirname, basename } from "node:path";
|
|
|
65
65
|
import { fileURLToPath } from "node:url";
|
|
66
66
|
|
|
67
67
|
const __gatewayDir = dirname(fileURLToPath(import.meta.url));
|
|
68
|
-
const ROUNDHOUSE_VERSION: string = (() => {
|
|
69
|
-
try { return JSON.parse(readFileSync(join(__gatewayDir, "..", "package.json"), "utf8")).version; }
|
|
70
|
-
catch { return "unknown"; }
|
|
71
|
-
})();
|
|
72
68
|
|
|
73
69
|
function telegramChatIdFromThreadId(threadId: unknown): number | null {
|
|
74
70
|
if (typeof threadId !== "string") return null;
|