made tiny updates

This commit is contained in:
SinachPat
2026-05-05 22:06:31 +01:00
parent 19b8f29523
commit fdf64ee72c
39 changed files with 5499 additions and 381 deletions
+95 -11
View File
@@ -2,18 +2,21 @@
// ── Originmain CLI ───────────────────────────────────────────────────────────
// Usage:
// npx @originmain/cli dev --target http://localhost:3000 [--port 4170]
// [--index-port 4171] [--no-index]
// npx @originmain/cli dev --target http://localhost:3000 [--port 4170]
// [--index-port 4171] [--no-index]
// npx @originmain/cli login [--app-url https://app.originmain.io]
//
// Starts a reverse proxy + optional AST indexer for live React component
// inspection in Originmain artboards. See SOURCE-AWARE-CANVAS.md for details.
import { parseArgs } from 'node:util';
import { resolve } from 'node:path';
import { startProxy } from './proxy.js';
import { Indexer } from './indexer.js';
import { startIndexServer } from './index-server.js';
import { detectProjectMeta } from './detect-framework.js';
import { parseArgs } from 'node:util';
import { resolve } from 'node:path';
import { startProxy } from './proxy.js';
import { Indexer } from './indexer.js';
import { startIndexServer } from './index-server.js';
import { detectProjectMeta } from './detect-framework.js';
import { initIsolationServer } from './isolation-server.js';
import { runLogin } from './commands/login.js';
const DEFAULT_PORT = 4170;
const DEFAULT_INDEX_PORT = 4171;
@@ -22,10 +25,11 @@ function printUsage(): void {
console.log(`
\x1b[36m\x1b[1mOriginmain CLI\x1b[0m
Usage:
originmain dev --target <url> [options]
Commands:
originmain dev --target <url> [options]
originmain login [--app-url <url>]
Options:
Dev options:
--target, -t Target dev server URL (required)
Example: http://localhost:3000
--port, -p Proxy listen port (default: ${DEFAULT_PORT})
@@ -33,10 +37,15 @@ function printUsage(): void {
--no-index Disable the AST indexer (Props/Code tabs degraded)
--help, -h Show this help
Login options:
--app-url Originmain app URL (default: https://app.originmain.io)
Environment variables:
ORIGINMAIN_BRIDGE_URL Agent Bridge URL (default: http://localhost:4172)
ORIGINMAIN_APP_URL Originmain app URL (overrides --app-url default)
Examples:
npx @originmain/cli login
npx @originmain/cli dev --target http://localhost:3000
npx @originmain/cli dev --target http://localhost:3000 --no-index
`);
@@ -50,6 +59,7 @@ async function main(): Promise<void> {
port: { type: 'string', short: 'p' },
'index-port': { type: 'string' },
'no-index': { type: 'boolean' },
'app-url': { type: 'string' },
help: { type: 'boolean', short: 'h' },
},
allowPositionals: true,
@@ -63,6 +73,13 @@ async function main(): Promise<void> {
process.exit(values.help ? 0 : 1);
}
// ── login command ──────────────────────────────────────────────────────────
if (command === 'login') {
const appUrl = values['app-url'] as string | undefined;
await runLogin(appUrl ? { appUrl } : {});
process.exit(0);
}
if (command !== 'dev') {
console.error(` Unknown command: ${command}\n Run "originmain --help" for usage.`);
process.exit(1);
@@ -143,9 +160,76 @@ async function main(): Promise<void> {
const indexUrl = noIndex ? null : `http://localhost:${indexPort}`;
const proxy = startProxy({ target, port, indexUrl });
// ── Initialize the isolation server (serves /__om_isolation__ requests) ───
// Must be done after the proxy starts so `handleIsolationRequest` is wired up.
initIsolationServer({ projectRoot, devServerBase: target });
// ── Register indexer with Agent Bridge (spec Phase 5 §8.3) ───────────────
// Read workspace token from env or ~/.originmain/config.json.
// If no token is found, skip registration and log a warning.
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
if (!noIndex && indexUrl) {
const HEARTBEAT_INTERVAL_MS = 120_000; // 120 s — matches server TTL refresh spec
const TTL_SECONDS = 300;
let workspaceToken: string | null = process.env['ORIGINMAIN_WORKSPACE_TOKEN'] ?? null;
if (!workspaceToken) {
try {
const homeDir = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '';
const cfgPath = resolve(homeDir, '.originmain', 'config.json');
const { readFileSync } = await import('node:fs');
const cfg = JSON.parse(readFileSync(cfgPath, 'utf-8')) as Record<string, unknown>;
if (typeof cfg['workspaceToken'] === 'string') workspaceToken = cfg['workspaceToken'];
} catch { /* config not present — skip registration */ }
}
if (workspaceToken) {
const registerUrl = `${bridgeUrl}/api/agent-bridge/register-indexer`;
async function registerIndexer(): Promise<void> {
try {
const res = await fetch(registerUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${workspaceToken}` },
body: JSON.stringify({ indexerUrl: indexUrl, ttl: TTL_SECONDS }),
signal: AbortSignal.timeout(8_000),
});
if (res.ok) {
console.log(` \x1b[32m✓\x1b[0m Indexer registered with Agent Bridge (TTL: ${TTL_SECONDS}s)`);
} else {
console.warn(` \x1b[33m⚠\x1b[0m Agent Bridge registration failed: ${res.status}`);
}
} catch (err) {
// Non-fatal — agent features are unavailable but the rest of the CLI works
const msg = err instanceof Error ? err.message : String(err);
console.warn(` \x1b[33m⚠\x1b[0m Could not reach Agent Bridge (${msg}). Agent features disabled.`);
}
}
async function sendHeartbeat(): Promise<void> {
try {
await fetch(registerUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${workspaceToken}` },
body: JSON.stringify({}), // no indexerUrl = heartbeat path
signal: AbortSignal.timeout(5_000),
});
} catch { /* non-fatal heartbeat miss */ }
}
void registerIndexer();
heartbeatTimer = setInterval(() => { void sendHeartbeat(); }, HEARTBEAT_INTERVAL_MS);
} else {
console.log(' \x1b[2mNot logged in — Agent Bridge integration disabled.\x1b[0m');
console.log(' \x1b[2mRun \x1b[0moriginmain login\x1b[2m to enable agent features.\x1b[0m');
}
}
// ── Graceful shutdown ─────────────────────────────────────────────────────
function shutdown(): void {
console.log('\n Shutting down...');
if (heartbeatTimer) clearInterval(heartbeatTimer);
proxy.close();
indexServer?.close();
process.exit(0);