feat(factory): finish workbench milestone pass

This commit is contained in:
Nathan Flurry 2026-03-09 16:34:27 -07:00
parent bf282199b5
commit 49cba9e6c2
137 changed files with 819 additions and 338 deletions

View file

@ -1,5 +1,5 @@
{
"name": "@openhandoff/cli",
"name": "@sandbox-agent/factory-cli",
"version": "0.1.0",
"private": true,
"type": "module",
@ -16,8 +16,8 @@
"dependencies": {
"@iarna/toml": "^2.2.5",
"@opentui/core": "^0.1.77",
"@openhandoff/client": "workspace:*",
"@openhandoff/shared": "workspace:*",
"@sandbox-agent/factory-client": "workspace:*",
"@sandbox-agent/factory-shared": "workspace:*",
"zod": "^4.1.5"
},
"devDependencies": {

View file

@ -11,8 +11,8 @@ import {
import { homedir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { checkBackendHealth } from "@openhandoff/client";
import type { AppConfig } from "@openhandoff/shared";
import { checkBackendHealth } from "@sandbox-agent/factory-client";
import type { AppConfig } from "@sandbox-agent/factory-shared";
import { CLI_BUILD_ID } from "../build-id.js";
const HEALTH_TIMEOUT_MS = 1_500;
@ -39,10 +39,10 @@ function backendStateDir(): string {
const xdgDataHome = process.env.XDG_DATA_HOME?.trim();
if (xdgDataHome) {
return join(xdgDataHome, "openhandoff", "backend");
return join(xdgDataHome, "sandbox-agent-factory", "backend");
}
return join(homedir(), ".local", "share", "openhandoff", "backend");
return join(homedir(), ".local", "share", "sandbox-agent-factory", "backend");
}
function backendPidPath(host: string, port: number): string {
@ -214,7 +214,7 @@ function resolveLaunchSpec(host: string, port: number): LaunchSpec {
command: "pnpm",
args: [
"--filter",
"@openhandoff/backend",
"@sandbox-agent/factory-backend",
"exec",
"bun",
"src/index.ts",

View file

@ -2,14 +2,14 @@
import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { homedir } from "node:os";
import { AgentTypeSchema, CreateHandoffInputSchema, type HandoffRecord } from "@openhandoff/shared";
import { AgentTypeSchema, CreateHandoffInputSchema, type HandoffRecord } from "@sandbox-agent/factory-shared";
import {
readBackendMetadata,
createBackendClientFromConfig,
formatRelativeAge,
groupHandoffStatus,
summarizeHandoffs
} from "@openhandoff/client";
} from "@sandbox-agent/factory-client";
import {
ensureBackendRunning,
getBackendStatus,

View file

@ -3,7 +3,7 @@ import { homedir } from "node:os";
import { dirname, isAbsolute, join, resolve } from "node:path";
import { cwd } from "node:process";
import * as toml from "@iarna/toml";
import type { AppConfig } from "@openhandoff/shared";
import type { AppConfig } from "@sandbox-agent/factory-shared";
import opencodeThemePackJson from "./themes/opencode-pack.json" with { type: "json" };
export type ThemeMode = "dark" | "light";
@ -101,7 +101,7 @@ export function resolveTuiTheme(config: AppConfig, baseDir = cwd()): TuiThemeRes
return {
theme: candidate.theme,
name: candidate.name,
source: "openhandoff config",
source: "factory config",
mode
};
}

View file

@ -1,11 +1,11 @@
import type { AppConfig, HandoffRecord } from "@openhandoff/shared";
import type { AppConfig, HandoffRecord } from "@sandbox-agent/factory-shared";
import { spawnSync } from "node:child_process";
import {
createBackendClientFromConfig,
filterHandoffs,
formatRelativeAge,
groupHandoffStatus
} from "@openhandoff/client";
} from "@sandbox-agent/factory-client";
import { CLI_BUILD_ID } from "./build-id.js";
import { resolveTuiTheme, type TuiTheme } from "./theme.js";
@ -338,7 +338,7 @@ export async function runTui(config: AppConfig, workspaceId: string): Promise<vo
const client = createBackendClientFromConfig(config);
const renderer = await createCliRenderer({ exitOnCtrlC: false });
const text = new TextRenderable(renderer, {
id: "openhandoff-switch",
id: "factory-switch",
content: "Loading..."
});
text.fg = themeResolution.theme.text;

View file

@ -2,9 +2,9 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";
import { homedir } from "node:os";
import * as toml from "@iarna/toml";
import { ConfigSchema, resolveWorkspaceId, type AppConfig } from "@openhandoff/shared";
import { ConfigSchema, resolveWorkspaceId, type AppConfig } from "@sandbox-agent/factory-shared";
export const CONFIG_PATH = `${homedir()}/.config/openhandoff/config.toml`;
export const CONFIG_PATH = `${homedir()}/.config/sandbox-agent-factory/config.toml`;
export function loadConfig(path = CONFIG_PATH): AppConfig {
if (!existsSync(path)) {

View file

@ -20,7 +20,7 @@ vi.mock("node:child_process", async () => {
});
import { ensureBackendRunning, parseBackendPort } from "../src/backend/manager.js";
import { ConfigSchema, type AppConfig } from "@openhandoff/shared";
import { ConfigSchema, type AppConfig } from "@sandbox-agent/factory-shared";
function backendStateFile(baseDir: string, host: string, port: number, suffix: string): string {
const sanitized = host
@ -62,7 +62,7 @@ describe("backend manager", () => {
backend: {
host: "127.0.0.1",
port: 7741,
dbPath: "~/.local/share/openhandoff/handoff.db",
dbPath: "~/.local/share/sandbox-agent-factory/handoff.db",
opencode_poll_interval: 2,
github_poll_interval: 30,
backup_interval_secs: 3600,

View file

@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from "vitest";
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { ConfigSchema, type AppConfig } from "@openhandoff/shared";
import { ConfigSchema, type AppConfig } from "@sandbox-agent/factory-shared";
import { resolveTuiTheme } from "../src/theme.js";
function withEnv(key: string, value: string | undefined): void {
@ -25,7 +25,7 @@ describe("resolveTuiTheme", () => {
backend: {
host: "127.0.0.1",
port: 7741,
dbPath: "~/.local/share/openhandoff/handoff.db",
dbPath: "~/.local/share/sandbox-agent-factory/handoff.db",
opencode_poll_interval: 2,
github_poll_interval: 30,
backup_interval_secs: 3600,
@ -98,7 +98,7 @@ describe("resolveTuiTheme", () => {
expect(resolution.theme.background).toBe("#0a0a0a");
});
it("prefers explicit openhandoff theme override from config", () => {
it("prefers explicit factory theme override from config", () => {
tempDir = mkdtempSync(join(tmpdir(), "hf-theme-test-"));
withEnv("XDG_STATE_HOME", join(tempDir, "state"));
withEnv("XDG_CONFIG_HOME", join(tempDir, "config"));
@ -107,6 +107,6 @@ describe("resolveTuiTheme", () => {
const resolution = resolveTuiTheme(config, tempDir);
expect(resolution.name).toBe("opencode-default");
expect(resolution.source).toBe("openhandoff config");
expect(resolution.source).toBe("factory config");
});
});

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { HandoffRecord } from "@openhandoff/shared";
import { filterHandoffs, fuzzyMatch } from "@openhandoff/client";
import type { HandoffRecord } from "@sandbox-agent/factory-shared";
import { filterHandoffs, fuzzyMatch } from "@sandbox-agent/factory-client";
import { formatRows } from "../src/tui.js";
const sample: HandoffRecord = {

View file

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { ConfigSchema } from "@openhandoff/shared";
import { ConfigSchema } from "@sandbox-agent/factory-shared";
import { resolveWorkspace } from "../src/workspace/config.js";
describe("cli workspace resolution", () => {
@ -11,7 +11,7 @@ describe("cli workspace resolution", () => {
backend: {
host: "127.0.0.1",
port: 7741,
dbPath: "~/.local/share/openhandoff/handoff.db",
dbPath: "~/.local/share/sandbox-agent-factory/handoff.db",
opencode_poll_interval: 2,
github_poll_interval: 30,
backup_interval_secs: 3600,