From 67cb2fa5fca287c483d7d98161970bbd397b3a9a Mon Sep 17 00:00:00 2001 From: ejclaw Date: Wed, 8 Apr 2026 07:38:06 +0900 Subject: [PATCH] Prune legacy setup service scaffolding --- .claude/skills/setup/SKILL.md | 58 +++----- .mcp.json | 3 - config-examples/mount-allowlist.json | 25 ---- groups-codex/nanoclaw/AGENTS.md | 13 -- launchd/com.ejclaw-codex.plist | 38 ----- setup/legacy-service-guard.test.ts | 123 ---------------- setup/legacy-service-guard.ts | 209 --------------------------- setup/restart-stack.test.ts | 25 ---- setup/restart-stack.ts | 26 ---- setup/service-defs.ts | 30 +--- setup/service.test.ts | 19 +-- setup/service.ts | 35 ----- setup/verify.ts | 37 +---- 13 files changed, 22 insertions(+), 619 deletions(-) delete mode 100644 .mcp.json delete mode 100644 config-examples/mount-allowlist.json delete mode 100644 groups-codex/nanoclaw/AGENTS.md delete mode 100644 launchd/com.ejclaw-codex.plist delete mode 100644 setup/legacy-service-guard.test.ts delete mode 100644 setup/legacy-service-guard.ts diff --git a/.claude/skills/setup/SKILL.md b/.claude/skills/setup/SKILL.md index 61d3ab4..f150c38 100644 --- a/.claude/skills/setup/SKILL.md +++ b/.claude/skills/setup/SKILL.md @@ -1,15 +1,16 @@ --- name: setup -description: Run initial EJClaw setup for the dual-service Discord architecture. +description: Run initial EJClaw setup for the unified single-service Discord architecture. --- # EJClaw Setup 설치는 `bash setup.sh`로 부트스트랩하고, 나머지는 `bun run setup -- --step `으로 진행합니다. 현재 기준 채널은 디스코드만 지원합니다. -EJClaw는 두 개의 서비스로 구성됩니다: -- **ejclaw** — Claude Code 봇 (`@claude`) -- **ejclaw-codex** — Codex 봇 (`@codex`) — `.env.codex` 파일이 있으면 자동 설치 +EJClaw는 현재 **단일 서비스**로 동작합니다: +- **ejclaw** — 통합 런타임 서비스 + +Claude가 기본 봇으로 동작하고, Codex는 별도 서비스가 아니라 필요 시 내부 러너/역할 라우팅으로 사용됩니다. ## 1. 부트스트랩 @@ -34,7 +35,7 @@ bun run setup -- --step environment ## 3. 필수 환경 변수 -### Claude 서비스 (.env) +### 기본 환경 변수 (.env) `.env`에 최소한 아래 값이 있어야 합니다. @@ -51,18 +52,10 @@ CLAUDE_CODE_OAUTH_TOKENS=token1,token2 # 다중 계정 자동 로테이션 GROQ_API_KEY=... # Discord 음성 전사 (Groq Whisper) ``` -### Codex 서비스 (.env.codex) - -Codex 봇을 함께 운영하려면 `.env.codex`를 만듭니다. 이 파일이 있으면 `--step service`가 자동으로 `ejclaw-codex` 서비스도 설치합니다. +Codex 관련 선택 설정은 **별도 서비스 파일이 아니라 같은 `.env` 또는 서비스 환경 변수**에 넣습니다: ```bash -DISCORD_BOT_TOKEN=... # Codex 봇 토큰 (Claude와 별도) -``` - -Codex 서비스의 추가 설정은 systemd 유닛에서 `Environment=` 라인으로 지정하거나 `.env.codex`에 추가합니다: - -```bash -# systemd 유닛 또는 .env.codex에 추가 가능 +# 같은 .env 또는 서비스 Environment=에 추가 가능 CODEX_MODEL=gpt-5.4 CODEX_EFFORT=xhigh OPENAI_API_KEY=... @@ -91,15 +84,17 @@ bun run setup -- --step runners - `runners/agent-runner` (Claude Code) - `runners/codex-runner` (Codex) +서비스는 하나지만, 내부 역할 라우팅과 paired 흐름에서 두 러너를 모두 쓸 수 있어서 둘 다 빌드합니다. + 실패하면 보통 `bun run build:runners` 출력과 각 러너의 `package.json` 의존성을 같이 보면 됩니다. ## 5. 디스코드 채널 등록 먼저 디스코드에서 개발자 모드를 켜고 채널 ID를 복사합니다. 등록 JID는 `dc:` 형식입니다. -듀얼 서비스에서는 **같은 채널을 두 번 등록**할 수 있습니다 — 각 agent type에 한 번씩. 등록은 `(jid, agent_type)` 복합 키로 저장됩니다. +채널 등록은 기본적으로 한 번 하면 됩니다. -Claude 봇 채널 등록: +예시: ```bash bun run setup -- --step register -- \ @@ -111,18 +106,6 @@ bun run setup -- --step register -- \ --no-trigger-required ``` -Codex 봇도 같은 채널을 쓴다면 별도로 등록합니다: - -```bash -ASSISTANT_NAME=codex bun run setup -- --step register -- \ - --jid dc:123456789012345678 \ - --name "My Server #general" \ - --folder discord_main \ - --trigger @codex \ - --is-main \ - --no-trigger-required -``` - 보조 채널 예시: ```bash @@ -140,24 +123,22 @@ bun run setup -- --step service ``` 이 명령은: -- **ejclaw** 서비스를 항상 설치합니다 -- `.env.codex`가 있으면 **ejclaw-codex** 서비스도 함께 설치합니다 +- **ejclaw** 서비스를 설치하고 시작합니다 플랫폼별: -- Linux (systemd): `~/.config/systemd/user/ejclaw.service` + `ejclaw-codex.service` -- macOS: `~/Library/LaunchAgents/com.ejclaw.plist` + `com.ejclaw-codex.plist` -- WSL (no systemd): `start-ejclaw.sh` + `start-ejclaw-codex.sh` +- Linux (systemd): `~/.config/systemd/user/ejclaw.service` +- macOS: `~/Library/LaunchAgents/com.ejclaw.plist` +- WSL (no systemd): `start-ejclaw.sh` 수동으로 서비스 관리: ```bash # Linux (systemd) -systemctl --user status ejclaw ejclaw-codex -systemctl --user restart ejclaw ejclaw-codex +systemctl --user status ejclaw +systemctl --user restart ejclaw # 로그 journalctl --user -u ejclaw -f -journalctl --user -u ejclaw-codex -f ``` ## 7. 최종 검증 @@ -169,7 +150,6 @@ bun run setup -- --step verify 성공 기준: - **ejclaw** 서비스가 running -- **ejclaw-codex** 서비스가 running (`.env.codex`가 있을 때) - Claude 인증이 configured - `CHANNEL_AUTH`에 `discord` - 등록 그룹 수가 1 이상 @@ -178,6 +158,6 @@ bun run setup -- --step verify - 빌드 문제: `bun run typecheck`, `bun test`, `bun run build:runners` - Claude 서비스 문제: `logs/ejclaw.error.log` 또는 `journalctl --user -u ejclaw -f` -- Codex 서비스 문제: `logs/ejclaw-codex.error.log` 또는 `journalctl --user -u ejclaw-codex -f` +- Codex 실행 문제: 같은 `ejclaw` 서비스 로그에서 `CODEX_MODEL`, `CODEX_EFFORT`, `OPENAI_API_KEY` 설정을 같이 확인 - 디스코드 연결 문제: `.env`의 `DISCORD_BOT_TOKEN`과 등록된 `dc:*` JID 확인 - 응답 문제: `tail -f logs/ejclaw.log` diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index da39e4f..0000000 --- a/.mcp.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "mcpServers": {} -} diff --git a/config-examples/mount-allowlist.json b/config-examples/mount-allowlist.json deleted file mode 100644 index e914883..0000000 --- a/config-examples/mount-allowlist.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "allowedRoots": [ - { - "path": "~/projects", - "allowReadWrite": true, - "description": "Development projects" - }, - { - "path": "~/repos", - "allowReadWrite": true, - "description": "Git repositories" - }, - { - "path": "~/Documents/work", - "allowReadWrite": false, - "description": "Work documents (read-only)" - } - ], - "blockedPatterns": [ - "password", - "secret", - "token" - ], - "nonMainReadOnly": true -} diff --git a/groups-codex/nanoclaw/AGENTS.md b/groups-codex/nanoclaw/AGENTS.md deleted file mode 100644 index a1664d9..0000000 --- a/groups-codex/nanoclaw/AGENTS.md +++ /dev/null @@ -1,13 +0,0 @@ -# Codex Assistant - -You are a participant in a Discord chat. Your name is "코덱스" (Codex). - -This channel works with the nanoclaw project at /home/clone-ej/nanoclaw. - -## Core Rules - -- Respond directly to messages. Do NOT give "reply suggestions" or "here's how you could respond". -- When someone asks a question, answer it yourself. You are having a conversation, not helping someone draft replies. -- Speak in natural, friendly Korean. -- When coding, debugging, or file work is needed, do it directly. -- When you reach a conclusion or need human judgment, mention @눈쟁이 in your message. diff --git a/launchd/com.ejclaw-codex.plist b/launchd/com.ejclaw-codex.plist deleted file mode 100644 index 844107f..0000000 --- a/launchd/com.ejclaw-codex.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - Label - com.ejclaw-codex - ProgramArguments - - {{NODE_PATH}} - {{PROJECT_ROOT}}/dist/index.js - - WorkingDirectory - {{PROJECT_ROOT}} - RunAtLoad - - KeepAlive - - EnvironmentVariables - - PATH - {{HOME}}/.npm-global/bin:{{HOME}}/.local/bin:/usr/local/bin:/usr/bin:/bin - HOME - {{HOME}} - ASSISTANT_NAME - Andy-Codex - EJCLAW_STORE_DIR - {{PROJECT_ROOT}}/store-codex - EJCLAW_GROUPS_DIR - {{PROJECT_ROOT}}/groups-codex - EJCLAW_DATA_DIR - {{PROJECT_ROOT}}/data-codex - - StandardOutPath - {{PROJECT_ROOT}}/logs/ejclaw-codex.log - StandardErrorPath - {{PROJECT_ROOT}}/logs/ejclaw-codex.error.log - - diff --git a/setup/legacy-service-guard.test.ts b/setup/legacy-service-guard.test.ts deleted file mode 100644 index 3b35046..0000000 --- a/setup/legacy-service-guard.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import fs from 'fs'; -import os from 'os'; -import path from 'path'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -const { execSyncMock } = vi.hoisted(() => ({ - execSyncMock: vi.fn(), -})); - -vi.mock('child_process', () => ({ - execSync: execSyncMock, -})); - -import { - detectLegacyServiceIssues, - formatLegacyServiceFailureMessage, -} from './legacy-service-guard.js'; - -describe('legacy service guard', () => { - const tempRoots: string[] = []; - - afterEach(() => { - execSyncMock.mockReset(); - for (const root of tempRoots.splice(0)) { - fs.rmSync(root, { recursive: true, force: true }); - } - }); - - it('detects legacy systemd units in the opposite scope', () => { - const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-legacy-')); - tempRoots.push(projectRoot); - - execSyncMock.mockImplementation((cmd: string) => { - if (cmd === 'systemctl is-active ejclaw-codex') { - throw new Error('inactive'); - } - if (cmd === 'systemctl list-unit-files') { - return 'ejclaw-codex.service enabled\n'; - } - if (cmd === 'systemctl --user is-active ejclaw-codex') { - throw new Error('inactive'); - } - if (cmd === 'systemctl --user list-unit-files') { - return ''; - } - if (cmd === 'systemctl is-active ejclaw-review') { - throw new Error('inactive'); - } - if (cmd === 'systemctl --user is-active ejclaw-review') { - throw new Error('inactive'); - } - if (cmd === 'systemctl list-unit-files' || cmd === 'systemctl --user list-unit-files') { - return ''; - } - throw new Error(`unexpected command: ${cmd}`); - }); - - expect(detectLegacyServiceIssues(projectRoot, 'systemd')).toEqual([ - { - name: 'ejclaw-codex', - status: 'stopped', - sources: ['systemd-system'], - }, - ]); - }); - - it('detects legacy nohup artifacts even on systemd hosts', () => { - const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-legacy-')); - tempRoots.push(projectRoot); - fs.writeFileSync(path.join(projectRoot, 'start-ejclaw-codex.sh'), '#!/bin/bash\n'); - - execSyncMock.mockImplementation((cmd: string) => { - if ( - cmd === 'systemctl is-active ejclaw-codex' || - cmd === 'systemctl --user is-active ejclaw-codex' || - cmd === 'systemctl is-active ejclaw-review' || - cmd === 'systemctl --user is-active ejclaw-review' - ) { - throw new Error('inactive'); - } - if ( - cmd === 'systemctl list-unit-files' || - cmd === 'systemctl --user list-unit-files' - ) { - return ''; - } - throw new Error(`unexpected command: ${cmd}`); - }); - - expect(detectLegacyServiceIssues(projectRoot, 'systemd')).toEqual([ - { - name: 'ejclaw-codex', - status: 'stopped', - sources: ['nohup'], - }, - ]); - }); - - it('formats cleanup instructions for the detected scopes only', () => { - const message = formatLegacyServiceFailureMessage({ - projectRoot: '/srv/ejclaw', - serviceManager: 'systemd', - homeDir: '/home/user', - services: [ - { - name: 'ejclaw-codex', - status: 'stopped', - sources: ['systemd-system', 'nohup'], - }, - { - name: 'ejclaw-review', - status: 'running', - sources: ['systemd-user'], - }, - ], - }); - - expect(message).toContain('systemctl disable --now ejclaw-codex'); - expect(message).toContain('systemctl --user disable --now ejclaw-review'); - expect(message).toContain('pkill -F "/srv/ejclaw/ejclaw-codex.pid"'); - expect(message).not.toContain('systemctl --user disable --now ejclaw-codex'); - }); -}); diff --git a/setup/legacy-service-guard.ts b/setup/legacy-service-guard.ts deleted file mode 100644 index 2099978..0000000 --- a/setup/legacy-service-guard.ts +++ /dev/null @@ -1,209 +0,0 @@ -import os from 'os'; -import path from 'path'; - -import type { ServiceManager } from './platform.js'; -import { getLegacyServiceDefs } from './service-defs.js'; -import { - checkLaunchdServiceArtifact, - checkNohupServiceArtifact, - checkSystemdServiceInScope, - type ServiceCheck, - type SystemdScope, -} from './verify-services.js'; - -type LegacySource = 'launchd' | 'systemd-system' | 'systemd-user' | 'nohup'; - -export interface LegacyServiceIssue extends ServiceCheck { - sources: LegacySource[]; -} - -function summarizeLegacyStatus(statuses: ServiceCheck['status'][]): ServiceCheck['status'] { - if (statuses.includes('running')) { - return 'running'; - } - if (statuses.includes('stopped')) { - return 'stopped'; - } - return 'not_found'; -} - -export function detectLegacyServiceIssues( - projectRoot: string, - serviceManager: ServiceManager, - homeDir = os.homedir(), -): LegacyServiceIssue[] { - return getLegacyServiceDefs(projectRoot) - .map((def) => { - if (serviceManager === 'launchd') { - const status = checkLaunchdServiceArtifact( - def.launchdLabel, - path.join(homeDir, 'Library', 'LaunchAgents', `${def.launchdLabel}.plist`), - ); - if (status === 'not_found') return null; - return { - name: def.name, - status, - sources: ['launchd'] as LegacySource[], - }; - } - - if (serviceManager === 'systemd') { - const scopedChecks: Array<{ source: LegacySource; status: ServiceCheck['status'] }> = [ - { - source: 'systemd-system', - status: checkSystemdServiceInScope(def.name, 'system'), - }, - { - source: 'systemd-user', - status: checkSystemdServiceInScope(def.name, 'user'), - }, - { - source: 'nohup', - status: checkNohupServiceArtifact(projectRoot, def.name), - }, - ]; - const detected = scopedChecks.filter( - (current) => current.status !== 'not_found', - ); - if (detected.length === 0) return null; - return { - name: def.name, - status: summarizeLegacyStatus(detected.map((current) => current.status)), - sources: detected.map((current) => current.source), - }; - } - - const status = checkNohupServiceArtifact(projectRoot, def.name); - if (status === 'not_found') return null; - return { - name: def.name, - status, - sources: ['nohup'] as LegacySource[], - }; - }) - .filter((service): service is LegacyServiceIssue => Boolean(service)); -} - -function formatSystemdCleanupForScope( - serviceNames: string[], - homeDir: string, - scope: SystemdScope, -): string[] { - if (serviceNames.length === 0) return []; - const systemctlPrefix = scope === 'system' ? 'systemctl' : 'systemctl --user'; - const unitDir = - scope === 'system' - ? '/etc/systemd/system' - : path.join(homeDir, '.config', 'systemd', 'user'); - const unitPaths = serviceNames - .map((serviceName) => - JSON.stringify(path.join(unitDir, `${serviceName}.service`)), - ) - .join(' '); - - return [ - `${systemctlPrefix} disable --now ${serviceNames.join(' ')}`, - `rm -f ${unitPaths}`, - `${systemctlPrefix} daemon-reload`, - ]; -} - -function formatLaunchdCleanup(serviceNames: string[], homeDir: string): string { - if (serviceNames.length === 0) { - return ''; - } - const plistPaths = serviceNames.map((serviceName) => { - const label = serviceName === 'ejclaw-codex' - ? 'com.ejclaw-codex' - : 'com.ejclaw-review'; - return path.join(homeDir, 'Library', 'LaunchAgents', `${label}.plist`); - }); - - return [ - ...plistPaths.map( - (plistPath) => - `launchctl unload ${JSON.stringify(plistPath)} 2>/dev/null || true`, - ), - `rm -f ${plistPaths.map((plistPath) => JSON.stringify(plistPath)).join(' ')}`, - ].join('\n'); -} - -function formatNohupCleanup( - projectRoot: string, - serviceNames: string[], -): string { - if (serviceNames.length === 0) { - return ''; - } - const pidPaths = serviceNames - .map((serviceName) => path.join(projectRoot, `${serviceName}.pid`)) - .map((currentPath) => JSON.stringify(currentPath)); - const wrapperPaths = serviceNames - .map((serviceName) => path.join(projectRoot, `start-${serviceName}.sh`)) - .map((currentPath) => JSON.stringify(currentPath)); - - return [ - ...pidPaths.map((pidPath) => `pkill -F ${pidPath} 2>/dev/null || true`), - `rm -f ${[...pidPaths, ...wrapperPaths].join(' ')}`, - ].join('\n'); -} - -export function formatLegacyServiceFailureMessage(args: { - projectRoot: string; - serviceManager: ServiceManager; - services: LegacyServiceIssue[]; - homeDir?: string; -}): string { - const homeDir = args.homeDir ?? os.homedir(); - const details = args.services - .map( - (service) => - `${service.name}=${service.status} [${service.sources.join(',')}]`, - ) - .join(', '); - - let cleanupLines: string[] = []; - if (args.serviceManager === 'launchd') { - cleanupLines = formatLaunchdCleanup( - args.services - .filter((service) => service.sources.includes('launchd')) - .map((service) => service.name), - homeDir, - ).split('\n'); - } else if (args.serviceManager === 'systemd') { - cleanupLines = [ - ...formatSystemdCleanupForScope( - args.services - .filter((service) => service.sources.includes('systemd-system')) - .map((service) => service.name), - homeDir, - 'system', - ), - ...formatSystemdCleanupForScope( - args.services - .filter((service) => service.sources.includes('systemd-user')) - .map((service) => service.name), - homeDir, - 'user', - ), - ...formatNohupCleanup( - args.projectRoot, - args.services - .filter((service) => service.sources.includes('nohup')) - .map((service) => service.name), - ).split('\n'), - ].filter(Boolean); - } else { - cleanupLines = formatNohupCleanup( - args.projectRoot, - args.services.map((service) => service.name), - ).split('\n'); - } - - return [ - `Legacy EJClaw multi-service install detected: ${details}`, - 'This setup is reinstall-only. Remove the legacy services before continuing.', - 'Suggested cleanup:', - ...cleanupLines, - ].join('\n'); -} diff --git a/setup/restart-stack.test.ts b/setup/restart-stack.test.ts index e71aa0f..b9399b8 100644 --- a/setup/restart-stack.test.ts +++ b/setup/restart-stack.test.ts @@ -82,31 +82,6 @@ describe('restartStackServices', () => { ).toThrow('restart:stack only supports Linux systemd services in this repo'); }); - it('fails fast when legacy services are still present', () => { - const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-restart-')); - tempRoots.push(tempRoot); - - const execFileSyncImpl = vi.fn(); - - expect(() => - restartStackServices(tempRoot, { - direct: true, - execFileSyncImpl, - runningAsRoot: false, - serviceManager: 'systemd', - serviceId: null, - legacyServiceIssues: [ - { - name: 'ejclaw-codex', - status: 'stopped', - sources: ['systemd-system'], - }, - ], - }), - ).toThrow('Legacy EJClaw multi-service install detected'); - expect(execFileSyncImpl).not.toHaveBeenCalled(); - }); - it('falls back to direct restart when the oneshot unit is not installed for an external caller', () => { const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-restart-')); tempRoots.push(tempRoot); diff --git a/setup/restart-stack.ts b/setup/restart-stack.ts index b7d4ca5..c3fc44d 100644 --- a/setup/restart-stack.ts +++ b/setup/restart-stack.ts @@ -1,12 +1,6 @@ import { execFileSync } from 'child_process'; -import os from 'os'; import { pathToFileURL } from 'url'; -import { - detectLegacyServiceIssues, - formatLegacyServiceFailureMessage, - type LegacyServiceIssue, -} from './legacy-service-guard.js'; import { getServiceManager, isRoot } from './platform.js'; import { getConfiguredServiceNames } from './service-defs.js'; @@ -23,8 +17,6 @@ interface RestartStackDeps { serviceManager?: ServiceManager; direct?: boolean; serviceId?: string | null; - legacyServiceIssues?: LegacyServiceIssue[]; - homeDir?: string; } function restartStackServicesDirect( @@ -70,24 +62,6 @@ export function restartStackServices( ); } - const legacyServiceIssues = - deps.legacyServiceIssues ?? - detectLegacyServiceIssues( - projectRoot, - serviceManager, - deps.homeDir ?? os.homedir(), - ); - if (legacyServiceIssues.length > 0) { - throw new Error( - formatLegacyServiceFailureMessage({ - projectRoot, - serviceManager, - services: legacyServiceIssues, - homeDir: deps.homeDir, - }), - ); - } - const services = getConfiguredServiceNames(projectRoot); const execImpl = deps.execFileSyncImpl ?? execFileSync; const systemctlArgs = deps.runningAsRoot ?? isRoot() ? [] : ['--user']; diff --git a/setup/service-defs.ts b/setup/service-defs.ts index 2125a5a..a5a5918 100644 --- a/setup/service-defs.ts +++ b/setup/service-defs.ts @@ -1,4 +1,4 @@ -export type ServiceKind = 'primary' | 'legacy'; +export type ServiceKind = 'primary'; export interface ServiceDef { /** Stable topology kind used by setup/verify logic */ @@ -35,23 +35,6 @@ const CURRENT_SERVICE_TEMPLATES: ServiceTemplate[] = [ }, ]; -const LEGACY_SERVICE_TEMPLATES: ServiceTemplate[] = [ - { - kind: 'legacy', - name: 'ejclaw-codex', - launchdLabel: 'com.ejclaw-codex', - description: 'Legacy EJClaw Codex Assistant', - logName: 'ejclaw-codex', - }, - { - kind: 'legacy', - name: 'ejclaw-review', - launchdLabel: 'com.ejclaw-review', - description: 'Legacy EJClaw Codex Review Assistant', - logName: 'ejclaw-review', - }, -]; - function materializeServiceDef(template: ServiceTemplate): ServiceDef { const environmentFile = undefined; const extraEnv = @@ -79,17 +62,6 @@ export function getServiceDefs(projectRoot: string): ServiceDef[] { ); } -export function getLegacyServiceDefs(projectRoot: string): ServiceDef[] { - void projectRoot; - return LEGACY_SERVICE_TEMPLATES.map((template) => - materializeServiceDef(template), - ); -} - export function getConfiguredServiceNames(projectRoot: string): string[] { return getServiceDefs(projectRoot).map((def) => def.name); } - -export function getLegacyServiceNames(projectRoot: string): string[] { - return getLegacyServiceDefs(projectRoot).map((def) => def.name); -} diff --git a/setup/service.test.ts b/setup/service.test.ts index eff0c9e..940cb71 100644 --- a/setup/service.test.ts +++ b/setup/service.test.ts @@ -9,11 +9,7 @@ import { buildStackRestartSystemdUnit, buildSystemdUnit, } from './service-renderers.js'; -import { - getLegacyServiceDefs, - getServiceDefs, - type ServiceDef, -} from './service-defs.js'; +import { getServiceDefs, type ServiceDef } from './service-defs.js'; /** * Tests for service configuration generation. @@ -189,19 +185,6 @@ describe('service definitions', () => { }); }); - it('keeps legacy service identities available for migration guards', () => { - const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-stack-')); - tempRoots.push(tempRoot); - - const defs = getLegacyServiceDefs(tempRoot); - - expect(defs.map((def) => def.name)).toEqual([ - 'ejclaw-codex', - 'ejclaw-review', - ]); - expect(defs.map((def) => def.kind)).toEqual(['legacy', 'legacy']); - }); - it('generates a oneshot stack restart unit', () => { const unit = buildStackRestartSystemdUnit( '/srv/ejclaw', diff --git a/setup/service.ts b/setup/service.ts index 2c0f8d0..77a65a1 100644 --- a/setup/service.ts +++ b/setup/service.ts @@ -17,10 +17,6 @@ import { getNodePath, getServiceManager, } from './platform.js'; -import { - detectLegacyServiceIssues, - formatLegacyServiceFailureMessage, -} from './legacy-service-guard.js'; import { getServiceDefs } from './service-defs.js'; import { setupLaunchd, setupLinux } from './service-installers.js'; import { emitStatus } from './status.js'; @@ -54,37 +50,6 @@ export async function run(_args: string[]): Promise { ); } - const legacyServiceIssues = detectLegacyServiceIssues( - projectRoot, - serviceManager, - homeDir, - ); - if (legacyServiceIssues.length > 0) { - const errorMessage = formatLegacyServiceFailureMessage({ - projectRoot, - serviceManager, - homeDir, - services: legacyServiceIssues, - }); - logger.error( - { legacyServiceIssues, serviceManager }, - 'Legacy multi-service install detected during setup', - ); - emitStatus('SETUP_SERVICE', { - SERVICE_TYPE: serviceManager, - NODE_PATH: nodePath, - PROJECT_PATH: projectRoot, - STATUS: 'failed', - ERROR: 'legacy_services_detected', - LEGACY_SERVICES: legacyServiceIssues - .map((service) => `${service.name}:${service.status}`) - .join(','), - LOG: 'logs/setup.log', - }); - console.error(errorMessage); - process.exit(1); - } - // Build first logger.info('Building TypeScript'); try { diff --git a/setup/verify.ts b/setup/verify.ts index ddd0ba1..8e71945 100644 --- a/setup/verify.ts +++ b/setup/verify.ts @@ -7,13 +7,7 @@ * * Uses better-sqlite3 directly (no sqlite3 CLI), platform-aware service checks. */ -import os from 'os'; - import { logger } from '../src/logger.js'; -import { - detectLegacyServiceIssues, - formatLegacyServiceFailureMessage, -} from './legacy-service-guard.js'; import { getServiceManager } from './platform.js'; import { getServiceDefs } from './service-defs.js'; import { emitStatus } from './status.js'; @@ -39,21 +33,10 @@ export async function run(_args: string[]): Promise { // 1. Check service statuses const serviceDefs = getServiceDefs(projectRoot); const services = getServiceChecks(serviceDefs, projectRoot, mgr); - const legacyServiceIssues = detectLegacyServiceIssues( - projectRoot, - mgr, - os.homedir(), - ); for (const svc of services) { logger.info({ service: svc.name, status: svc.status }, 'Service status'); } - for (const svc of legacyServiceIssues) { - logger.error( - { service: svc.name, status: svc.status }, - 'Legacy service detected during verification', - ); - } const credentials = detectCredentials(projectRoot); const channelAuth = detectChannelAuth(); @@ -80,10 +63,7 @@ export async function run(_args: string[]): Promise { activeArbiterTasks, }, ); - const legacyServicesSummary = Object.fromEntries( - legacyServiceIssues.map((service) => [service.name, service.status]), - ); - const status = legacyServiceIssues.length > 0 ? 'failed' : baseStatus; + const status = baseStatus; logger.info( { @@ -92,27 +72,12 @@ export async function run(_args: string[]): Promise { tribunalRooms: detectedTribunalRooms, activeArbiterTasks: detectedActiveArbiterTasks, servicesSummary, - legacyServicesSummary, }, 'Verification complete', ); - if (legacyServiceIssues.length > 0) { - logger.error( - { - cleanup: formatLegacyServiceFailureMessage({ - projectRoot, - serviceManager: mgr, - homeDir: os.homedir(), - services: legacyServiceIssues, - }), - }, - 'Verification failed due to legacy multi-service install', - ); - } emitStatus('VERIFY', { SERVICES: JSON.stringify(servicesSummary), - LEGACY_SERVICES: JSON.stringify(legacyServicesSummary), // Legacy field (keep for backward compatibility) SERVICE: services[0].status, CREDENTIALS: credentials,