Spaces:
Sleeping
Sleeping
File size: 5,365 Bytes
dbeb746 cf7dc69 3cd6080 cf7dc69 3cd6080 dbeb746 3cd6080 dbeb746 3cd6080 dbeb746 3cd6080 dbeb746 cf7dc69 3cd6080 cf7dc69 dbeb746 3cd6080 dbeb746 3cd6080 dbeb746 3cd6080 dbeb746 cf7dc69 dbeb746 cf7dc69 dbeb746 3cd6080 dbeb746 3cd6080 dbeb746 3cd6080 dbeb746 cf7dc69 dbeb746 cf7dc69 3cd6080 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | import { execFile } from 'node:child_process';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { homedir } from 'node:os';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
const DEFAULT_STATE_PATH = join(homedir(), '.config', 'sin', 'sin-code-ai', 'onboarding-state.json');
type OnboardingState = {
ownerEmail?: string;
notes?: string;
updatedAt?: string;
};
export type TemplateAgentAction =
| { action: 'agent.help' }
| { action: 'sin.code.ai.health' }
| { action: 'sin.code.ai.room13.claim'; room13Url?: string; bearerToken?: string; workerName?: string; confirm?: boolean }
| { action: 'sin.code.ai.onboarding.status' }
| { action: 'sin.code.ai.onboarding.save'; ownerEmail?: string; notes?: string; confirm?: boolean };
export async function executeTemplateAgentAction(action: TemplateAgentAction): Promise<unknown> {
switch (action.action) {
case 'agent.help':
return buildHelpPayload();
case 'sin.code.ai.health':
return {
ok: true,
agent: 'sin-code-ai',
primaryModel: 'openai/gpt-5.4',
team: 'Team - Coding',
opencode: await runOpenCodeHealthPrompt(),
};
case 'sin.code.ai.room13.claim':
return await runRoom13Claim(action);
case 'sin.code.ai.onboarding.status':
return {
ok: true,
statePath: DEFAULT_STATE_PATH,
state: await readState(),
};
case 'sin.code.ai.onboarding.save':
if (!action.confirm) {
throw new Error('input_required:confirm=true required');
}
return {
ok: true,
statePath: DEFAULT_STATE_PATH,
state: await writeState({
ownerEmail: clean(action.ownerEmail),
notes: clean(action.notes),
updatedAt: new Date().toISOString(),
}),
};
}
}
function buildHelpPayload() {
return {
ok: true,
agent: 'sin-code-ai',
actions: ['sin.code.ai.health', 'sin.code.ai.room13.claim', 'sin.code.ai.onboarding.status', 'sin.code.ai.onboarding.save'],
controlPlane: {
projectionMode: 'control-plane-first',
capabilityRegistry: 'control_plane.capabilities',
consumerAuthScript: './scripts/hf_pull_script.py',
completeInstallScript: './scripts/complete-install.sh',
},
};
}
async function readState(): Promise<OnboardingState | null> {
try {
return JSON.parse(await readFile(DEFAULT_STATE_PATH, 'utf8')) as OnboardingState;
} catch {
return null;
}
}
async function writeState(state: OnboardingState) {
await mkdir(dirname(DEFAULT_STATE_PATH), { recursive: true });
await writeFile(DEFAULT_STATE_PATH, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
return state;
}
function clean(value: string | undefined) {
const normalized = String(value || '').trim();
return normalized || undefined;
}
async function runRoom13Claim(action: any) {
const url = action.room13Url || process.env.ROOM13_URL;
const token = action.bearerToken || process.env.ROOM13_BEARER_TOKEN;
const worker = action.workerName || process.env.ROOM13_WORKER_NAME || 'sin-code-ai';
if (!url || !token) throw new Error('missing_room13_config');
const regRes = await fetch(`${url}/api/workers`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ name: `${worker}:team-coding`, type: 'general', capabilities: ['zeus_github_branch', 'sin_issues_pool_issue'], worker_identity: worker, persona_id: worker }),
});
if (!regRes.ok) throw new Error(`Failed to register worker: ${await regRes.text()}`);
const workerId = (await regRes.json() as any).id;
const claimRes = await fetch(`${url}/api/tasks/claim-next`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ worker_id: workerId, accepted_types: ['zeus_github_branch', 'sin_issues_pool_issue'], accepted_team_ids: ['team-coding'], min_priority: 'low', run_id: `${worker}-${Date.now()}`, lease_epoch: 0 }),
});
if (claimRes.status === 404) return { ok: true, claimed: false, message: 'No pending tasks found' };
if (!claimRes.ok) throw new Error(`Failed to claim task: ${await claimRes.text()}`);
return { ok: true, claimed: true, workerId, task: await claimRes.json() };
}
async function runOpenCodePrompt(prompt: string, timeoutMs = 120000) {
const result = await execFileAsync('opencode', ['run', prompt, '--format', 'json'], { timeout: timeoutMs });
return result.stdout;
}
async function runOpenCodeHealthPrompt() {
const prompt = 'You are SIN-Code-AI. Reply with a concise readiness line for the agent health check.';
try {
const { stdout } = await execFileAsync('opencode', ['run', prompt, '--format', 'json'], {
maxBuffer: 10 * 1024 * 1024,
timeout: 5_000,
});
const parts: string[] = [];
for (const line of String(stdout || '').split('\n')) {
const trimmed = line.trim();
if (!trimmed) continue;
try {
const event = JSON.parse(trimmed) as { type?: string; part?: { text?: string } };
if (event.type === 'text' && event.part?.text) parts.push(event.part.text);
} catch {}
}
return parts.join('').trim() || 'ready';
} catch {
return 'ready';
}
}
|