Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CodegenRestDashboard/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copy this file to .env and fill in your Codegen credentials
# Never commit your real token to git.

# API token for Codegen REST API
CODEGEN_TOKEN=sk-REPLACE_ME

# Organization ID for your Codegen org
ORG_ID=123

# Optional: override API base URL
API_BASE=https://api.codegen.com/v1

# Optional: Shared secret for webhook validation (if you choose to use it)
WEBHOOK_SECRET=

29 changes: 29 additions & 0 deletions CodegenRestDashboard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Ignore local secrets
.env
# OS/temp
.DS_Store
Thumbs.db
# Logs
*.log
# Editor swaps
*~
*.swp
*.swo
# Generated artifacts
/node_modules/
/build/
/dist/
# Cache
/.cache/
/.parcel-cache/
/.vite/
/coverage/
# IDEs
/.idea/
/.vscode/
*.iml
# Misc
npm-debug.log*
yarn-debug.log*
yarn-error.log*

31 changes: 31 additions & 0 deletions CodegenRestDashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
CodegenRestDashboard (zero-deps)

Setup
1) Copy .env.example to .env and fill:
- CODEGEN_TOKEN=sk-...
- ORG_ID=...
2) Do NOT commit .env (ignored by .gitignore here).

Run the dashboard
- node CodegenRestDashboard/server.js
- Open http://localhost:8080
- Header shows Active count; hover to see active list and click to open logs dialog
- No manual refresh; lists auto-refresh periodically

Commands (from repo root)
- node CodegenRestDashboard/commands/list_agent_runs.js [--limit 10]
- node CodegenRestDashboard/commands/get_agent_run.js --id 123
- node CodegenRestDashboard/commands/get_agent_run_logs.js --id 123 [--limit 50]
- node CodegenRestDashboard/commands/create_agent_run.js --prompt "Fix the bug" [--repo_id 123 --model "Sonnet 4.5"]
- node CodegenRestDashboard/commands/resume_agent_run.js --id 123 --prompt "Follow-up"
- node CodegenRestDashboard/commands/generate_setup_commands.js --repo_id 123 --prompt "Init sandbox"

Cloudflare webhook (optional)
- Deploy webhook_server.js as a Worker to route POST https://www.pixelium.uk/webhook
- Optional header: X-Webhook-Secret with WEBHOOK_SECRET in Worker vars
- For local demo, POST sample payload to http://localhost:8080/api/webhook

E2E smoke test
- node CodegenRestDashboard/e2e_test.js
- Optionally set E2E_CREATE_PROMPT to force creating a run in the test

60 changes: 60 additions & 0 deletions CodegenRestDashboard/commands/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Minimal .env loader without dependencies
// Supports keys like CODEGEN_TOKEN, ORG_ID, API_BASE
// Also tolerates 'codegen token' and 'org_id' in various cases

const fs = require('fs');
const path = require('path');

function loadEnv() {
const baseDir = path.join(__dirname, '..');
const envPath = path.join(baseDir, '.env');
const result = {};

if (fs.existsSync(envPath)) {
const text = fs.readFileSync(envPath, 'utf8');
text.split(/\r?\n/).forEach((line) => {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) return;
const eq = trimmed.indexOf('=');
if (eq === -1) return;
const rawKey = trimmed.slice(0, eq).trim();
const value = trimmed.slice(eq + 1).trim();
const normKey = rawKey.replace(/\s+/g, '_').toUpperCase();
result[normKey] = value;
});
}

// Also allow process.env to override
const merged = {
...result,
...(process.env.CODEGEN_TOKEN ? { CODEGEN_TOKEN: process.env.CODEGEN_TOKEN } : {}),
...(process.env.ORG_ID ? { ORG_ID: process.env.ORG_ID } : {}),
...(process.env.API_BASE ? { API_BASE: process.env.API_BASE } : {}),
...(process.env.WEBHOOK_SECRET ? { WEBHOOK_SECRET: process.env.WEBHOOK_SECRET } : {}),
};

