diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1922b98e5..28a2739b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: command: pnpm lint - runtime: node task: test - command: pnpm test + command: node scripts/ci-sanitize-output.mjs pnpm test - runtime: node task: build command: pnpm build @@ -104,7 +104,7 @@ jobs: command: pnpm lint - runtime: node task: test - command: pnpm test + command: node scripts/ci-sanitize-output.mjs pnpm test - runtime: node task: build command: pnpm build diff --git a/CHANGELOG.md b/CHANGELOG.md index 938e39cc6..a019532ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - WhatsApp: group `/model list` output by provider for scannability. (#456) - thanks @mcinteerj - Hooks: allow per-hook model overrides for webhook/Gmail runs (e.g. GPT 5 Mini). - Control UI: logs tab opens at the newest entries (bottom). +- Control UI: default to relative paths for control UI assets. (#569) — thanks @bjesuiter - Control UI: add Docs link, remove chat composer divider, and add New session button. - Control UI: link sessions list to chat view. (#471) — thanks @HazAT - Sessions: support session `label` in store/list/UI and allow `sessions_send` lookup by label. (#570) — thanks @azade-c diff --git a/scripts/ci-sanitize-output.mjs b/scripts/ci-sanitize-output.mjs new file mode 100644 index 000000000..9c9b12012 --- /dev/null +++ b/scripts/ci-sanitize-output.mjs @@ -0,0 +1,37 @@ +import { spawn } from "node:child_process"; + +function sanitizeBuffer(input) { + const out = Buffer.allocUnsafe(input.length); + for (let i = 0; i < input.length; i++) { + const b = input[i]; + // Keep: tab/newline/carriage return + printable ASCII; replace everything else. + out[i] = b === 9 || b === 10 || b === 13 || (b >= 32 && b <= 126) ? b : 63; + } + return out; +} + +const [command, ...args] = process.argv.slice(2); +if (!command) { + process.stderr.write( + "Usage: node scripts/ci-sanitize-output.mjs [args...]\n", + ); + process.exit(2); +} + +const child = spawn(command, args, { + stdio: ["ignore", "pipe", "pipe"], + shell: process.platform === "win32", +}); + +child.stdout.on("data", (chunk) => { + process.stdout.write(sanitizeBuffer(Buffer.from(chunk))); +}); + +child.stderr.on("data", (chunk) => { + process.stderr.write(sanitizeBuffer(Buffer.from(chunk))); +}); + +child.on("exit", (code, signal) => { + if (signal) process.exit(1); + process.exit(code ?? 1); +}); diff --git a/src/auto-reply/reply.triggers.test.ts b/src/auto-reply/reply.triggers.test.ts index d5017a3fa..c4c67945a 100644 --- a/src/auto-reply/reply.triggers.test.ts +++ b/src/auto-reply/reply.triggers.test.ts @@ -53,13 +53,27 @@ vi.mock("../web/session.js", () => webMocks); async function withTempHome(fn: (home: string) => Promise): Promise { const base = await fs.mkdtemp(join(tmpdir(), "clawdbot-triggers-")); const previousHome = process.env.HOME; + const previousUserProfile = process.env.USERPROFILE; + const previousHomeDrive = process.env.HOMEDRIVE; + const previousHomePath = process.env.HOMEPATH; process.env.HOME = base; + if (process.platform === "win32") { + process.env.USERPROFILE = base; + const driveMatch = base.match(/^([A-Za-z]:)(.*)$/); + if (driveMatch) { + process.env.HOMEDRIVE = driveMatch[1]; + process.env.HOMEPATH = driveMatch[2] || "\\"; + } + } try { vi.mocked(runEmbeddedPiAgent).mockClear(); vi.mocked(abortEmbeddedPiRun).mockClear(); return await fn(base); } finally { process.env.HOME = previousHome; + process.env.USERPROFILE = previousUserProfile; + process.env.HOMEDRIVE = previousHomeDrive; + process.env.HOMEPATH = previousHomePath; await fs.rm(base, { recursive: true, force: true }); } } diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index b787c0faa..fd44cca22 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -582,6 +582,7 @@ export async function getReplyFromConfig( directives, effectiveModelDirective, cfg, + agentDir, sessionEntry, sessionStore, sessionKey, diff --git a/src/auto-reply/reply/directive-handling.ts b/src/auto-reply/reply/directive-handling.ts index b7e4eaaf8..0d7f5c585 100644 --- a/src/auto-reply/reply/directive-handling.ts +++ b/src/auto-reply/reply/directive-handling.ts @@ -976,6 +976,7 @@ export async function persistInlineDirectives(params: { directives: InlineDirectives; effectiveModelDirective?: string; cfg: ClawdbotConfig; + agentDir?: string; sessionEntry?: SessionEntry; sessionStore?: Record; sessionKey?: string; @@ -1009,6 +1010,7 @@ export async function persistInlineDirectives(params: { formatModelSwitchEvent, agentCfg, } = params; + const { agentDir } = params; let { provider, model } = params; const activeAgentId = sessionKey ? resolveAgentIdFromSessionKey(sessionKey) diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 30dbd25a3..c347c2b0e 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -14,7 +14,7 @@ function normalizeBase(input: string): string { export default defineConfig(({ command }) => { const envBase = process.env.CLAWDBOT_CONTROL_UI_BASE_PATH?.trim(); - const base = envBase ? normalizeBase(envBase) : "/"; + const base = envBase ? normalizeBase(envBase) : "./"; return { base, publicDir: path.resolve(here, "public"),