176 lines
4.6 KiB
TypeScript
176 lines
4.6 KiB
TypeScript
/**
|
|
* Step: register — Assign a room via the canonical application service.
|
|
*
|
|
* EJClaw is Discord-only, so registrations must target Discord channel IDs.
|
|
*/
|
|
import { execFileSync } from 'child_process';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import { GROUPS_DIR } from '../src/config.js';
|
|
import { assignRoom, initDatabase } from '../src/db.js';
|
|
import { isValidGroupFolder } from '../src/group-folder.js';
|
|
import { logger } from '../src/logger.js';
|
|
import { emitStatus } from './status.js';
|
|
|
|
/**
|
|
* Resolve a usable git-repo work_dir for a new tribunal room.
|
|
* Order: <folder> if .git exists, else <folder>/repo if .git exists,
|
|
* else `git init` <folder> (with an empty initial commit) and use it.
|
|
* Returns the absolute work_dir path.
|
|
*/
|
|
function ensureWorkDirForFolder(folderName: string): string {
|
|
const folderPath = path.join(GROUPS_DIR, folderName);
|
|
fs.mkdirSync(folderPath, { recursive: true });
|
|
|
|
if (fs.existsSync(path.join(folderPath, '.git'))) {
|
|
return folderPath;
|
|
}
|
|
const repoSub = path.join(folderPath, 'repo');
|
|
if (fs.existsSync(path.join(repoSub, '.git'))) {
|
|
return repoSub;
|
|
}
|
|
|
|
// No git repo found anywhere — initialize at the folder root.
|
|
const gitEnv = {
|
|
...process.env,
|
|
GIT_AUTHOR_NAME: 'ejclaw',
|
|
GIT_AUTHOR_EMAIL: 'ejclaw@local',
|
|
GIT_COMMITTER_NAME: 'ejclaw',
|
|
GIT_COMMITTER_EMAIL: 'ejclaw@local',
|
|
};
|
|
execFileSync('git', ['init', '-q'], { cwd: folderPath, env: gitEnv });
|
|
execFileSync(
|
|
'git',
|
|
['commit', '-q', '--allow-empty', '-m', `init ${folderName} workspace`],
|
|
{ cwd: folderPath, env: gitEnv },
|
|
);
|
|
logger.info(
|
|
{ folder: folderName, workDir: folderPath },
|
|
'Initialized git repository for tribunal workspace',
|
|
);
|
|
return folderPath;
|
|
}
|
|
|
|
interface RegisterArgs {
|
|
jid: string;
|
|
name: string;
|
|
folder: string;
|
|
channel: string;
|
|
isMain: boolean;
|
|
assistantNameProvided: boolean;
|
|
}
|
|
|
|
function parseArgs(args: string[]): RegisterArgs {
|
|
const result: RegisterArgs = {
|
|
jid: '',
|
|
name: '',
|
|
folder: '',
|
|
channel: 'discord',
|
|
isMain: false,
|
|
assistantNameProvided: false,
|
|
};
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
switch (args[i]) {
|
|
case '--jid':
|
|
result.jid = args[++i] || '';
|
|
break;
|
|
case '--name':
|
|
result.name = args[++i] || '';
|
|
break;
|
|
case '--folder':
|
|
result.folder = args[++i] || '';
|
|
break;
|
|
case '--channel':
|
|
result.channel = (args[++i] || '').toLowerCase();
|
|
break;
|
|
case '--is-main':
|
|
result.isMain = true;
|
|
break;
|
|
case '--assistant-name':
|
|
result.assistantNameProvided = true;
|
|
i += 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export async function run(args: string[]): Promise<void> {
|
|
const parsed = parseArgs(args);
|
|
|
|
if (!parsed.jid || !parsed.name || !parsed.folder) {
|
|
emitStatus('REGISTER_CHANNEL', {
|
|
STATUS: 'failed',
|
|
ERROR: 'missing_required_args',
|
|
LOG: 'logs/setup.log',
|
|
});
|
|
process.exit(4);
|
|
}
|
|
|
|
if (!isValidGroupFolder(parsed.folder)) {
|
|
emitStatus('REGISTER_CHANNEL', {
|
|
STATUS: 'failed',
|
|
ERROR: 'invalid_folder',
|
|
LOG: 'logs/setup.log',
|
|
});
|
|
process.exit(4);
|
|
}
|
|
|
|
if (parsed.channel !== 'discord') {
|
|
emitStatus('REGISTER_CHANNEL', {
|
|
STATUS: 'failed',
|
|
ERROR: 'unsupported_channel',
|
|
LOG: 'logs/setup.log',
|
|
});
|
|
process.exit(4);
|
|
}
|
|
|
|
if (parsed.assistantNameProvided) {
|
|
emitStatus('REGISTER_CHANNEL', {
|
|
STATUS: 'failed',
|
|
ERROR: 'assistant_name_option_removed',
|
|
NEXT_STEP:
|
|
'Use a dedicated assistant identity configuration command instead',
|
|
LOG: 'logs/setup.log',
|
|
});
|
|
process.exit(4);
|
|
}
|
|
|
|
logger.info(parsed, 'Registering channel');
|
|
|
|
initDatabase();
|
|
|
|
// Defaults for newly registered rooms:
|
|
// roomMode = tribunal, owner = claude-code, reviewer = codex
|
|
// Tribunal rooms also require a git-backed work_dir; auto-init if missing.
|
|
const workDir = ensureWorkDirForFolder(parsed.folder);
|
|
|
|
assignRoom(parsed.jid, {
|
|
name: parsed.name,
|
|
folder: parsed.folder,
|
|
isMain: parsed.isMain,
|
|
roomMode: 'tribunal',
|
|
ownerAgentType: 'claude-code',
|
|
reviewerAgentType: 'codex',
|
|
workDir,
|
|
});
|
|
logger.info('Assigned room through canonical room service');
|
|
|
|
fs.mkdirSync(path.join(GROUPS_DIR, parsed.folder, 'logs'), {
|
|
recursive: true,
|
|
});
|
|
logger.info({ folder: parsed.folder }, 'Ensured group log directory exists');
|
|
|
|
emitStatus('REGISTER_CHANNEL', {
|
|
JID: parsed.jid,
|
|
NAME: parsed.name,
|
|
FOLDER: parsed.folder,
|
|
CHANNEL: parsed.channel,
|
|
STATUS: 'success',
|
|
LOG: 'logs/setup.log',
|
|
});
|
|
}
|