Merge pull request #2111 from qwibitai/setup-scratch-agent-cleanup
feat(setup): delete scratch agent after ping-pong, simplify flow
This commit is contained in:
75
scripts/delete-cli-agent.ts
Normal file
75
scripts/delete-cli-agent.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* Delete the scratch CLI agent created during setup's ping-pong test.
|
||||||
|
*
|
||||||
|
* Dynamically finds and removes all rows referencing the agent group
|
||||||
|
* (any table with an agent_group_id column), deletes the agent group
|
||||||
|
* itself, and removes the groups/<folder>/ directory. Leaves the CLI
|
||||||
|
* messaging group intact so it can be reused for a new agent.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* pnpm exec tsx scripts/delete-cli-agent.ts --folder <folder-name>
|
||||||
|
*/
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { DATA_DIR } from '../src/config.js';
|
||||||
|
import { getAgentGroupByFolder, deleteAgentGroup } from '../src/db/agent-groups.js';
|
||||||
|
import { initDb } from '../src/db/connection.js';
|
||||||
|
import { runMigrations } from '../src/db/migrations/index.js';
|
||||||
|
|
||||||
|
interface Args {
|
||||||
|
folder: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(): Args {
|
||||||
|
const argv = process.argv.slice(2);
|
||||||
|
let folder = '';
|
||||||
|
for (let i = 0; i < argv.length; i++) {
|
||||||
|
if (argv[i] === '--folder' && argv[i + 1]) folder = argv[++i];
|
||||||
|
}
|
||||||
|
if (!folder) {
|
||||||
|
console.error('usage: pnpm exec tsx scripts/delete-cli-agent.ts --folder <folder-name>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
return { folder };
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = parseArgs();
|
||||||
|
|
||||||
|
const db = initDb(path.join(DATA_DIR, 'v2.db'));
|
||||||
|
runMigrations(db);
|
||||||
|
|
||||||
|
const ag = getAgentGroupByFolder(args.folder);
|
||||||
|
if (!ag) {
|
||||||
|
console.log(`No agent group with folder "${args.folder}" — nothing to delete.`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanup = db.transaction(() => {
|
||||||
|
const tables = db
|
||||||
|
.prepare(
|
||||||
|
`SELECT DISTINCT m.name FROM sqlite_master m
|
||||||
|
JOIN pragma_table_info(m.name) p ON p.name = 'agent_group_id'
|
||||||
|
WHERE m.type = 'table' AND m.name != 'agent_groups'`,
|
||||||
|
)
|
||||||
|
.all() as { name: string }[];
|
||||||
|
for (const { name } of tables) {
|
||||||
|
db.prepare(`DELETE FROM ${name} WHERE agent_group_id = ?`).run(ag.id);
|
||||||
|
}
|
||||||
|
deleteAgentGroup(ag.id);
|
||||||
|
});
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
// Remove the groups/<folder>/ directory.
|
||||||
|
const groupDir = path.join(process.cwd(), 'groups', args.folder);
|
||||||
|
if (fs.existsSync(groupDir)) {
|
||||||
|
fs.rmSync(groupDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove session data on disk.
|
||||||
|
const sessionsDir = path.join(DATA_DIR, 'v2-sessions', ag.id);
|
||||||
|
if (fs.existsSync(sessionsDir)) {
|
||||||
|
fs.rmSync(sessionsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Deleted agent group ${ag.id} (${args.folder}).`);
|
||||||
@@ -41,11 +41,13 @@ const CLI_SYNTHETIC_USER_ID = `${CLI_CHANNEL}:${CLI_PLATFORM_ID}`;
|
|||||||
interface Args {
|
interface Args {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
agentName: string;
|
agentName: string;
|
||||||
|
folder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseArgs(argv: string[]): Args {
|
function parseArgs(argv: string[]): Args {
|
||||||
let displayName: string | undefined;
|
let displayName: string | undefined;
|
||||||
let agentName: string | undefined;
|
let agentName: string | undefined;
|
||||||
|
let folder: string | undefined;
|
||||||
for (let i = 0; i < argv.length; i++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
const key = argv[i];
|
const key = argv[i];
|
||||||
const val = argv[i + 1];
|
const val = argv[i + 1];
|
||||||
@@ -55,6 +57,9 @@ function parseArgs(argv: string[]): Args {
|
|||||||
} else if (key === '--agent-name') {
|
} else if (key === '--agent-name') {
|
||||||
agentName = val;
|
agentName = val;
|
||||||
i++;
|
i++;
|
||||||
|
} else if (key === '--folder') {
|
||||||
|
folder = val;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +72,7 @@ function parseArgs(argv: string[]): Args {
|
|||||||
return {
|
return {
|
||||||
displayName,
|
displayName,
|
||||||
agentName: agentName?.trim() || displayName,
|
agentName: agentName?.trim() || displayName,
|
||||||
|
folder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +101,7 @@ async function main(): Promise<void> {
|
|||||||
const promotedToOwner = false;
|
const promotedToOwner = false;
|
||||||
|
|
||||||
// 2. Agent group + filesystem.
|
// 2. Agent group + filesystem.
|
||||||
const folder = `cli-with-${normalizeName(args.displayName)}`;
|
const folder = args.folder || `cli-with-${normalizeName(args.displayName)}`;
|
||||||
let ag: AgentGroup | undefined = getAgentGroupByFolder(folder);
|
let ag: AgentGroup | undefined = getAgentGroupByFolder(folder);
|
||||||
if (!ag) {
|
if (!ag) {
|
||||||
const agId = generateId('ag');
|
const agId = generateId('ag');
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import { pollHealth } from './onecli.js';
|
|||||||
import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js';
|
import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js';
|
||||||
import { claudeCliAvailable, resolveTimezoneViaClaude } from './lib/tz-from-claude.js';
|
import { claudeCliAvailable, resolveTimezoneViaClaude } from './lib/tz-from-claude.js';
|
||||||
import * as setupLog from './logs.js';
|
import * as setupLog from './logs.js';
|
||||||
import { ensureAnswer, fail, runQuietChild, runQuietStep } from './lib/runner.js';
|
import { ensureAnswer, fail, runQuietChild, runQuietStep, spawnQuiet } from './lib/runner.js';
|
||||||
import { emit as phEmit } from './lib/diagnostics.js';
|
import { emit as phEmit } from './lib/diagnostics.js';
|
||||||
import { accentGreen, brandBody, brandBold, brandChip, dimWrap, fitToWidth, fmtDuration, note, wrapForGutter } from './lib/theme.js';
|
import { accentGreen, brandBody, brandBold, brandChip, dimWrap, fitToWidth, fmtDuration, note, wrapForGutter } from './lib/theme.js';
|
||||||
import { isValidTimezone } from '../src/timezone.js';
|
import { isValidTimezone } from '../src/timezone.js';
|
||||||
@@ -328,7 +328,7 @@ async function main(): Promise<void> {
|
|||||||
running: 'Bringing your assistant online…',
|
running: 'Bringing your assistant online…',
|
||||||
done: 'Assistant wired up.',
|
done: 'Assistant wired up.',
|
||||||
},
|
},
|
||||||
['--display-name', displayName!, '--agent-name', CLI_AGENT_NAME],
|
['--display-name', displayName!, '--agent-name', CLI_AGENT_NAME, '--folder', '_ping-test'],
|
||||||
);
|
);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
await fail(
|
await fail(
|
||||||
@@ -349,6 +349,27 @@ async function main(): Promise<void> {
|
|||||||
const ping = await confirmAssistantResponds();
|
const ping = await confirmAssistantResponds();
|
||||||
if (ping === 'ok') {
|
if (ping === 'ok') {
|
||||||
phEmit('first_chat_ready');
|
phEmit('first_chat_ready');
|
||||||
|
const cleanupRawLog = setupLog.stepRawLog('cleanup-cli-agent');
|
||||||
|
const cleanupStart = Date.now();
|
||||||
|
const cleanup = await spawnQuiet(
|
||||||
|
'pnpm',
|
||||||
|
['exec', 'tsx', 'scripts/delete-cli-agent.ts', '--folder', '_ping-test'],
|
||||||
|
cleanupRawLog,
|
||||||
|
);
|
||||||
|
setupLog.step(
|
||||||
|
'cleanup-cli-agent',
|
||||||
|
cleanup.ok ? 'success' : 'failed',
|
||||||
|
Date.now() - cleanupStart,
|
||||||
|
{ exit_code: cleanup.exitCode },
|
||||||
|
cleanupRawLog,
|
||||||
|
);
|
||||||
|
if (!cleanup.ok) {
|
||||||
|
p.log.warn(
|
||||||
|
brandBody(
|
||||||
|
`Couldn't clean up the test agent — it may still appear in your agent list. See ${cleanupRawLog} for details.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
const next = ensureAnswer(
|
const next = ensureAnswer(
|
||||||
await brightSelect<'continue' | 'chat'>({
|
await brightSelect<'continue' | 'chat'>({
|
||||||
message: 'What next?',
|
message: 'What next?',
|
||||||
@@ -366,7 +387,23 @@ async function main(): Promise<void> {
|
|||||||
}),
|
}),
|
||||||
) as 'continue' | 'chat';
|
) as 'continue' | 'chat';
|
||||||
setupLog.userInput('first_chat_choice', next);
|
setupLog.userInput('first_chat_choice', next);
|
||||||
if (next === 'chat') await runFirstChat();
|
if (next === 'chat') {
|
||||||
|
const terminalAgentName = `${displayName!}'s Terminal`;
|
||||||
|
const createRes = await runQuietChild(
|
||||||
|
'create-terminal-agent',
|
||||||
|
'pnpm',
|
||||||
|
['exec', 'tsx', 'scripts/init-cli-agent.ts', '--display-name', displayName!, '--agent-name', terminalAgentName],
|
||||||
|
{ running: `Creating ${terminalAgentName}…`, done: `${terminalAgentName} is ready.` },
|
||||||
|
);
|
||||||
|
if (!createRes.ok) {
|
||||||
|
await fail(
|
||||||
|
'create-terminal-agent',
|
||||||
|
`Couldn't create ${terminalAgentName}.`,
|
||||||
|
'You can retry later with `pnpm exec tsx scripts/init-cli-agent.ts`.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await runFirstChat();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
phEmit('first_chat_failed', { reason: ping });
|
phEmit('first_chat_failed', { reason: ping });
|
||||||
renderPingFailureNote(ping);
|
renderPingFailureNote(ping);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* Args:
|
* Args:
|
||||||
* --display-name <name> (required) operator's display name
|
* --display-name <name> (required) operator's display name
|
||||||
* --agent-name <name> (optional) agent persona name, defaults to display-name
|
* --agent-name <name> (optional) agent persona name, defaults to display-name
|
||||||
|
* --folder <name> (optional) explicit folder name, defaults to cli-with-<normalized-display-name>
|
||||||
*/
|
*/
|
||||||
import { execFileSync } from 'child_process';
|
import { execFileSync } from 'child_process';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@@ -18,9 +19,11 @@ import { emitStatus } from './status.js';
|
|||||||
function parseArgs(args: string[]): {
|
function parseArgs(args: string[]): {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
agentName?: string;
|
agentName?: string;
|
||||||
|
folder?: string;
|
||||||
} {
|
} {
|
||||||
let displayName: string | undefined;
|
let displayName: string | undefined;
|
||||||
let agentName: string | undefined;
|
let agentName: string | undefined;
|
||||||
|
let folder: string | undefined;
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
const key = args[i];
|
const key = args[i];
|
||||||
@@ -34,6 +37,10 @@ function parseArgs(args: string[]): {
|
|||||||
agentName = val;
|
agentName = val;
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
|
case '--folder':
|
||||||
|
folder = val;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,17 +53,18 @@ function parseArgs(args: string[]): {
|
|||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { displayName, agentName };
|
return { displayName, agentName, folder };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function run(args: string[]): Promise<void> {
|
export async function run(args: string[]): Promise<void> {
|
||||||
const { displayName, agentName } = parseArgs(args);
|
const { displayName, agentName, folder } = parseArgs(args);
|
||||||
|
|
||||||
const projectRoot = process.cwd();
|
const projectRoot = process.cwd();
|
||||||
const script = path.join(projectRoot, 'scripts', 'init-cli-agent.ts');
|
const script = path.join(projectRoot, 'scripts', 'init-cli-agent.ts');
|
||||||
|
|
||||||
const scriptArgs = ['exec', 'tsx', script, '--display-name', displayName];
|
const scriptArgs = ['exec', 'tsx', script, '--display-name', displayName];
|
||||||
if (agentName) scriptArgs.push('--agent-name', agentName);
|
if (agentName) scriptArgs.push('--agent-name', agentName);
|
||||||
|
if (folder) scriptArgs.push('--folder', folder);
|
||||||
|
|
||||||
log.info('Invoking init-cli-agent', { displayName, agentName });
|
log.info('Invoking init-cli-agent', { displayName, agentName });
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user