HAL AI 채팅룸 도입(0.3.0): 운영자 콘솔 오른쪽 패널에 통합 방(첫 탭 고정) + 모델별 개별 방, 모델 연결(로그인·OAuth PKCE·API 키, 비밀은 Vault), 모델별 고유 정보(이름·역할·특성), HAL 제어·감시 스킬 문서·도구 29종(읽기 즉시 / 쓰기·고위험은 제안 → 운영자 승인(재인증·3단계 확인)), 통합 방 규칙(@이름 지정, 모델→모델 자동 1회 전달), MCP stdio 서버(읽기 전용), 개발 지시 v0.3.0(Oz), docs/HAL-AI.md, VERSION_HISTORY 0.3.0 — test/hal-ai.test.mjs RED 0/11 → GREEN 11/11, 전체 26파일 182건 통과

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Claude Fable 5.1
2026-10-28 17:41:50 +00:00
parent 6bfde82c5e
commit ba3c5cb2f8
23 changed files with 1494 additions and 11 deletions
+5 -1
View File
@@ -17,6 +17,7 @@
* node hal/index.mjs module-deploy --module <m> --version <v> --servers <srv_id[,srv_id]> [--by <who>] [--reason <text>] [--nonce <n>]
* 모듈 배포 작업(module.deploy)을 운영자 API 와 같은 멱등 규칙으로 큐에 넣는다(시스템 신원, 감사 기록). 실행은 serve 프로세스의 작업 엔진이 맡는다
* node hal/index.mjs job --id <j_id> 작업·자식 작업 readback(JSON)
* node hal/index.mjs mcp HAL 도구를 MCP(stdio) 서버로 내보낸다(0.3.0, 읽기 도구만 실행 — hal/ai/mcp.mjs)
*/
import { loadConfig } from './config.mjs';
import { createHalDeps, VERSION } from './deps.mjs';
@@ -37,7 +38,7 @@ import { join, dirname, basename } from 'node:path';
const log = (...a) => console.log(new Date().toISOString(), ...a);
const cfg = loadConfig(); const args = process.argv.slice(2); const cmd = args[0] && !args[0].startsWith('--') ? args[0] : 'serve';
const opt = (name, def = null) => { const i = args.indexOf('--' + name); return i >= 0 ? (args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true) : def; };
const d = createHalDeps({ config: cfg, log });
const d = createHalDeps({ config: cfg, log: cmd === 'mcp' ? (...a) => console.error(new Date().toISOString(), ...a) : log }); // mcp 모드는 stdout 을 JSON-RPC 전용으로 비워 둔다
if (cmd === 'setup-token') {
if (core.ensureOpsAccountExists(d)) { console.error('운영자 계정이 이미 있습니다 — 설정 토큰은 최초 1회만 쓸 수 있습니다'); process.exit(1); }
@@ -96,6 +97,9 @@ if (cmd === 'setup-token') {
} else if (cmd === 'job') {
const j = opt('id') && opt('id') !== true ? d.jobs.get(opt('id')) : null; if (!j) { console.error('--id <j_id> 가 필요합니다(없는 작업)'); process.exit(1); }
console.log(JSON.stringify({ job: describe(j), children: d.jobs.list({ parentId: j.id, limit: 100 }).map(describe) }, null, 1));
} else if (cmd === 'mcp') {
// 0.3.0: HAL 도구를 MCP(stdio) 서버로 내보낸다(hal/ai/mcp.mjs). 읽기 도구만 실행, 쓰기·고위험은 운영자 콘솔 승인 안내. 로그는 stderr.
const { runMcpServer } = await import('./ai/mcp.mjs'); await runMcpServer(d); d.store.close();
} else if (cmd === 'serve') {
// "하나의 HAL" 임대(§34). 다른 인스턴스가 살아 있으면 기동 거부(--takeover 는 재해복구 절차에서만).
try { core.claimLive(d, { instanceId: d.instanceId, version: VERSION, takeover: !!opt('takeover') }); } catch (e) { console.error(`[hal] 기동 거부: ${e.message}`); process.exit(2); }