chore: drop runner shim and add committer helper
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
READ ~/Projects/agent-scripts/AGENTS.MD BEFORE ANYTHING (skip if missing).
|
||||
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
@@ -25,6 +27,7 @@
|
||||
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
|
||||
- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
|
||||
- Group related changes; avoid bundling unrelated refactors.
|
||||
- PRs should summarize scope, note testing performed, and mention any user-facing changes or new flags.
|
||||
|
||||
@@ -45,7 +45,7 @@ First Clawdis release after the Warelay rebrand. This is a semver-major because
|
||||
- Removed Twilio support and all related commands/options (webhook/up/provider flags/wait-poll); CLAWDIS is Baileys Web-only.
|
||||
|
||||
### Changes
|
||||
- Default agent handling now favors Pi RPC while falling back to the plain command runner for non-Pi invocations, keeping heartbeat/session plumbing intact.
|
||||
- Default agent handling now favors Pi RPC while falling back to plain command execution for non-Pi invocations, keeping heartbeat/session plumbing intact.
|
||||
- Documentation updated to reflect Pi-only support and to mark legacy Claude paths as historical.
|
||||
- Status command reports web session health + session recipients; config paths are locked to `~/.clawdis` with session metadata stored under `~/.clawdis/sessions/`.
|
||||
- Simplified send/agent/relay/heartbeat to web-only delivery; removed Twilio mocks/tests and dead code.
|
||||
|
||||
@@ -30,7 +30,7 @@ swift run swabble transcribe /path/to/audio.m4a --format srt --output out.srt
|
||||
```
|
||||
|
||||
## Use as a library
|
||||
Add swabble as a SwiftPM dependency and import the `Swabble` product to reuse the Speech pipeline, config loader, hook runner, and transcript store in your own app:
|
||||
Add swabble as a SwiftPM dependency and import the `Swabble` product to reuse the Speech pipeline, config loader, hook executor, and transcript store in your own app:
|
||||
|
||||
```swift
|
||||
// Package.swift
|
||||
|
||||
@@ -10,7 +10,7 @@ public struct HookJob: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
public actor HookRunner {
|
||||
public actor HookExecutor {
|
||||
private let config: SwabbleConfig
|
||||
private var lastRun: Date?
|
||||
private let hostname: String
|
||||
@@ -46,8 +46,8 @@ struct ServeCommand: ParsableCommand {
|
||||
}
|
||||
let stripped = Self.stripWake(text: seg.text, cfg: cfg)
|
||||
let job = HookJob(text: stripped, timestamp: Date())
|
||||
let runner = HookRunner(config: cfg)
|
||||
try await runner.run(job: job)
|
||||
let executor = HookExecutor(config: cfg)
|
||||
try await executor.run(job: job)
|
||||
if cfg.transcripts.enabled {
|
||||
await TranscriptsStore.shared.append(text: stripped)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ struct TestHookCommand: ParsableCommand {
|
||||
|
||||
mutating func run() async throws {
|
||||
let cfg = try ConfigLoader.load(at: configURL)
|
||||
let runner = HookRunner(config: cfg)
|
||||
try await runner.run(job: HookJob(text: text, timestamp: Date()))
|
||||
let executor = HookExecutor(config: cfg)
|
||||
try await executor.run(job: HookJob(text: text, timestamp: Date()))
|
||||
print("hook invoked")
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Goal: brabble-style always-on voice hook for macOS 26 using Apple Speech.framewo
|
||||
- **Config**: `SwabbleConfig` Codable. Fields: audio device name/index, wake (enabled/word/aliases/sensitivity placeholder), hook (command/args/prefix/cooldown/min_chars/timeout/env), logging (level, format), transcripts (enabled, max kept), speech (locale, enableEtiquetteReplacements flag). Stored JSON; default written by `setup`.
|
||||
- **Audio + Speech pipeline**: `SpeechPipeline` wraps `AVAudioEngine` input → `SpeechAnalyzer` with `SpeechTranscriber` module. Emits partial/final transcripts via async stream. Requests `.audioTimeRange` when transcripts enabled. Handles Speech permission and asset download prompts ahead of capture.
|
||||
- **Wake gate**: text-based keyword match against latest partial/final; strips wake term before hook dispatch. `--no-wake` disables.
|
||||
- **Hook runner**: async `HookRunner` spawns `Process` with configured args, prefix substitution `${hostname}`. Enforces cooldown + timeout; injects env `SWABBLE_TEXT`, `SWABBLE_PREFIX` plus user env map.
|
||||
- **Hook executor**: async `HookExecutor` spawns `Process` with configured args, prefix substitution `${hostname}`. Enforces cooldown + timeout; injects env `SWABBLE_TEXT`, `SWABBLE_PREFIX` plus user env map.
|
||||
- **Transcripts store**: in-memory ring buffer; optional persisted JSON lines under `~/Library/Application Support/swabble/transcripts.log`.
|
||||
- **Logging**: simple structured logger to stderr; respects log level.
|
||||
|
||||
|
||||
@@ -466,7 +466,7 @@ extension GeneralSettings {
|
||||
}
|
||||
|
||||
// Step 1: basic SSH reachability check
|
||||
let sshResult = await ShellRunner.run(
|
||||
let sshResult = await ShellExecutor.run(
|
||||
command: Self.sshCheckCommand(target: settings.target, identity: settings.identity),
|
||||
cwd: nil,
|
||||
env: nil,
|
||||
|
||||
@@ -129,7 +129,7 @@ enum RelayEnvironment {
|
||||
let cmd = [pnpm, "add", "-g", "clawdis@\(target)"]
|
||||
|
||||
statusHandler("Installing clawdis@\(target) via pnpm…")
|
||||
let response = await ShellRunner.run(command: cmd, cwd: nil, env: ["PATH": preferred], timeout: 300)
|
||||
let response = await ShellExecutor.run(command: cmd, cwd: nil, env: ["PATH": preferred], timeout: 300)
|
||||
if response.ok {
|
||||
statusHandler("Installed clawdis@\(target)")
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ClawdisIPC
|
||||
import Foundation
|
||||
|
||||
enum ShellRunner {
|
||||
enum ShellExecutor {
|
||||
static func run(command: [String], cwd: String?, env: [String: String]?, timeout: Double?) async -> Response {
|
||||
guard !command.isEmpty else { return Response(ok: false, message: "empty command") }
|
||||
|
||||
@@ -70,7 +70,7 @@ final class ClawdisXPCService: NSObject, ClawdisXPCProtocol {
|
||||
.ensure([.screenRecording], interactive: false)[.screenRecording] ?? false
|
||||
guard authorized else { return Response(ok: false, message: "screen recording permission missing") }
|
||||
}
|
||||
return await ShellRunner.run(command: command, cwd: cwd, env: env, timeout: timeoutSec)
|
||||
return await ShellExecutor.run(command: command, cwd: cwd, env: env, timeout: timeoutSec)
|
||||
|
||||
case let .agent(message, thinking, session, deliver, to):
|
||||
let trimmed = message.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- **mcporter** — MCP runtime/CLI to list, call, and sync Model Context Protocol servers.
|
||||
- **Peekaboo** — Fast macOS screenshots with optional AI vision analysis.
|
||||
- **camsnap** — Capture frames, clips, or motion alerts from RTSP/ONVIF security cams.
|
||||
- **oracle** — OpenAI-ready agent runner with session replay and browser control.
|
||||
- **oracle** — OpenAI-ready agent CLI with session replay and browser control.
|
||||
- **eightctl** — Control Eight Sleep Pod temperature, alarms, schedules, and metrics.
|
||||
- **imsg** — macOS Messages CLI to read/tail chats and send iMessage/SMS.
|
||||
- **spotify-player** — Terminal Spotify client to search/queue/control playback.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Plan: `clawdis agent` (direct-to-agent invocation)
|
||||
|
||||
Goal: Add a CLI subcommand that talks directly to the configured agent/command runner (no WhatsApp send), while reusing the same session handling and config clawdis already uses for auto-replies.
|
||||
Goal: Add a CLI subcommand that talks directly to the configured agent command (no WhatsApp send), while reusing the same session handling and config clawdis already uses for auto-replies.
|
||||
|
||||
## Why
|
||||
- Sometimes we want to poke the agent directly (same prompt templates/sessions) without sending a WhatsApp message.
|
||||
|
||||
@@ -15,7 +15,7 @@ Last updated: 2025-12-09
|
||||
- **Clients (mac app / CLI / web admin)**
|
||||
- One WS connection per client.
|
||||
- Send requests (`health`, `status`, `send`, `agent`, `system-presence`, toggles) and subscribe to events (`tick`, `agent`, `presence`, `shutdown`).
|
||||
- **Agent runner (Tau/Pi process)**
|
||||
- **Agent process (Tau/Pi)**
|
||||
- Spawned by the Gateway on demand for `agent` calls; streams events back over the same WS connection.
|
||||
- **WebChat**
|
||||
- Serves static assets locally.
|
||||
|
||||
@@ -53,7 +53,7 @@ struct Response { ok: Bool; message?: String; payload?: Data }
|
||||
- NotificationManager: UNUserNotificationCenter primary; AppleScript `display notification` fallback; respects the `--sound` value on each request.
|
||||
- PermissionManager: checks/requests Notifications, Accessibility (AX), Screen Recording (capture probe); publishes changes for UI.
|
||||
- ScreenCaptureManager: window/display PNG capture; gated on permission.
|
||||
- ShellRunner: executes `Process` with timeout; rejects when `needsScreenRecording` and permission missing; returns stdout/stderr in payload.
|
||||
- ShellExecutor: executes `Process` with timeout; rejects when `needsScreenRecording` and permission missing; returns stdout/stderr in payload.
|
||||
- XPCListener actor: routes Request → managers; logs via OSLog.
|
||||
|
||||
## CLI (`clawdis-mac`)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Updated: 2025-12-07
|
||||
|
||||
# Why grammY
|
||||
- TS-first Bot API client with built-in long-poll + webhook runners, middleware, error handling, rate limiter.
|
||||
- TS-first Bot API client with built-in long-poll + webhook helpers, middleware, error handling, rate limiter.
|
||||
- Cleaner media helpers than hand-rolling fetch + FormData; supports all Bot API methods.
|
||||
- Extensible: proxy support via custom fetch, session middleware (optional), type-safe context.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ How to see whether the WhatsApp Web/Baileys bridge is healthy from the menu bar
|
||||
- Uses a cached snapshot so the UI loads instantly and falls back gracefully when offline.
|
||||
|
||||
## How the probe works
|
||||
- App runs `clawdis health --json` via `ShellRunner` every ~60s and on demand. The probe loads creds, attempts a short Baileys connect, and reports status without sending messages.
|
||||
- App runs `clawdis health --json` via `ShellExecutor` every ~60s and on demand. The probe loads creds, attempts a short Baileys connect, and reports status without sending messages.
|
||||
- Cache the last good snapshot and the last error separately to avoid flicker; show the timestamp of each.
|
||||
|
||||
## When in doubt
|
||||
|
||||
@@ -66,7 +66,7 @@ LOG CATEGORIES (examples):
|
||||
• xpc - XPC service calls
|
||||
• notifications - Notification helper
|
||||
• screenshot - Screenshotter
|
||||
• shell - ShellRunner
|
||||
• shell - ShellExecutor
|
||||
|
||||
QUICK START:
|
||||
vtlog -n 100 Show last 100 lines from all components
|
||||
|
||||
107
scripts/committer
Executable file
107
scripts/committer
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
# Disable glob expansion to handle brackets in file paths
|
||||
set -f
|
||||
usage() {
|
||||
printf 'Usage: %s [--force] "commit message" "file" ["file" ...]\n' "$(basename "$0")" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
force_delete_lock=false
|
||||
if [ "${1:-}" = "--force" ]; then
|
||||
force_delete_lock=true
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
commit_message=$1
|
||||
shift
|
||||
|
||||
if [[ "$commit_message" != *[![:space:]]* ]]; then
|
||||
printf 'Error: commit message must not be empty\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "$commit_message" ]; then
|
||||
printf 'Error: first argument looks like a file path ("%s"); provide the commit message first\n' "$commit_message" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
files=("$@")
|
||||
|
||||
# Disallow "." because it stages the entire repository and defeats the helper's safety guardrails.
|
||||
for file in "${files[@]}"; do
|
||||
if [ "$file" = "." ]; then
|
||||
printf 'Error: "." is not allowed; list specific paths instead\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
last_commit_error=''
|
||||
|
||||
run_git_commit() {
|
||||
local stderr_log
|
||||
stderr_log=$(mktemp)
|
||||
if git commit -m "$commit_message" -- "${files[@]}" 2> >(tee "$stderr_log" >&2); then
|
||||
rm -f "$stderr_log"
|
||||
last_commit_error=''
|
||||
return 0
|
||||
fi
|
||||
|
||||
last_commit_error=$(cat "$stderr_log")
|
||||
rm -f "$stderr_log"
|
||||
return 1
|
||||
}
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
if [ ! -e "$file" ]; then
|
||||
if ! git ls-files --error-unmatch -- "$file" >/dev/null 2>&1; then
|
||||
printf 'Error: file not found: %s\n' "$file" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
git restore --staged :/
|
||||
git add --force -- "${files[@]}"
|
||||
|
||||
if git diff --staged --quiet; then
|
||||
printf 'Warning: no staged changes detected for: %s\n' "${files[*]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
committed=false
|
||||
if run_git_commit; then
|
||||
committed=true
|
||||
elif [ "$force_delete_lock" = true ]; then
|
||||
lock_path=$(
|
||||
printf '%s\n' "$last_commit_error" |
|
||||
awk -F"'" '/Unable to create .*\.git\/index\.lock/ { print $2; exit }'
|
||||
)
|
||||
|
||||
if [ -n "$lock_path" ] && [ -e "$lock_path" ]; then
|
||||
rm -f "$lock_path"
|
||||
printf 'Removed stale git lock: %s\n' "$lock_path" >&2
|
||||
if run_git_commit; then
|
||||
committed=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$committed" = false ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'Committed "%s" with %d files\n' "$commit_message" "${#files[@]}"
|
||||
@@ -62,7 +62,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
thinkLevel: "medium",
|
||||
});
|
||||
@@ -100,7 +99,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -142,7 +140,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -183,7 +180,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -240,7 +236,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
onAgentEvent: (evt) => events.push(evt),
|
||||
});
|
||||
@@ -281,7 +276,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: true,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -311,7 +305,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 10,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -344,7 +337,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -379,7 +371,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -411,7 +402,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
onPartialReply: onPartial,
|
||||
verboseLevel: "off",
|
||||
@@ -445,7 +435,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 1000,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
@@ -475,7 +464,6 @@ describe("runCommandReply (pi)", () => {
|
||||
systemSent: false,
|
||||
timeoutMs: 100,
|
||||
timeoutSeconds: 1,
|
||||
commandRunner: vi.fn(),
|
||||
enqueue: enqueueImmediate,
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import { logError } from "../logger.js";
|
||||
import { getChildLogger } from "../logging.js";
|
||||
import { splitMediaFromOutput } from "../media/parse.js";
|
||||
import { enqueueCommand } from "../process/command-queue.js";
|
||||
import type { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { runPiRpc } from "../process/tau-rpc.js";
|
||||
import { applyTemplate, type TemplateContext } from "./templating.js";
|
||||
import {
|
||||
@@ -146,7 +145,7 @@ type CommandReplyConfig = NonNullable<WarelayConfig["inbound"]>["reply"] & {
|
||||
mode: "command";
|
||||
};
|
||||
|
||||
type EnqueueRunner = typeof enqueueCommand;
|
||||
type EnqueueCommandFn = typeof enqueueCommand;
|
||||
|
||||
type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high";
|
||||
|
||||
@@ -159,8 +158,7 @@ type CommandReplyParams = {
|
||||
systemSent: boolean;
|
||||
timeoutMs: number;
|
||||
timeoutSeconds: number;
|
||||
commandRunner: typeof runCommandWithTimeout;
|
||||
enqueue?: EnqueueRunner;
|
||||
enqueue?: EnqueueCommandFn;
|
||||
thinkLevel?: ThinkLevel;
|
||||
verboseLevel?: "off" | "on";
|
||||
onPartialReply?: (payload: ReplyPayload) => Promise<void> | void;
|
||||
@@ -347,7 +345,6 @@ export async function runCommandReply(
|
||||
systemSent,
|
||||
timeoutMs,
|
||||
timeoutSeconds,
|
||||
commandRunner: _commandRunner,
|
||||
enqueue = enqueueCommand,
|
||||
thinkLevel,
|
||||
verboseLevel,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import * as tauRpc from "../process/tau-rpc.js";
|
||||
import * as commandReply from "./command-reply.js";
|
||||
import { getReplyFromConfig } from "./reply.js";
|
||||
|
||||
const webMocks = vi.hoisted(() => ({
|
||||
@@ -27,7 +28,7 @@ afterEach(() => {
|
||||
|
||||
describe("trigger handling", () => {
|
||||
it("aborts even with timestamp prefix", async () => {
|
||||
const runner = vi.fn();
|
||||
const commandSpy = vi.spyOn(commandReply, "runCommandReply");
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "[Dec 5 10:00] stop",
|
||||
@@ -36,15 +37,14 @@ describe("trigger handling", () => {
|
||||
},
|
||||
{},
|
||||
baseCfg,
|
||||
runner,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toBe("⚙️ Agent was aborted.");
|
||||
expect(runner).not.toHaveBeenCalled();
|
||||
expect(commandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("restarts even with prefix/whitespace", async () => {
|
||||
const runner = vi.fn();
|
||||
const commandSpy = vi.spyOn(commandReply, "runCommandReply");
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: " [Dec 5] /restart",
|
||||
@@ -53,15 +53,14 @@ describe("trigger handling", () => {
|
||||
},
|
||||
{},
|
||||
baseCfg,
|
||||
runner,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text?.startsWith("⚙️ Restarting" ?? "")).toBe(true);
|
||||
expect(runner).not.toHaveBeenCalled();
|
||||
expect(commandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports status without invoking the agent", async () => {
|
||||
const runner = vi.fn();
|
||||
const commandSpy = vi.spyOn(commandReply, "runCommandReply");
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "/status",
|
||||
@@ -70,11 +69,10 @@ describe("trigger handling", () => {
|
||||
},
|
||||
{},
|
||||
baseCfg,
|
||||
runner,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Status");
|
||||
expect(runner).not.toHaveBeenCalled();
|
||||
expect(commandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores think directives that only appear in the context wrapper", async () => {
|
||||
|
||||
@@ -17,7 +17,6 @@ import { isVerbose, logVerbose } from "../globals.js";
|
||||
import { buildProviderSummary } from "../infra/provider-summary.js";
|
||||
import { triggerWarelayRestart } from "../infra/restart.js";
|
||||
import { drainSystemEvents } from "../infra/system-events.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { resolveHeartbeatSeconds } from "../web/reconnect.js";
|
||||
import { getWebAuthAgeMs, webAuthExists } from "../web/session.js";
|
||||
@@ -163,7 +162,6 @@ export async function getReplyFromConfig(
|
||||
ctx: MsgContext,
|
||||
opts?: GetReplyOptions,
|
||||
configOverride?: WarelayConfig,
|
||||
commandRunner: typeof runCommandWithTimeout = runCommandWithTimeout,
|
||||
): Promise<ReplyPayload | ReplyPayload[] | undefined> {
|
||||
// Choose reply from config: static text or external command stdout.
|
||||
const cfg = configOverride ?? loadConfig();
|
||||
@@ -737,7 +735,6 @@ export async function getReplyFromConfig(
|
||||
systemSent,
|
||||
timeoutMs,
|
||||
timeoutSeconds,
|
||||
commandRunner,
|
||||
thinkLevel: resolvedThinkLevel,
|
||||
verboseLevel: resolvedVerboseLevel,
|
||||
onPartialReply: opts?.onPartialReply,
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
saveSessionStore,
|
||||
} from "../config/sessions.js";
|
||||
import { emitAgentEvent } from "../infra/agent-events.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { normalizeE164 } from "../utils.js";
|
||||
|
||||
@@ -319,7 +318,6 @@ export async function agentCommand(
|
||||
systemSent,
|
||||
timeoutMs,
|
||||
timeoutSeconds,
|
||||
commandRunner: runCommandWithTimeout,
|
||||
thinkLevel: resolvedThinkLevel,
|
||||
verboseLevel: resolvedVerboseLevel,
|
||||
runId: sessionId,
|
||||
|
||||
@@ -101,7 +101,7 @@ describe("web inbound media saves with extension", () => {
|
||||
|
||||
realSock.ev.emit("messages.upsert", upsert);
|
||||
|
||||
// Allow a brief window for the async handler to fire on slower runners.
|
||||
// Allow a brief window for the async handler to fire on slower hosts.
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (onMessage.mock.calls.length > 0) break;
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
|
||||
Reference in New Issue
Block a user