From 974d8e4b6d8a7d7c061fe1f59a8f0efdd99e1913 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 22:25:24 +0200 Subject: [PATCH] fix(sf): expose daemon as sf-server --- package-lock.json | 7 ++++-- package.json | 8 ++++-- packages/daemon/package.json | 5 ++-- packages/daemon/src/cli.ts | 18 +++++++------ packages/daemon/src/daemon.test.ts | 3 ++- packages/daemon/src/session-manager.test.ts | 11 ++++++++ scripts/ensure-workspace-builds.cjs | 1 + scripts/link-workspace-packages.cjs | 1 + scripts/validate-pack.js | 28 +++++++++++++++++++++ 9 files changed, 68 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf0fd0f1a..ecce7085d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,9 @@ }, "bin": { "sf": "dist/loader.js", - "sf-cli": "dist/loader.js" + "sf-cli": "dist/loader.js", + "sf-daemon": "packages/daemon/dist/cli.js", + "sf-server": "packages/daemon/dist/cli.js" }, "devDependencies": { "@biomejs/biome": "^2.4.13", @@ -16327,7 +16329,8 @@ "zod": "^3.24.0" }, "bin": { - "sf-daemon": "dist/cli.js" + "sf-daemon": "dist/cli.js", + "sf-server": "dist/cli.js" }, "devDependencies": { "@types/node": "^24.12.0", diff --git a/package.json b/package.json index 329f13cbc..3fb300cd0 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ ], "bin": { "sf": "dist/loader.js", - "sf-cli": "dist/loader.js" + "sf-cli": "dist/loader.js", + "sf-daemon": "packages/daemon/dist/cli.js", + "sf-server": "packages/daemon/dist/cli.js" }, "files": [ "dist", @@ -49,7 +51,8 @@ "build:rpc-client": "npm --workspace @singularity-forge/rpc-client run build", "build:pi": "npm run build:native-pkg && npm run build:pi-tui && npm run build:pi-ai && npm run build:pi-agent-core && npm run build:pi-coding-agent", "build:mcp-server": "npm --workspace @singularity-forge/mcp-server run build", - "build:core": "npm run build:pi && npm run build:rpc-client && npm run build:mcp-server && npm run check:versioned-json && tsc && npm run copy-resources && npm run copy-themes && npm run copy-export-html", + "build:daemon": "npm --workspace @singularity-forge/daemon run build", + "build:core": "npm run build:pi && npm run build:rpc-client && npm run build:daemon && npm run build:mcp-server && npm run check:versioned-json && tsc && npm run copy-resources && npm run copy-themes && npm run copy-export-html", "build": "npm run build:core && node scripts/build-web-if-stale.cjs", "stage:web-host": "node scripts/stage-web-standalone.cjs", "build:web-host": "npm --prefix web run build && npm run stage:web-host", @@ -78,6 +81,7 @@ "build:native:dev": "node rust-engine/scripts/build.js --dev", "dev": "node scripts/dev.js", "sf": "node scripts/dev-cli.js", + "sf:server": "node packages/daemon/dist/cli.js", "sf:web": "npm run build:pi && npm run copy-resources && node scripts/build-web-if-stale.cjs && node scripts/dev-cli.js --web", "sf:web:stop": "node scripts/dev-cli.js web stop", "sf:web:stop:all": "node scripts/dev-cli.js web stop all", diff --git a/packages/daemon/package.json b/packages/daemon/package.json index 9846d7740..347f62b65 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -21,11 +21,12 @@ } }, "bin": { - "sf-daemon": "./dist/cli.js" + "sf-daemon": "./dist/cli.js", + "sf-server": "./dist/cli.js" }, "scripts": { "build": "tsc", - "test": "node --test dist/daemon.test.js" + "test": "vitest run packages/daemon/src --root ../.. --config vitest.config.ts" }, "dependencies": { "@anthropic-ai/sdk": "^0.92.0", diff --git a/packages/daemon/src/cli.ts b/packages/daemon/src/cli.ts index c096fdc58..27bc19646 100644 --- a/packages/daemon/src/cli.ts +++ b/packages/daemon/src/cli.ts @@ -6,7 +6,11 @@ import { Logger } from './logger.js'; import { Daemon } from './daemon.js'; import { install, uninstall, status } from './launchd.js'; -const USAGE = `Usage: sf-daemon [options] +const COMMAND_NAME = 'sf-server'; + +const USAGE = `Usage: sf-server [options] + +Alias: sf-daemon Options: --config Path to YAML config file (default: ~/.sf/daemon.yaml) @@ -46,27 +50,27 @@ async function main(): Promise { scriptPath, configPath, }); - process.stdout.write('sf-daemon: launchd agent installed and loaded.\n'); + process.stdout.write(`${COMMAND_NAME}: launchd agent installed and loaded.\n`); process.exit(0); } if (values.uninstall) { uninstall(); - process.stdout.write('sf-daemon: launchd agent uninstalled.\n'); + process.stdout.write(`${COMMAND_NAME}: launchd agent uninstalled.\n`); process.exit(0); } if (values.status) { const result = status(); if (!result.registered) { - process.stdout.write('sf-daemon: not registered with launchd.\n'); + process.stdout.write(`${COMMAND_NAME}: not registered with launchd.\n`); } else if (result.pid != null) { process.stdout.write( - `sf-daemon: running (PID ${result.pid}, last exit status: ${result.lastExitStatus ?? 'n/a'})\n`, + `${COMMAND_NAME}: running (PID ${result.pid}, last exit status: ${result.lastExitStatus ?? 'n/a'})\n`, ); } else { process.stdout.write( - `sf-daemon: registered but not running (last exit status: ${result.lastExitStatus ?? 'n/a'})\n`, + `${COMMAND_NAME}: registered but not running (last exit status: ${result.lastExitStatus ?? 'n/a'})\n`, ); } process.exit(0); @@ -89,6 +93,6 @@ async function main(): Promise { main().catch((err: unknown) => { const msg = err instanceof Error ? err.message : String(err); - process.stderr.write(`sf-daemon: fatal: ${msg}\n`); + process.stderr.write(`${COMMAND_NAME}: fatal: ${msg}\n`); process.exit(1); }); diff --git a/packages/daemon/src/daemon.test.ts b/packages/daemon/src/daemon.test.ts index fdff6590c..5497b04a1 100644 --- a/packages/daemon/src/daemon.test.ts +++ b/packages/daemon/src/daemon.test.ts @@ -550,7 +550,8 @@ describe('CLI integration', () => { [resolveCliPath()!, '--help'], { encoding: 'utf-8', timeout: 5000 }, ); - assert.ok(result.includes('Usage: sf-daemon')); + assert.ok(result.includes('Usage: sf-server')); + assert.ok(result.includes('Alias: sf-daemon')); assert.ok(result.includes('--config')); assert.ok(result.includes('--verbose')); }); diff --git a/packages/daemon/src/session-manager.test.ts b/packages/daemon/src/session-manager.test.ts index b668aedce..e322855be 100644 --- a/packages/daemon/src/session-manager.test.ts +++ b/packages/daemon/src/session-manager.test.ts @@ -859,6 +859,17 @@ describe('SessionManager', () => { assert.equal(session.projectName, 'my-app'); }); + // ---- Default command starts autonomous mode ---- + + it('sends autonomous command when no command is provided', async () => { + const { manager } = createManager(); + + await manager.startSession({ projectDir: '/tmp/default-autonomous' }); + const client = manager.lastClient!; + + assert.ok(client.prompted.includes('/sf autonomous')); + }); + // ---- Custom command is sent instead of default ---- it('sends custom command when provided', async () => { diff --git a/scripts/ensure-workspace-builds.cjs b/scripts/ensure-workspace-builds.cjs index 60636feb6..ddb299519 100644 --- a/scripts/ensure-workspace-builds.cjs +++ b/scripts/ensure-workspace-builds.cjs @@ -98,6 +98,7 @@ if (require.main === module) { 'pi-agent-core', 'pi-coding-agent', 'rpc-client', + 'daemon', 'mcp-server', ] diff --git a/scripts/link-workspace-packages.cjs b/scripts/link-workspace-packages.cjs index 714d070c6..fb8406639 100644 --- a/scripts/link-workspace-packages.cjs +++ b/scripts/link-workspace-packages.cjs @@ -32,6 +32,7 @@ const packageDirs = [ 'pi-coding-agent', 'pi-tui', 'rpc-client', + 'daemon', 'mcp-server', ] diff --git a/scripts/validate-pack.js b/scripts/validate-pack.js index 147c801e4..0f53b4182 100644 --- a/scripts/validate-pack.js +++ b/scripts/validate-pack.js @@ -97,6 +97,7 @@ try { 'dist/loader.js', 'packages/pi-coding-agent/dist/index.js', 'packages/rpc-client/dist/index.js', + 'packages/daemon/dist/cli.js', 'packages/mcp-server/dist/cli.js', 'scripts/link-workspace-packages.cjs', 'dist/web/standalone/server.js', @@ -151,6 +152,7 @@ try { const criticalPackages = [ { scope: '@singularity-forge', name: 'pi-coding-agent' }, { scope: '@singularity-forge', name: 'rpc-client' }, + { scope: '@singularity-forge', name: 'daemon' }, ]; let resolutionFailed = false; for (const pkg of criticalPackages) { @@ -175,7 +177,13 @@ try { // --- Run the binary to confirm end-to-end resolution --- console.log('==> Running installed binary (sf -v)...'); const loaderPath = join(installedRoot, 'dist', 'loader.js'); + const daemonCliPath = join(installedRoot, 'packages', 'daemon', 'dist', 'cli.js'); const bundledWorkflowMcpCliPath = join(installedRoot, 'packages', 'mcp-server', 'dist', 'cli.js'); + if (!existsSync(daemonCliPath)) { + console.log('ERROR: Bundled daemon CLI missing after install.'); + console.log(` Expected: ${daemonCliPath}`); + process.exit(1); + } if (!existsSync(bundledWorkflowMcpCliPath)) { console.log('ERROR: Bundled workflow MCP CLI missing after install.'); console.log(` Expected: ${bundledWorkflowMcpCliPath}`); @@ -201,6 +209,26 @@ try { process.exit(1); } + console.log('==> Running installed daemon binary (sf-server --help)...'); + try { + const helpOutput = execFileSync(process.execPath, [daemonCliPath, '--help'], { + cwd: installDir, + encoding: 'utf8', + stdio: ['pipe', 'pipe', 'pipe'], + timeout: 15000, + maxBuffer: DEFAULT_MAX_BUFFER, + }); + if (!helpOutput.includes('Usage: sf-server')) { + console.log('ERROR: sf-server --help returned unexpected output.'); + process.exit(1); + } + } catch (err) { + console.log('ERROR: Running sf-server --help failed after install.'); + if (err.stdout) console.log(err.stdout); + if (err.stderr) console.log(err.stderr); + process.exit(1); + } + console.log(''); console.log('Package is installable. Safe to publish.'); process.exit(0);