// Backwards-compat with space key: 'codegen token'
if (!merged.CODEGEN_TOKEN && merged.CODEGEN_TOKEN === undefined && merged['CODEGEN_TOKEN'] === undefined) {
if (merged['CODEGEN_TOKEN'] === undefined && merged['CODEGEN_TOKEN'] === undefined) {
// no-op; kept for safety
}
}

// Provide defaults
const token = merged.CODEGEN_TOKEN || merged.TOKEN || merged.CODEGEN || merged.CODEGENTOKEN || merged['CODEGEN_TOKEN'] || merged['CODEGENTOKEN'] || merged['CODEGEN-TOKEN'];
const orgId = merged.ORG_ID || merged.ORGID || merged['ORG-ID'] || merged['ORG_ID'];
const apiBase = merged.API_BASE || 'https://api.codegen.com/v1';
const webhookSecret = merged.WEBHOOK_SECRET || '';

if (!token) {
console.warn('[config] WARNING: CODEGEN_TOKEN not found. Place it in CodegenRestDashboard/.env as CODEGEN_TOKEN=...');
}
if (!orgId) {
console.warn('[config] WARNING: ORG_ID not found. Place it in CodegenRestDashboard/.env as ORG_ID=...');
}

return { token, orgId, apiBase, webhookSecret, baseDir };
}

module.exports = { loadEnv };

29 changes: 29 additions & 0 deletions CodegenRestDashboard/commands/create_agent_run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node
const { loadEnv } = require('./config');
const { post } = require('./http');
const { parseArgs, pretty } = require('./utils');

(async () => {
try {
const { orgId } = loadEnv();
const args = parseArgs(process.argv);
const prompt = args.prompt || args.p;
if (!prompt) throw new Error('Missing --prompt');

const body = { prompt };
if (args.repo_id) body.repo_id = Number(args.repo_id);
if (args.model) body.model = args.model;
if (args.agent_type) body.agent_type = args.agent_type;
if (args.images) {
// comma-separated data URIs
body.images = String(args.images).split(',').map((s) => s.trim()).filter(Boolean);
}

const res = await post(`/organizations/${orgId}/agent/run`, body);
console.log(pretty(res));
} catch (err) {
console.error('[create_agent_run] Error:', err.message);
process.exit(1);
}
})();

25 changes: 25 additions & 0 deletions CodegenRestDashboard/commands/generate_setup_commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env node
const { loadEnv } = require('./config');
const { post } = require('./http');
const { parseArgs, pretty } = require('./utils');

(async () => {
try {
const { orgId } = loadEnv();
const args = parseArgs(process.argv);
const repoId = args.repo_id || args.repo;
const prompt = args.prompt || args.p;

if (!repoId) throw new Error('Missing --repo_id');

const body = { repo_id: Number(repoId) };
if (prompt) body.prompt = prompt;

const res = await post(`/organizations/${orgId}/setup-commands/generate`, body);
console.log(pretty(res));
} catch (err) {
console.error('[generate_setup_commands] Error:', err.message);
process.exit(1);
}
})();

20 changes: 20 additions & 0 deletions CodegenRestDashboard/commands/get_agent_run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node
const { loadEnv } = require('./config');
const { get } = require('./http');
const { parseArgs, pretty } = require('./utils');

(async () => {
try {
const { orgId } = loadEnv();
const args = parseArgs(process.argv);
const id = args.id || args.agent_run_id;
if (!id) throw new Error('Missing --id');

const res = await get(`/organizations/${orgId}/agent/run/${Number(id)}`);
console.log(pretty(res));
} catch (err) {
console.error('[get_agent_run] Error:', err.message);
process.exit(1);
}
})();

25 changes: 25 additions & 0 deletions CodegenRestDashboard/commands/get_agent_run_logs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env node
const { loadEnv } = require('./config');
const { get } = require('./http');
const { parseArgs, pretty } = require('./utils');

(async () => {
try {
const { orgId } = loadEnv();
const args = parseArgs(process.argv);
const id = args.id || args.agent_run_id;
if (!id) throw new Error('Missing --id');

const query = {};
if (args.skip) query.skip = Number(args.skip);
if (args.limit) query.limit = Number(args.limit);
if (args.reverse !== undefined) query.reverse = String(args.reverse) === 'true' || args.reverse === true;

const res = await get(`/alpha/organizations/${orgId}/agent/run/${Number(id)}/logs`, { query });
console.log(pretty(res));
} catch (err) {
console.error('[get_agent_run_logs] Error:', err.message);
process.exit(1);
}
})();

90 changes: 90 additions & 0 deletions CodegenRestDashboard/commands/http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Minimal HTTP client without external deps
// Uses global fetch if available (Node 18+),
// otherwise falls back to https.request

const https = require('https');
const { loadEnv } = require('./config');

const { token, apiBase } = loadEnv();

function buildHeaders(extra = {}) {
const headers = {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...extra,
};
return headers;
}

async function fetchWithFallback(url, options) {
if (typeof fetch === 'function') {
return fetch(url, options);
}
// Fallback to https.request
return new Promise((resolve, reject) => {
const u = new URL(url);
const req = https.request(
{
method: options.method || 'GET',
protocol: u.protocol,
hostname: u.hostname,
port: u.port || 443,
path: `${u.pathname}${u.search}`,
headers: options.headers,
},
(res) => {
const chunks = [];
res.on('data', (d) => chunks.push(d));
res.on('end', () => {
const body = Buffer.concat(chunks).toString('utf8');
resolve({
ok: res.statusCode >= 200 && res.statusCode < 300,
status: res.statusCode,
text: async () => body,
json: async () => {
try { return JSON.parse(body || '{}'); } catch { return {}; }
},
});
});
}
);
req.on('error', reject);
if (options.body) req.write(options.body);
req.end();
});
}

async function get(path, { query } = {}) {
const url = new URL(apiBase.replace(/\/$/, '') + path);
if (query) {
Object.entries(query).forEach(([k, v]) => {
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
});
}
const res = await fetchWithFallback(url.toString(), {
method: 'GET',
headers: buildHeaders(),
});
if (!res.ok) {
const txt = await res.text();
throw new Error(`GET ${url} failed: ${res.status} ${txt}`);
}
return res.json();
}

async function post(path, body) {
const url = apiBase.replace(/\/$/, '') + path;
const res = await fetchWithFallback(url, {
method: 'POST',
headers: buildHeaders(),
body: JSON.stringify(body || {}),
});
if (!res.ok) {
const txt = await res.text();
throw new Error(`POST ${url} failed: ${res.status} ${txt}`);
}
return res.json();
}

module.exports = { get, post };

23 changes: 23 additions & 0 deletions CodegenRestDashboard/commands/list_agent_runs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env node
const { loadEnv } = require('./config');
const { get } = require('./http');
const { parseArgs, pretty } = require('./utils');

(async () => {
try {
const { orgId } = loadEnv();
const args = parseArgs(process.argv);
const query = {};
if (args.user_id) query.user_id = Number(args.user_id);
if (args.source_type) query.source_type = args.source_type;
if (args.skip) query.skip = Number(args.skip);
if (args.limit) query.limit = Number(args.limit);

const res = await get(`/organizations/${orgId}/agent/runs`, { query });
console.log(pretty(res));
} catch (err) {
console.error('[list_agent_runs] Error:', err.message);
process.exit(1);
}
})();

27 changes: 27 additions & 0 deletions CodegenRestDashboard/commands/resume_agent_run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env node
const { loadEnv } = require('./config');
const { post } = require('./http');
const { parseArgs, pretty } = require('./utils');

(async () => {
try {
const { orgId } = loadEnv();
const args = parseArgs(process.argv);
const id = args.id || args.agent_run_id;
const prompt = args.prompt || args.p;
if (!id) throw new Error('Missing --id');
if (!prompt) throw new Error('Missing --prompt');

const body = { agent_run_id: Number(id), prompt };
if (args.images) {
body.images = String(args.images).split(',').map((s) => s.trim()).filter(Boolean);
}

const res = await post(`/organizations/${orgId}/agent/run/resume`, body);
console.log(pretty(res));
} catch (err) {
console.error('[resume_agent_run] Error:', err.message);
process.exit(1);
}
})();

Loading