refactor(multitenant): centralize controlplane session paths
Introduce a shared controlplane paths helper and use it in runtime plus operator tooling. This removes another tenant-derived path assumption and aligns controlplane session logs with the actual tmp-based layout used by the platform. --- Build: pass | Tests: pass — 105 passed (7 files)
This commit is contained in:
parent
c8cfa898de
commit
66a36a6548
6 changed files with 56 additions and 11 deletions
|
|
@ -89,6 +89,11 @@ Also updated:
|
|||
- `scripts/agent-status.ts`, `scripts/agent-task.ts`, and
|
||||
`scripts/agent-task-status.ts` now use platform-owned wording for operator
|
||||
errors
|
||||
- `src/controlplane-paths.ts` now centralizes controlplane session directory
|
||||
layout under project `tmp/`
|
||||
- `scripts/agent-logs.ts` now reads the real controlplane session paths
|
||||
(`tmp/sessions` and `tmp/sessions/pi`) instead of guessing a tenant-named
|
||||
home config path
|
||||
|
||||
## Recommended first code tasks
|
||||
|
||||
|
|
@ -118,8 +123,9 @@ Likely next targets:
|
|||
|
||||
- `src/controlplane-heartbeat.ts`
|
||||
- shared tmp socket/pid/session names
|
||||
- `scripts/agent-logs.ts` and any remaining operator tooling still keyed to
|
||||
tenant-derived runtime paths
|
||||
- any remaining operator tooling still keyed to tenant-derived runtime paths
|
||||
- broader ownership audit for remaining `AGENT_NAME` references in shared
|
||||
runtime code
|
||||
|
||||
### 3. Build a dedicated platform audit command
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
import { TMP_DIR } from '../src/config.js';
|
||||
import {
|
||||
getControlplanePiSessionDir,
|
||||
getControlplaneSessionDir,
|
||||
} from '../src/controlplane-paths.js';
|
||||
|
||||
const agentId = process.argv[2] ?? '';
|
||||
const agentName = (process.env.AGENT_NAME || 'clawdie').toLowerCase();
|
||||
const SESSIONS_DIR = path.join(os.homedir(), '.config', agentName, 'sessions');
|
||||
const SESSIONS_DIR = getControlplaneSessionDir(TMP_DIR);
|
||||
const PI_SESSIONS_DIR = getControlplanePiSessionDir(SESSIONS_DIR);
|
||||
|
||||
function findSessionFiles(): string[] {
|
||||
if (!fs.existsSync(SESSIONS_DIR)) return [];
|
||||
const files: string[] = [];
|
||||
for (const entry of fs.readdirSync(SESSIONS_DIR, { withFileTypes: true })) {
|
||||
if (entry.name.endsWith('.jsonl')) {
|
||||
files.push(path.join(SESSIONS_DIR, entry.name));
|
||||
const roots = [SESSIONS_DIR, PI_SESSIONS_DIR];
|
||||
for (const root of roots) {
|
||||
if (!fs.existsSync(root)) continue;
|
||||
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
||||
if (entry.name.endsWith('.jsonl')) {
|
||||
files.push(path.join(root, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return files.sort().reverse();
|
||||
|
|
|
|||
20
src/controlplane-paths.test.ts
Normal file
20
src/controlplane-paths.test.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
getControlplanePiSessionDir,
|
||||
getControlplaneSessionDir,
|
||||
} from './controlplane-paths.js';
|
||||
|
||||
describe('controlplane-paths', () => {
|
||||
it('derives the controlplane session dir from tmp', () => {
|
||||
expect(getControlplaneSessionDir('/srv/clawdie/tmp')).toBe(
|
||||
'/srv/clawdie/tmp/sessions',
|
||||
);
|
||||
});
|
||||
|
||||
it('derives the nested pi session dir from the controlplane session dir', () => {
|
||||
expect(getControlplanePiSessionDir('/srv/clawdie/tmp/sessions')).toBe(
|
||||
'/srv/clawdie/tmp/sessions/pi',
|
||||
);
|
||||
});
|
||||
});
|
||||
9
src/controlplane-paths.ts
Normal file
9
src/controlplane-paths.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import path from 'path';
|
||||
|
||||
export function getControlplaneSessionDir(tmpDir: string): string {
|
||||
return path.join(tmpDir, 'sessions');
|
||||
}
|
||||
|
||||
export function getControlplanePiSessionDir(sessionDir: string): string {
|
||||
return path.join(sessionDir, 'pi');
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { getControlplanePiSessionDir } from './controlplane-paths.js';
|
||||
import { getAgentSkillIndex } from './skill-library.js';
|
||||
import {
|
||||
CONTROLPLANE_JAIL_ISOLATION,
|
||||
|
|
@ -109,7 +110,7 @@ export function buildControlplaneRunCommand(
|
|||
|
||||
// Pi writes its own native JSONL format — keep it in a 'pi/' subdirectory
|
||||
// so it doesn't collide with the controlplane SessionEntry files in the parent.
|
||||
const piSessionDir = path.join(absSessionCwd, 'pi');
|
||||
const piSessionDir = getControlplanePiSessionDir(absSessionCwd);
|
||||
fs.mkdirSync(piSessionDir, { recursive: true });
|
||||
const sessionFile = path.join(piSessionDir, `${agentId}.jsonl`);
|
||||
const identityFile =
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
TELEGRAM_OPS_CHAT_ID,
|
||||
WATCHDOG_MODE,
|
||||
} from './config.js';
|
||||
import { getControlplaneSessionDir } from './controlplane-paths.js';
|
||||
import { incCounter, registerGauge, startMetricsServer } from './metrics.js';
|
||||
import {
|
||||
buildStartupReport,
|
||||
|
|
@ -970,7 +971,7 @@ async function main(): Promise<void> {
|
|||
// Controlplane uses the memory pool — ensure schema + agents + budgets exist.
|
||||
await runSchemaMigration(getMemoryPool());
|
||||
|
||||
const sessionDir = path.join(TMP_DIR, 'sessions');
|
||||
const sessionDir = getControlplaneSessionDir(TMP_DIR);
|
||||
fs.mkdirSync(sessionDir, { recursive: true });
|
||||
|
||||
let controlplaneServer: import('http').Server | undefined;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue