@inceptionstack/roundhouse 0.5.4 → 0.5.7
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 +1 -3
- package/architecture.md +37 -19
- package/package.json +2 -1
- package/skills/pr-merge-discipline/SKILL.md +36 -0
- package/skills/roundhouse-cron/SKILL.md +136 -0
- package/src/agents/kiro/kiro-adapter.ts +1 -4
- package/src/agents/pi/pi-adapter.ts +1 -4
- package/src/cli/cli.ts +6 -1
- package/src/cli/doctor/checks/system.ts +1 -1
- package/src/cli/setup/args.ts +8 -9
- package/src/cli/setup/flows.ts +47 -14
- package/src/cli/{setup-logger.ts → setup/logger.ts} +4 -4
- package/src/cli/{setup-prompts.ts → setup/prompts.ts} +23 -2
- package/src/cli/setup/runtime.ts +1 -1
- package/src/cli/setup/steps.ts +5 -5
- package/src/cli/{setup-telegram.ts → setup/telegram.ts} +4 -4
- package/src/cli/setup/types.ts +4 -3
- package/src/cli/setup.ts +8 -8
- package/src/cli/systemd.ts +2 -0
- package/src/cli/update.ts +111 -0
- package/src/cron/runner.ts +2 -1
- package/src/gateway/commands.ts +29 -4
- package/src/{gateway.ts → gateway/gateway.ts} +126 -100
- package/src/gateway/helpers.ts +1 -1
- package/src/gateway/index.ts +2 -5
- package/src/gateway/streaming.ts +1 -1
- package/src/gateway/tools-inject.ts +45 -0
- package/src/gateway/tools.md +54 -0
- package/src/{bundle.ts → provisioning/bundle.ts} +32 -0
- package/src/transports/index.ts +6 -0
- package/src/{telegram-html.ts → transports/telegram/html.ts} +2 -2
- package/src/{pairing.ts → transports/telegram/pairing.ts} +1 -1
- package/src/transports/telegram/telegram-adapter.ts +111 -0
- package/src/transports/types.ts +71 -0
- package/src/voice/providers/whisper.ts +37 -94
- package/src/voice/stt-service.ts +35 -17
- package/src/voice/types.ts +1 -3
- package/src/commands/update.ts +0 -69
- /package/src/{commands.ts → transports/telegram/bot-commands.ts} +0 -0
- /package/src/{telegram-format.ts → transports/telegram/format.ts} +0 -0
- /package/src/{notify/telegram.ts → transports/telegram/notify.ts} +0 -0
- /package/src/{telegram-progress.ts → transports/telegram/progress.ts} +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cli/setup
|
|
2
|
+
* cli/setup/telegram.ts — Telegram API helpers for setup
|
|
3
3
|
*
|
|
4
4
|
* Zero-dependency Telegram Bot API client using global fetch.
|
|
5
5
|
* Token is never logged — redacted in all error messages.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { BOT_COMMANDS } from "
|
|
8
|
+
import { createPairingNonce } from "../../transports/telegram/pairing";
|
|
9
|
+
import { BOT_COMMANDS } from "../../transports/telegram/bot-commands";
|
|
10
10
|
|
|
11
11
|
// ── Types ────────────────────────────────────────────
|
|
12
12
|
|
|
@@ -90,7 +90,7 @@ export async function pairTelegram(
|
|
|
90
90
|
log: (msg: string) => void = console.log,
|
|
91
91
|
opts?: { nonce?: string; showLink?: boolean },
|
|
92
92
|
): Promise<PairResult | null> {
|
|
93
|
-
const nonce = opts?.nonce ??
|
|
93
|
+
const nonce = opts?.nonce ?? createPairingNonce();
|
|
94
94
|
const normalizedUsers = allowedUsers.map((u) => u.replace(/^@/, "").toLowerCase());
|
|
95
95
|
|
|
96
96
|
// Clear stale updates — advance offset past existing
|
package/src/cli/setup/types.ts
CHANGED
|
@@ -18,17 +18,18 @@ export interface SetupOptions {
|
|
|
18
18
|
systemd: boolean;
|
|
19
19
|
voice: boolean;
|
|
20
20
|
psst: boolean;
|
|
21
|
-
nonInteractive: boolean;
|
|
22
21
|
force: boolean;
|
|
23
22
|
dryRun: boolean;
|
|
24
23
|
/** Telegram-focused setup flow */
|
|
25
24
|
telegram: boolean;
|
|
26
|
-
/**
|
|
27
|
-
|
|
25
|
+
/** Non-interactive mode (no TTY prompts) */
|
|
26
|
+
nonInteractive: boolean;
|
|
28
27
|
/** QR code display mode */
|
|
29
28
|
qr: "auto" | "always" | "never";
|
|
30
29
|
/** Agent type (default: pi) */
|
|
31
30
|
agent: string;
|
|
31
|
+
/** Whether --agent was explicitly passed on CLI */
|
|
32
|
+
_agentExplicit?: boolean;
|
|
32
33
|
/** Set by detection: skip agent package install if already configured */
|
|
33
34
|
_skipAgentInstall?: boolean;
|
|
34
35
|
}
|
package/src/cli/setup.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { readFile } from "node:fs/promises";
|
|
13
|
-
import { BOT_COMMANDS } from "../commands";
|
|
13
|
+
import { BOT_COMMANDS } from "../transports/telegram/bot-commands";
|
|
14
14
|
import { atomicWriteJson, execSafe } from "./setup/helpers";
|
|
15
15
|
import { type SetupOptions } from "./setup/types";
|
|
16
16
|
import { parseSetupArgs } from "./setup/args";
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
validateBotToken,
|
|
29
29
|
pairTelegram,
|
|
30
|
-
} from "./setup
|
|
30
|
+
} from "./setup/telegram";
|
|
31
31
|
import {
|
|
32
32
|
stepPreflight,
|
|
33
33
|
stepValidateToken,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
stepPostflight,
|
|
43
43
|
} from "./setup/steps";
|
|
44
44
|
import { resolveAgentForSetup, textLog, textStepLog } from "./setup/runtime";
|
|
45
|
-
import { runInteractiveTelegramSetup,
|
|
45
|
+
import { runInteractiveTelegramSetup, runNonInteractiveTelegramSetup } from "./setup/flows";
|
|
46
46
|
|
|
47
47
|
// ── Orchestrator ─────────────────────────────────────
|
|
48
48
|
|
|
@@ -63,8 +63,8 @@ export async function cmdSetup(argv: string[]): Promise<void> {
|
|
|
63
63
|
|
|
64
64
|
// Route to --telegram flows
|
|
65
65
|
if (opts.telegram) {
|
|
66
|
-
if (opts.
|
|
67
|
-
await
|
|
66
|
+
if (opts.nonInteractive) {
|
|
67
|
+
await runNonInteractiveTelegramSetup(opts);
|
|
68
68
|
} else {
|
|
69
69
|
await runInteractiveTelegramSetup(opts);
|
|
70
70
|
}
|
|
@@ -258,12 +258,12 @@ function printSetupHelp(): void {
|
|
|
258
258
|
console.log(`
|
|
259
259
|
Usage:
|
|
260
260
|
roundhouse setup --telegram Interactive wizard (recommended)
|
|
261
|
-
TELEGRAM_BOT_TOKEN=... roundhouse setup \\\n --telegram --
|
|
261
|
+
TELEGRAM_BOT_TOKEN=... roundhouse setup \\\n --telegram --non-interactive --user USERNAME Non-interactive automation (SSM/cloud-init)
|
|
262
262
|
TELEGRAM_BOT_TOKEN=... roundhouse setup \\\n --user USERNAME Legacy (non-wizard) setup
|
|
263
263
|
|
|
264
264
|
Modes:
|
|
265
|
-
--telegram Telegram-focused setup (wizard or
|
|
266
|
-
--
|
|
265
|
+
--telegram Telegram-focused setup (wizard or non-interactive)
|
|
266
|
+
--non-interactive Suppress all prompts (for automation/SSM/cloud-init)
|
|
267
267
|
Requires TELEGRAM_BOT_TOKEN env var and --user
|
|
268
268
|
|
|
269
269
|
Required (or prompted in interactive --telegram):
|
package/src/cli/systemd.ts
CHANGED
|
@@ -127,6 +127,8 @@ WorkingDirectory=${home}
|
|
|
127
127
|
ExecStart=${opts.execStart}
|
|
128
128
|
Restart=on-failure
|
|
129
129
|
RestartSec=5
|
|
130
|
+
TimeoutStopSec=15
|
|
131
|
+
KillMode=mixed
|
|
130
132
|
EnvironmentFile=-${envFilePath}
|
|
131
133
|
Environment=ROUNDHOUSE_CONFIG=${CONFIG_PATH}
|
|
132
134
|
Environment=NODE_ENV=production
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/update.ts — Handle the /update command
|
|
3
|
+
*
|
|
4
|
+
* Transport-agnostic: receives a ProgressReporter interface,
|
|
5
|
+
* not a Telegram-specific thread object.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { execSync } from "node:child_process";
|
|
10
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { provisionBundle } from "../provisioning/bundle";
|
|
12
|
+
|
|
13
|
+
const GLOBAL_PI_EXTENSION_PACKAGES = [
|
|
14
|
+
"@inceptionstack/pi-hard-no",
|
|
15
|
+
"@inceptionstack/pi-branch-enforcer",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export interface UpdateProgress {
|
|
19
|
+
update(text: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface UpdateResult {
|
|
23
|
+
action: "already-latest" | "updated" | "error";
|
|
24
|
+
currentVersion: string;
|
|
25
|
+
latestVersion?: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check for updates, install if newer, provision bundle, patch settings.
|
|
31
|
+
* Returns the result — caller decides how to present it and whether to restart.
|
|
32
|
+
*/
|
|
33
|
+
export async function performUpdate(progress: UpdateProgress): Promise<UpdateResult> {
|
|
34
|
+
// Get current version
|
|
35
|
+
const pkg = await import("../../package.json", { with: { type: "json" } });
|
|
36
|
+
const currentVersion = pkg.default?.version ?? "unknown";
|
|
37
|
+
|
|
38
|
+
// Check latest version on npm
|
|
39
|
+
let latestVersion: string;
|
|
40
|
+
try {
|
|
41
|
+
latestVersion = execSync("npm view @inceptionstack/roundhouse version 2>/dev/null", {
|
|
42
|
+
timeout: 30_000,
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
}).trim();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// Update extensions anyway, but flag that version check failed
|
|
47
|
+
latestVersion = "";
|
|
48
|
+
console.warn("[roundhouse] npm view failed:", e instanceof Error ? e.message : e);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Always update extensions (even if roundhouse is already latest)
|
|
52
|
+
if (!latestVersion) {
|
|
53
|
+
await progress.update(`⚠️ Version check failed — updating extensions only`);
|
|
54
|
+
}
|
|
55
|
+
for (const extensionPackage of GLOBAL_PI_EXTENSION_PACKAGES) {
|
|
56
|
+
await progress.update(`📦 Updating extension: ${extensionPackage}...`);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
execSync(`npm install -g ${extensionPackage}@latest 2>&1`, {
|
|
60
|
+
timeout: 60_000,
|
|
61
|
+
encoding: "utf8",
|
|
62
|
+
});
|
|
63
|
+
await progress.update(`✅ ${extensionPackage} updated`);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
66
|
+
console.warn(`[roundhouse] failed to update extension ${extensionPackage}:`, msg);
|
|
67
|
+
await progress.update(`⚠️ Failed to update ${extensionPackage}: ${msg.slice(0, 150)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!latestVersion) {
|
|
72
|
+
return { action: "error", currentVersion, error: "Version check failed (extensions updated)" };
|
|
73
|
+
}
|
|
74
|
+
if (latestVersion === currentVersion) {
|
|
75
|
+
return { action: "already-latest", currentVersion };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await progress.update(`📦 Updating v${currentVersion} → v${latestVersion}...`);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
execSync("npm install -g @inceptionstack/roundhouse@latest 2>&1", {
|
|
82
|
+
timeout: 120_000,
|
|
83
|
+
encoding: "utf8",
|
|
84
|
+
});
|
|
85
|
+
} catch (e) {
|
|
86
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
87
|
+
console.warn("[roundhouse] self-update failed:", msg);
|
|
88
|
+
return { action: "error", currentVersion, error: `Self-update failed: ${msg}` };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Provision bundle (skills sync + CLI tools + config)
|
|
92
|
+
try {
|
|
93
|
+
provisionBundle();
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.warn("[roundhouse] bundle provisioning failed:", e instanceof Error ? e.message : e);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Ensure settings.json includes roundhouse package (for pre-bundle upgrades)
|
|
99
|
+
try {
|
|
100
|
+
const settingsPath = `${homedir()}/.pi/agent/settings.json`;
|
|
101
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
102
|
+
const selfPkg = "npm:@inceptionstack/roundhouse";
|
|
103
|
+
if (!Array.isArray(settings.packages)) settings.packages = [];
|
|
104
|
+
if (!settings.packages.includes(selfPkg)) {
|
|
105
|
+
settings.packages.push(selfPkg);
|
|
106
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
107
|
+
}
|
|
108
|
+
} catch { /* settings.json may not exist yet — fine, setup will create it */ }
|
|
109
|
+
|
|
110
|
+
return { action: "updated", currentVersion, latestVersion };
|
|
111
|
+
}
|
package/src/cron/runner.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getAgentFactory } from "../agents/registry";
|
|
9
|
-
|
|
9
|
+
// TODO: route through TransportAdapter.notify() when multi-transport lands
|
|
10
|
+
import { sendTelegramToMany } from "../transports/telegram/notify";
|
|
10
11
|
import { CronStore, generateRunId } from "./store";
|
|
11
12
|
import { buildTemplateContext, renderTemplate } from "./template";
|
|
12
13
|
import type { CronJobConfig, CronRunRecord } from "./types";
|
package/src/gateway/commands.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gateway/commands.ts —
|
|
2
|
+
* gateway/commands.ts — Chat command handlers
|
|
3
3
|
*
|
|
4
4
|
* Each handler is a standalone async function that receives a CommandContext.
|
|
5
5
|
* Extracted from Gateway.start() to reduce method size and enable unit testing.
|
|
@@ -9,7 +9,8 @@ import type { AgentAdapter, AgentStreamEvent, GatewayConfig } from "../types";
|
|
|
9
9
|
import { ROUNDHOUSE_VERSION } from "../config";
|
|
10
10
|
import { startTypingLoop } from "../util";
|
|
11
11
|
import { prepareMemoryForTurn, finalizeMemoryForTurn, flushMemoryThenCompact, determineMemoryMode } from "../memory/lifecycle";
|
|
12
|
-
|
|
12
|
+
// TODO: move progress into TransportAdapter when multi-transport lands
|
|
13
|
+
import { createProgressMessage } from "../transports/telegram/progress";
|
|
13
14
|
import { getSystemResources } from "./helpers";
|
|
14
15
|
|
|
15
16
|
// ── Types ────────────────────────────────────────────
|
|
@@ -70,10 +71,12 @@ export async function handleUpdate(ctx: CommandContext): Promise<void> {
|
|
|
70
71
|
console.log(`[roundhouse] /update requested by @${authorName} in thread=${thread.id}`);
|
|
71
72
|
const progress = await createProgressMessage(thread, "📦 Checking for updates...");
|
|
72
73
|
try {
|
|
73
|
-
const { performUpdate } = await import("../
|
|
74
|
+
const { performUpdate } = await import("../cli/update");
|
|
74
75
|
const result = await performUpdate(progress);
|
|
75
76
|
if (result.action === "already-latest") {
|
|
76
77
|
await progress.update(`✅ Already on latest (v${result.currentVersion})`);
|
|
78
|
+
} else if (result.action === "error") {
|
|
79
|
+
await progress.update(`⚠️ ${(result.error ?? "Update failed").slice(0, 200)}`);
|
|
77
80
|
} else if (result.action === "updated") {
|
|
78
81
|
await progress.update(`✅ Updated v${result.currentVersion} → v${result.latestVersion}. Restarting...`);
|
|
79
82
|
console.log(`[roundhouse] updated ${result.currentVersion} -> ${result.latestVersion}, restarting`);
|
|
@@ -160,15 +163,37 @@ export async function handleStatus(ctx: CommandContext): Promise<void> {
|
|
|
160
163
|
const nodeVer = process.version;
|
|
161
164
|
const memMB = (process.memoryUsage.rss() / 1024 / 1024).toFixed(1);
|
|
162
165
|
|
|
166
|
+
// Check for available update (async, non-blocking)
|
|
167
|
+
let updateAvailable = "";
|
|
168
|
+
try {
|
|
169
|
+
const { exec } = await import("node:child_process");
|
|
170
|
+
const { promisify } = await import("node:util");
|
|
171
|
+
const execAsync = promisify(exec);
|
|
172
|
+
const { stdout } = await execAsync("npm view @inceptionstack/roundhouse version 2>/dev/null", { timeout: 10_000 });
|
|
173
|
+
const latest = stdout.trim().split("\n").pop()!.trim();
|
|
174
|
+
if (latest && /^\d+\.\d+\.\d+$/.test(latest) && latest !== ROUNDHOUSE_VERSION) {
|
|
175
|
+
// Simple semver comparison: split and compare numerically
|
|
176
|
+
const [lM, lm, lp] = latest.split(".").map(Number);
|
|
177
|
+
const [cM, cm, cp] = ROUNDHOUSE_VERSION.split(".").map(Number);
|
|
178
|
+
if (lM > cM || (lM === cM && lm > cm) || (lM === cM && lm === cm && lp > cp)) {
|
|
179
|
+
updateAvailable = latest;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch { /* network unavailable — skip */ }
|
|
183
|
+
|
|
163
184
|
const info = agent.getInfo ? agent.getInfo(agentThreadId) : {};
|
|
164
185
|
const agentVersion = info.version ? `v${info.version}` : "";
|
|
165
186
|
const agentLabel = agentVersion ? `\`${agent.name}\` (${agentVersion})` : `\`${agent.name}\``;
|
|
166
187
|
|
|
188
|
+
const versionLine = updateAvailable
|
|
189
|
+
? `📦 Roundhouse: v${ROUNDHOUSE_VERSION} → ⬆️ v${updateAvailable} available (/update)`
|
|
190
|
+
: `📦 Roundhouse: v${ROUNDHOUSE_VERSION}`;
|
|
191
|
+
|
|
167
192
|
const lines = [
|
|
168
193
|
`📊 *Roundhouse Status*`,
|
|
169
194
|
``,
|
|
170
195
|
`🎫 Session: \`${agentThreadId}\``,
|
|
171
|
-
|
|
196
|
+
versionLine,
|
|
172
197
|
`🤖 Agent: ${agentLabel}`,
|
|
173
198
|
];
|
|
174
199
|
|