Keep root-platform identity separate from tenant labels
--- Build: pass | Tests: FAIL — 0 failed
This commit is contained in:
parent
d383e88b09
commit
a99f97172d
12 changed files with 51 additions and 25 deletions
|
|
@ -26,6 +26,7 @@ import {
|
||||||
PI_TUI_THINKING,
|
PI_TUI_THINKING,
|
||||||
PI_TUI_TOOLS,
|
PI_TUI_TOOLS,
|
||||||
PROJECT_ROOT,
|
PROJECT_ROOT,
|
||||||
|
RUNTIME_ID,
|
||||||
TENANT_ID,
|
TENANT_ID,
|
||||||
TMP_DIR,
|
TMP_DIR,
|
||||||
TIMEZONE,
|
TIMEZONE,
|
||||||
|
|
@ -52,7 +53,7 @@ import {
|
||||||
type PiRuntimeUsage,
|
type PiRuntimeUsage,
|
||||||
} from './pi-usage.js';
|
} from './pi-usage.js';
|
||||||
|
|
||||||
const METRICS_PREFIX = `${TENANT_ID}_`;
|
const METRICS_PREFIX = `${RUNTIME_ID}_`;
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import {
|
import {
|
||||||
applyFallback,
|
applyFallback,
|
||||||
|
|
@ -345,7 +346,7 @@ export async function runJailAgent(
|
||||||
fs.mkdirSync(logDir, { recursive: true });
|
fs.mkdirSync(logDir, { recursive: true });
|
||||||
|
|
||||||
const safeName = input.groupFolder.replace(/[^a-zA-Z0-9-]/g, '-');
|
const safeName = input.groupFolder.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||||
const runId = `${TENANT_ID}-${safeName}-${Date.now()}`;
|
const runId = `${RUNTIME_ID}-${safeName}-${Date.now()}`;
|
||||||
const logFile = path.join(logDir, `agent-${runId}.log`);
|
const logFile = path.join(logDir, `agent-${runId}.log`);
|
||||||
|
|
||||||
// ── Offline stub ──────────────────────────────────────────────────────
|
// ── Offline stub ──────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ describe('config identity', () => {
|
||||||
const config = await import('./config.js');
|
const config = await import('./config.js');
|
||||||
|
|
||||||
expect(config.TENANT_ID).toBe('mevy');
|
expect(config.TENANT_ID).toBe('mevy');
|
||||||
|
expect(config.RUNTIME_ID).toBe('mevy');
|
||||||
expect(config.TENANT_DISPLAY_NAME).toBe('Bob');
|
expect(config.TENANT_DISPLAY_NAME).toBe('Bob');
|
||||||
expect(config.ASSISTANT_NAME).toBe('Bob');
|
expect(config.ASSISTANT_NAME).toBe('Bob');
|
||||||
expect(config.AGENT_DOMAIN).toBe('home.arpa');
|
expect(config.AGENT_DOMAIN).toBe('home.arpa');
|
||||||
|
|
@ -98,6 +99,7 @@ describe('config identity', () => {
|
||||||
const config = await import('./config.js');
|
const config = await import('./config.js');
|
||||||
|
|
||||||
expect(config.TENANT_ID).toBe('');
|
expect(config.TENANT_ID).toBe('');
|
||||||
|
expect(config.RUNTIME_ID).toBe('clawdie');
|
||||||
expect(config.AGENT_CONFIG_DIR).toBe('clawdie-cp');
|
expect(config.AGENT_CONFIG_DIR).toBe('clawdie-cp');
|
||||||
expect(config.AGENT_INTERNAL_DOMAIN).toBe('clawdie.home.arpa');
|
expect(config.AGENT_INTERNAL_DOMAIN).toBe('clawdie.home.arpa');
|
||||||
expect(config.CMS_WEBROOT).toBe('/usr/local/www/clawdie');
|
expect(config.CMS_WEBROOT).toBe('/usr/local/www/clawdie');
|
||||||
|
|
|
||||||
|
|
@ -270,10 +270,13 @@ export const AGENT_GENDER: AgentGender = (() => {
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// ── Derived system identifiers ──
|
// ── Derived system identifiers ──
|
||||||
|
//
|
||||||
|
// RUNTIME_ID is for labels and derived paths that need a stable non-empty
|
||||||
|
// identifier in both root-platform and additive-tenant installs. It does not
|
||||||
|
// change TENANT_ID semantics: the root install still has TENANT_ID=''.
|
||||||
|
export const RUNTIME_ID = TENANT_ID || SERVICE_NAME;
|
||||||
|
|
||||||
const RUNTIME_TENANT_ID = TENANT_ID || SERVICE_NAME;
|
export const AGENT_CONFIG_DIR = tenantControlplanePrefix(RUNTIME_ID);
|
||||||
|
|
||||||
export const AGENT_CONFIG_DIR = tenantControlplanePrefix(RUNTIME_TENANT_ID);
|
|
||||||
export const AGENT_DOMAIN =
|
export const AGENT_DOMAIN =
|
||||||
process.env.AGENT_DOMAIN ||
|
process.env.AGENT_DOMAIN ||
|
||||||
envConfig.AGENT_DOMAIN ||
|
envConfig.AGENT_DOMAIN ||
|
||||||
|
|
@ -285,7 +288,7 @@ export const AGENT_INTERNAL_DOMAIN =
|
||||||
process.env.AGENT_INTERNAL_DOMAIN ||
|
process.env.AGENT_INTERNAL_DOMAIN ||
|
||||||
envConfig.AGENT_INTERNAL_DOMAIN ||
|
envConfig.AGENT_INTERNAL_DOMAIN ||
|
||||||
registryDefaults?.tenants[TENANT_ID]?.internalDomain ||
|
registryDefaults?.tenants[TENANT_ID]?.internalDomain ||
|
||||||
tenantInternalDomain(RUNTIME_TENANT_ID, PLATFORM_INTERNAL_BASE);
|
tenantInternalDomain(RUNTIME_ID, PLATFORM_INTERNAL_BASE);
|
||||||
export const CONTROLPLANE_INTERNAL_DOMAIN = PLATFORM_INTERNAL_DOMAIN;
|
export const CONTROLPLANE_INTERNAL_DOMAIN = PLATFORM_INTERNAL_DOMAIN;
|
||||||
export const CONTROLPLANE_HOST_LABEL =
|
export const CONTROLPLANE_HOST_LABEL =
|
||||||
CONTROLPLANE_INTERNAL_DOMAIN.split('.')[0] || 'ai';
|
CONTROLPLANE_INTERNAL_DOMAIN.split('.')[0] || 'ai';
|
||||||
|
|
@ -714,11 +717,11 @@ export const CMS_JAIL_IP =
|
||||||
export const CMS_WEBROOT =
|
export const CMS_WEBROOT =
|
||||||
process.env.CMS_WEBROOT ||
|
process.env.CMS_WEBROOT ||
|
||||||
envConfig.CMS_WEBROOT ||
|
envConfig.CMS_WEBROOT ||
|
||||||
`/usr/local/www/${RUNTIME_TENANT_ID}`;
|
`/usr/local/www/${RUNTIME_ID}`;
|
||||||
export const ASTRO_SITE_PATH =
|
export const ASTRO_SITE_PATH =
|
||||||
process.env.ASTRO_SITE_PATH ||
|
process.env.ASTRO_SITE_PATH ||
|
||||||
envConfig.ASTRO_SITE_PATH ||
|
envConfig.ASTRO_SITE_PATH ||
|
||||||
tenantSiteRoot(RUNTIME_TENANT_ID, PLATFORM_RUNTIME_HOME);
|
tenantSiteRoot(RUNTIME_ID, PLATFORM_RUNTIME_HOME);
|
||||||
export const GIT_JAIL_IP =
|
export const GIT_JAIL_IP =
|
||||||
process.env.WARDEN_GIT_IP ||
|
process.env.WARDEN_GIT_IP ||
|
||||||
envConfig.WARDEN_GIT_IP ||
|
envConfig.WARDEN_GIT_IP ||
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
CMS_WEBROOT,
|
CMS_WEBROOT,
|
||||||
CONTROLPLANE_MAX_BODY_BYTES,
|
CONTROLPLANE_MAX_BODY_BYTES,
|
||||||
CONTROLPLANE_BIND_HOST,
|
CONTROLPLANE_BIND_HOST,
|
||||||
|
RUNTIME_ID,
|
||||||
TENANT_ID,
|
TENANT_ID,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
import {
|
import {
|
||||||
|
|
@ -59,7 +60,7 @@ import { logger } from './logger.js';
|
||||||
|
|
||||||
// ── Types ──────────────────────────────────────────────────────────────────
|
// ── Types ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const METRICS_PREFIX = `${TENANT_ID}_`;
|
const METRICS_PREFIX = `${RUNTIME_ID}_`;
|
||||||
|
|
||||||
export interface ControlplaneState {
|
export interface ControlplaneState {
|
||||||
agents: Array<{ id: string; role: string; heartbeat_enabled: boolean }>;
|
agents: Array<{ id: string; role: string; heartbeat_enabled: boolean }>;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { randomUUID } from 'crypto';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
|
|
||||||
import { incLabeledCounter } from '../metrics.js';
|
import { incLabeledCounter } from '../metrics.js';
|
||||||
import { TENANT_ID } from '../config.js';
|
import { RUNTIME_ID, TENANT_ID } from '../config.js';
|
||||||
import { buildHostdAuth } from './auth.js';
|
import { buildHostdAuth } from './auth.js';
|
||||||
import { SOCKET_PATH } from './types.js';
|
import { SOCKET_PATH } from './types.js';
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -92,7 +92,7 @@ export class HostdClient {
|
||||||
try {
|
try {
|
||||||
const resp = JSON.parse(line) as HostdResponse;
|
const resp = JSON.parse(line) as HostdResponse;
|
||||||
if (resp.id === id) {
|
if (resp.id === id) {
|
||||||
const _mp = `${TENANT_ID}_`;
|
const _mp = `${RUNTIME_ID}_`;
|
||||||
incLabeledCounter(`${_mp}jail_ops_total`, 'op', op);
|
incLabeledCounter(`${_mp}jail_ops_total`, 'op', op);
|
||||||
if (!resp.ok) incLabeledCounter(`${_mp}jail_ops_errors_total`, 'op', op);
|
if (!resp.ok) incLabeledCounter(`${_mp}jail_ops_errors_total`, 'op', op);
|
||||||
settle(() => resolve(resp));
|
settle(() => resolve(resp));
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
PI_TUI_PROVIDER,
|
PI_TUI_PROVIDER,
|
||||||
PI_TUI_MODEL,
|
PI_TUI_MODEL,
|
||||||
PROJECT_ROOT,
|
PROJECT_ROOT,
|
||||||
|
RUNTIME_ID,
|
||||||
TELEGRAM_BOT_TOKEN,
|
TELEGRAM_BOT_TOKEN,
|
||||||
STT_MODEL,
|
STT_MODEL,
|
||||||
STT_MAX_ATTEMPTS,
|
STT_MAX_ATTEMPTS,
|
||||||
|
|
@ -60,8 +61,8 @@ import { cleanupPendingMaintenanceSnapshots } from './maintenance-snapshots.js';
|
||||||
import { hostd } from './hostd/client.js';
|
import { hostd } from './hostd/client.js';
|
||||||
|
|
||||||
// ── Metrics prefix — derived from agent name for multi-install dashboards ──
|
// ── Metrics prefix — derived from agent name for multi-install dashboards ──
|
||||||
const METRICS_PREFIX = `${TENANT_ID}_`;
|
const METRICS_PREFIX = `${RUNTIME_ID}_`;
|
||||||
const ROOT_AGENT_ID = TENANT_ID.trim() || SERVICE_NAME;
|
const ROOT_AGENT_ID = RUNTIME_ID;
|
||||||
import { Watchdog } from './watchdog.js';
|
import { Watchdog } from './watchdog.js';
|
||||||
import { extractTmpImagePaths } from './outbound-images.js';
|
import { extractTmpImagePaths } from './outbound-images.js';
|
||||||
import './channels/telegram.js';
|
import './channels/telegram.js';
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
LLAMA_CPP_INTERNAL_DOMAIN,
|
LLAMA_CPP_INTERNAL_DOMAIN,
|
||||||
OLLAMA_INTERNAL_DOMAIN,
|
OLLAMA_INTERNAL_DOMAIN,
|
||||||
PLATFORM_INTERNAL_BASE,
|
PLATFORM_INTERNAL_BASE,
|
||||||
|
RUNTIME_ID,
|
||||||
SUBNET_BASE,
|
SUBNET_BASE,
|
||||||
TENANT_ID,
|
TENANT_ID,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
|
|
@ -41,11 +42,11 @@ function uniqueNames(names: string[]): string[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalHostsBlockStart(): string {
|
export function getLocalHostsBlockStart(): string {
|
||||||
return `# >>> ${TENANT_ID} local hosts >>>`;
|
return `# >>> ${RUNTIME_ID} local hosts >>>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalHostsBlockEnd(): string {
|
export function getLocalHostsBlockEnd(): string {
|
||||||
return `# <<< ${TENANT_ID} local hosts <<<`;
|
return `# <<< ${RUNTIME_ID} local hosts <<<`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalHostsEntries(): LocalHostsEntry[] {
|
export function getLocalHostsEntries(): LocalHostsEntry[] {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* Enable by setting METRICS_PORT (default 9100).
|
* Enable by setting METRICS_PORT (default 9100).
|
||||||
* Set METRICS_PORT=0 to disable entirely.
|
* Set METRICS_PORT=0 to disable entirely.
|
||||||
*
|
*
|
||||||
* Metrics exposed (prefix = ${TENANT_ID}_):
|
* Metrics exposed (prefix = ${RUNTIME_ID}_):
|
||||||
* sessions_started_total — counter
|
* sessions_started_total — counter
|
||||||
* sessions_completed_total — counter {status="ok|error|timeout"}
|
* sessions_completed_total — counter {status="ok|error|timeout"}
|
||||||
* session_duration_seconds_sum — counter (sum of durations)
|
* session_duration_seconds_sum — counter (sum of durations)
|
||||||
|
|
@ -23,10 +23,10 @@
|
||||||
*/
|
*/
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
|
|
||||||
import { TENANT_ID } from './config.js';
|
import { RUNTIME_ID } from './config.js';
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
|
|
||||||
const P = `${TENANT_ID}_`;
|
const P = `${RUNTIME_ID}_`;
|
||||||
|
|
||||||
// ── Registry ──────────────────────────────────────────────────────────────────
|
// ── Registry ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import pg from 'pg';
|
import pg from 'pg';
|
||||||
|
|
||||||
import { SKILLS_DB_URL, TENANT_ID } from './config.js';
|
import { RUNTIME_ID, SKILLS_DB_URL } from './config.js';
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import { incCounter } from './metrics.js';
|
import { incCounter } from './metrics.js';
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ export async function searchBuiltinKnowledge(
|
||||||
[queryText, limit],
|
[queryText, limit],
|
||||||
);
|
);
|
||||||
|
|
||||||
const _mp = `${TENANT_ID}_`;
|
const _mp = `${RUNTIME_ID}_`;
|
||||||
incCounter(`${_mp}skill_searches_total`);
|
incCounter(`${_mp}skill_searches_total`);
|
||||||
incCounter(`${_mp}skill_search_hits_total`, rows.length);
|
incCounter(`${_mp}skill_search_hits_total`, rows.length);
|
||||||
return rows;
|
return rows;
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const mockCreate = vi.hoisted(() => vi.fn());
|
const mockCreate = vi.hoisted(() => vi.fn());
|
||||||
|
const openAiCtor = vi.hoisted(() => vi.fn());
|
||||||
|
|
||||||
vi.mock('openai', () => {
|
vi.mock('openai', () => {
|
||||||
return {
|
return {
|
||||||
default: vi.fn().mockImplementation(function () {
|
default: openAiCtor.mockImplementation(function () {
|
||||||
return {
|
return {
|
||||||
audio: {
|
audio: {
|
||||||
transcriptions: {
|
transcriptions: {
|
||||||
|
|
@ -68,6 +69,22 @@ describe('initTranscription', () => {
|
||||||
}),
|
}),
|
||||||
).not.toThrow();
|
).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses a stable OpenRouter title in root-platform mode', async () => {
|
||||||
|
const { initTranscription: init } = await import('./transcription.js');
|
||||||
|
init({
|
||||||
|
provider: 'openrouter',
|
||||||
|
model: 'whisper-1',
|
||||||
|
openrouterApiKey: 'or-test-key',
|
||||||
|
});
|
||||||
|
expect(openAiCtor).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
defaultHeaders: expect.objectContaining({
|
||||||
|
'X-Title': 'clawdie-ai',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import OpenAI from 'openai';
|
import OpenAI from 'openai';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { TENANT_ID } from './config.js';
|
import { RUNTIME_ID } from './config.js';
|
||||||
|
|
||||||
let openaiClient: OpenAI | null = null;
|
let openaiClient: OpenAI | null = null;
|
||||||
let transcriptionReady = false;
|
let transcriptionReady = false;
|
||||||
|
|
@ -123,7 +123,7 @@ export function initTranscription(
|
||||||
baseURL: 'https://openrouter.ai/api/v1',
|
baseURL: 'https://openrouter.ai/api/v1',
|
||||||
defaultHeaders: {
|
defaultHeaders: {
|
||||||
'HTTP-Referer': 'https://codeberg.org/Clawdie/Clawdie-AI',
|
'HTTP-Referer': 'https://codeberg.org/Clawdie/Clawdie-AI',
|
||||||
'X-Title': `${TENANT_ID}-ai`,
|
'X-Title': `${RUNTIME_ID}-ai`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
transcriptionReady = true;
|
transcriptionReady = true;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import path from 'path';
|
||||||
|
|
||||||
import { readEnvFile } from './env.js';
|
import { readEnvFile } from './env.js';
|
||||||
import {
|
import {
|
||||||
TENANT_ID,
|
RUNTIME_ID,
|
||||||
TMP_DIR,
|
TMP_DIR,
|
||||||
VISION_MAX_CHARS_PER_IMAGE,
|
VISION_MAX_CHARS_PER_IMAGE,
|
||||||
VISION_MAX_IMAGES,
|
VISION_MAX_IMAGES,
|
||||||
|
|
@ -90,7 +90,7 @@ async function describeImageOpenRouter(imagePath: string): Promise<string> {
|
||||||
Authorization: `Bearer ${key}`,
|
Authorization: `Bearer ${key}`,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'HTTP-Referer': 'https://codeberg.org/Clawdie/Clawdie-AI',
|
'HTTP-Referer': 'https://codeberg.org/Clawdie/Clawdie-AI',
|
||||||
'X-Title': `${TENANT_ID}-ai`,
|
'X-Title': `${RUNTIME_ID}-ai`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue