diff --git a/.gitignore b/.gitignore index b75a0fa..1fa7219 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .wrangler/ +.npmrc diff --git a/package.json b/package.json new file mode 100644 index 0000000..be7ec6b --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "name": "config-service", + "private": true, + "workspaces": ["packages/*"] +} diff --git a/packages/cfg/.gitignore b/packages/cfg/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/packages/cfg/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/cfg/package.json b/packages/cfg/package.json new file mode 100644 index 0000000..dff3eb3 --- /dev/null +++ b/packages/cfg/package.json @@ -0,0 +1,17 @@ +{ + "name": "@shazhou/cfg", + "version": "1.0.0", + "type": "module", + "bin": { + "cfg": "./dist/cli.js" + }, + "files": ["dist"], + "scripts": { + "build": "bun build src/cli.ts --outfile dist/cli.js --target node --minify && { echo '#!/usr/bin/env node'; cat dist/cli.js; } > dist/cli.tmp && mv dist/cli.tmp dist/cli.js", + "prepublishOnly": "bun run build" + }, + "publishConfig": { + "access": "public" + }, + "license": "MIT" +} diff --git a/packages/cfg/src/api.ts b/packages/cfg/src/api.ts new file mode 100644 index 0000000..0c96a09 --- /dev/null +++ b/packages/cfg/src/api.ts @@ -0,0 +1,70 @@ +import { getToken, getEndpoint, loadCache, saveCache } from "./config.js"; + +export async function api( + method: string, + path: string, + body?: unknown +): Promise<{ data: any; status: number }> { + const url = `${getEndpoint()}${path}`; + const headers: Record = { + Authorization: `Bearer ${getToken()}`, + "Content-Type": "application/json", + }; + let res: Response; + try { + res = await fetch(url, { + method, + headers, + body: body !== undefined ? JSON.stringify(body) : undefined, + }); + } catch (err: any) { + console.error( + `Error: ${err?.cause?.message || err?.message || "network error"}` + ); + process.exit(1); + } + if (res.status === 204) return { data: null, status: 204 }; + const text = await res.text(); + let data: any; + try { + data = JSON.parse(text); + } catch { + data = text; + } + if (!res.ok) { + const msg = data?.error || text; + console.error(`Error: ${msg}`); + process.exit(1); + } + return { data, status: res.status }; +} + +export async function trySyncRemote(): Promise { + const url = `${getEndpoint()}/config`; + const headers: Record = { + Authorization: `Bearer ${getToken()}`, + "Content-Type": "application/json", + }; + try { + const res = await fetch(url, { method: "GET", headers }); + if (!res.ok) return false; + const data = await res.json() as any; + if (!data?.secrets) return false; + const now = new Date().toISOString(); + const cache = { + agent_id: data.agent_id, + secrets: data.secrets, + synced_at: now, + attempted_at: now, + }; + saveCache(cache); + return true; + } catch { + const cache = loadCache(); + if (cache) { + cache.attempted_at = new Date().toISOString(); + saveCache(cache); + } + return false; + } +} diff --git a/packages/cfg/src/cli.ts b/packages/cfg/src/cli.ts new file mode 100644 index 0000000..6f8171b --- /dev/null +++ b/packages/cfg/src/cli.ts @@ -0,0 +1,91 @@ +import { + cmdSync, + cmdEnv, + cmdGet, + cmdSet, + cmdUnset, + cmdFlags, + cmdList, + cmdToken, + cmdAdminAddUser, + cmdAdminRemoveUser, + cmdAdminListAgents, + cmdAdminRefreshToken, + cmdAdminInspect, + showHelp, +} from "./commands.js"; + +const [cmd, ...args] = process.argv.slice(2); + +switch (cmd) { + case "sync": + await cmdSync(); + break; + case "env": + await cmdEnv(); + break; + case "get": { + const remote = args.includes("--remote"); + const filtered = args.filter((a) => a !== "--remote"); + if (!filtered[0]) { + console.error("Usage: cfg get [--remote] "); + process.exit(1); + } + await cmdGet(filtered[0], remote); + break; + } + case "set": + await cmdSet(args); + break; + case "unset": + await cmdUnset(args); + break; + case "flags": + await cmdFlags(args); + break; + case "list": + await cmdList(); + break; + case "token": + if (!args[0]) { + console.error("Usage: cfg token "); + process.exit(1); + } + cmdToken(args[0]); + break; + case "admin": { + const [sub, ...subArgs] = args; + switch (sub) { + case "agents": + await cmdAdminListAgents(); + break; + case "add": + await cmdAdminAddUser(subArgs); + break; + case "remove": + await cmdAdminRemoveUser(subArgs); + break; + case "refresh": + await cmdAdminRefreshToken(subArgs); + break; + case "inspect": + await cmdAdminInspect(subArgs); + break; + default: + console.error(`Unknown admin command: ${sub}`); + showHelp(); + process.exit(1); + } + break; + } + case "help": + case "--help": + case "-h": + case undefined: + showHelp(); + break; + default: + console.error(`Unknown command: ${cmd}`); + showHelp(); + process.exit(1); +} diff --git a/cli/cfg.js b/packages/cfg/src/commands.ts old mode 100755 new mode 100644 similarity index 56% rename from cli/cfg.js rename to packages/cfg/src/commands.ts index 31f9282..ff3ec88 --- a/cli/cfg.js +++ b/packages/cfg/src/commands.ts @@ -1,119 +1,17 @@ -#!/usr/bin/env node +import { + loadConfig, + saveConfig, + loadCache, + saveCache, + ONE_DAY, + TWO_HOURS, + DEFAULT_ENDPOINT, + type Cache, +} from "./config.js"; +import { api, trySyncRemote } from "./api.js"; -// src/cli.ts -import { readFileSync, writeFileSync, mkdirSync } from "fs"; -import { join } from "path"; -import { homedir } from "os"; -var CONFIG_DIR = join(homedir(), ".config", "cfg"); -var CONFIG_FILE = join(CONFIG_DIR, "config.json"); -var CACHE_FILE = join(CONFIG_DIR, "cache.json"); -var DEFAULT_ENDPOINT = "https://config.shazhou.work"; -var ONE_DAY = 24 * 60 * 60 * 1000; -var TWO_HOURS = 2 * 60 * 60 * 1000; -function loadConfig() { - try { - return JSON.parse(readFileSync(CONFIG_FILE, "utf-8")); - } catch { - return {}; - } -} -function saveConfig(cfg) { - mkdirSync(CONFIG_DIR, { recursive: true }); - writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + ` -`); -} -function loadCache() { - try { - return JSON.parse(readFileSync(CACHE_FILE, "utf-8")); - } catch { - return null; - } -} -function saveCache(cache) { - mkdirSync(CONFIG_DIR, { recursive: true }); - writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2) + ` -`); -} -function getToken() { - const cfg = loadConfig(); - const token = process.env.CFG_TOKEN || cfg.token; - if (!token) { - console.error("No token configured. Run: cfg token "); - process.exit(1); - } - return token; -} -function getEndpoint() { - const cfg = loadConfig(); - return cfg.endpoint || process.env.CFG_ENDPOINT || DEFAULT_ENDPOINT; -} -async function api(method, path, body) { - const url = `${getEndpoint()}${path}`; - const headers = { - Authorization: `Bearer ${getToken()}`, - "Content-Type": "application/json" - }; - let res; - try { - res = await fetch(url, { - method, - headers, - body: body !== undefined ? JSON.stringify(body) : undefined - }); - } catch (err) { - console.error(`Error: ${err?.cause?.message || err?.message || "network error"}`); - process.exit(1); - } - if (res.status === 204) - return { data: null, status: 204 }; - const text = await res.text(); - let data; - try { - data = JSON.parse(text); - } catch { - data = text; - } - if (!res.ok) { - const msg = data?.error || text; - console.error(`Error: ${msg}`); - process.exit(1); - } - return { data, status: res.status }; -} -async function trySyncRemote() { - const url = `${getEndpoint()}/config`; - const headers = { - Authorization: `Bearer ${getToken()}`, - "Content-Type": "application/json" - }; - try { - const res = await fetch(url, { method: "GET", headers }); - if (!res.ok) - return false; - const data = await res.json(); - if (!data?.secrets) - return false; - const now = new Date().toISOString(); - const cache = { - agent_id: data.agent_id, - secrets: data.secrets, - synced_at: now, - attempted_at: now - }; - saveCache(cache); - return true; - } catch { - const cache = loadCache(); - if (cache) { - cache.attempted_at = new Date().toISOString(); - saveCache(cache); - } - return false; - } -} -function shouldAutoSync(cache) { - if (!cache) - return true; +export function shouldAutoSync(cache: Cache | null): boolean { + if (!cache) return true; const now = Date.now(); const syncedAt = new Date(cache.synced_at).getTime(); const attemptedAt = new Date(cache.attempted_at || cache.synced_at).getTime(); @@ -125,7 +23,8 @@ function shouldAutoSync(cache) { } return true; } -async function cmdSync() { + +export async function cmdSync(): Promise { const { data } = await api("GET", "/config"); if (!data?.secrets) { console.error("No secrets found"); @@ -136,13 +35,14 @@ async function cmdSync() { agent_id: data.agent_id, secrets: data.secrets, synced_at: now, - attempted_at: now + attempted_at: now, }; saveCache(cache); const count = Object.keys(cache.secrets).length; console.log(`✓ Synced ${count} keys (agent: ${cache.agent_id})`); } -async function cmdEnv() { + +export async function cmdEnv(): Promise { let cache = loadCache(); if (shouldAutoSync(cache)) { const ok = await trySyncRemote(); @@ -153,13 +53,16 @@ async function cmdEnv() { process.exit(1); } } - for (const [key, entry] of Object.entries(cache.secrets).sort(([a], [b]) => a.localeCompare(b))) { + for (const [key, entry] of Object.entries(cache!.secrets).sort(([a], [b]) => + a.localeCompare(b) + )) { if (entry.env === false) continue; const escaped = entry.value.replace(/'/g, "'\\''"); console.log(`export ${key}='${escaped}'`); } } -async function cmdGet(key, remote) { + +export async function cmdGet(key: string, remote: boolean): Promise { if (remote) { const { data } = await api("GET", `/config/${encodeURIComponent(key)}`); console.log(data.value); @@ -168,8 +71,7 @@ async function cmdGet(key, remote) { let cache = loadCache(); if (shouldAutoSync(cache)) { const ok = await trySyncRemote(); - if (ok) - cache = loadCache(); + if (ok) cache = loadCache(); else if (!cache) { console.error("No local cache and sync failed. Run: cfg sync"); process.exit(1); @@ -186,13 +88,18 @@ async function cmdGet(key, remote) { } console.log(entry.value); } -async function cmdSet(args) { + +export async function cmdSet(args: string[]): Promise { const shared = args.includes("--shared"); const noEnv = args.includes("--no-env"); const isSecret = args.includes("--secret"); - const filtered = args.filter((a) => !["--shared", "--no-env", "--secret"].includes(a)); + const filtered = args.filter( + (a) => !["--shared", "--no-env", "--secret"].includes(a) + ); if (filtered.length < 2) { - console.error("Usage: cfg set [--shared] [--no-env] [--secret] "); + console.error( + "Usage: cfg set [--shared] [--no-env] [--secret] " + ); process.exit(1); } const [key, ...rest] = filtered; @@ -200,18 +107,25 @@ async function cmdSet(args) { const scope = shared ? "shared" : "personal"; const body = { value, env: !noEnv, secret: isSecret }; await api("PUT", `/config/${encodeURIComponent(key)}?scope=${scope}`, body); - const flags = []; + const flags: string[] = []; if (noEnv) flags.push("no-env"); if (isSecret) flags.push("secret"); const flagStr = flags.length ? ` [${flags.join(", ")}]` : ""; console.log(`✓ ${key} (${scope})${flagStr}`); const cache = loadCache(); if (cache) { - cache.secrets[key] = { value, scope, env: !noEnv, secret: isSecret, updated_at: new Date().toISOString() }; + cache.secrets[key] = { + value, + scope, + env: !noEnv, + secret: isSecret, + updated_at: new Date().toISOString(), + }; saveCache(cache); } } -async function cmdUnset(args) { + +export async function cmdUnset(args: string[]): Promise { const shared = args.includes("--shared"); const filtered = args.filter((a) => a !== "--shared"); if (filtered.length < 1) { @@ -228,12 +142,12 @@ async function cmdUnset(args) { saveCache(cache); } } -async function cmdList() { + +export async function cmdList(): Promise { let cache = loadCache(); if (shouldAutoSync(cache)) { const ok = await trySyncRemote(); - if (ok) - cache = loadCache(); + if (ok) cache = loadCache(); else if (!cache) { console.error("No local cache and sync failed. Run: cfg sync"); process.exit(1); @@ -251,43 +165,95 @@ async function cmdList() { const maxLen = Math.max(...keys.map((k) => k.length)); for (const key of keys) { const entry = cache.secrets[key]; - const scope = entry.scope === "personal" ? "\x1B[32mpersonal\x1B[0m" : "\x1B[34mshared\x1B[0m "; - const flags = []; + const scope = + entry.scope === "personal" + ? "\x1B[32mpersonal\x1B[0m" + : "\x1B[34mshared\x1B[0m "; + const flags: string[] = []; if (entry.env === false) flags.push("\x1B[33mno-env\x1B[0m"); if (entry.secret) flags.push("\x1B[31msecret\x1B[0m"); const flagStr = flags.length ? " " + flags.join(" ") : ""; console.log(` ${key.padEnd(maxLen + 2)}${scope}${flagStr}`); } - console.log(` -Total: ${keys.length} keys (agent: ${cache.agent_id})`); + console.log(`\nTotal: ${keys.length} keys (agent: ${cache.agent_id})`); console.log(`Last sync: ${cache.synced_at}`); } -function cmdToken(token) { + +export function cmdToken(token: string): void { const cfg = loadConfig(); cfg.token = token; saveConfig(cfg); console.log("✓ Token saved"); } -async function cmdAdminAddUser(args) { + +export async function cmdFlags(args: string[]): Promise { + const shared = args.includes("--shared"); + const filtered = args.filter((a) => !["--shared"].includes(a)); + if (filtered.length < 1) { + console.error( + "Usage: cfg flags [--shared] [--env|--no-env] [--secret|--no-secret]" + ); + process.exit(1); + } + const key = filtered[0]; + const flagArgs = filtered.slice(1); + const body: Record = {}; + if (flagArgs.includes("--env")) body.env = true; + if (flagArgs.includes("--no-env")) body.env = false; + if (flagArgs.includes("--secret")) body.secret = true; + if (flagArgs.includes("--no-secret")) body.secret = false; + if (Object.keys(body).length === 0) { + // Just show current flags + const { data } = await api("GET", `/config/${encodeURIComponent(key)}`); + console.log( + `${key}: env=${data.env ?? true}, secret=${data.secret ?? false} (${data.scope})` + ); + return; + } + const scope = shared ? "shared" : "personal"; + const { data } = await api( + "PATCH", + `/config/${encodeURIComponent(key)}?scope=${scope}`, + body + ); + console.log(`✓ ${key} flags updated: env=${data.env}, secret=${data.secret}`); + // Update cache + const cache = loadCache(); + if (cache && cache.secrets[key]) { + if (body.env !== undefined) cache.secrets[key].env = body.env; + if (body.secret !== undefined) cache.secrets[key].secret = body.secret; + saveCache(cache); + } +} + +export async function cmdAdminAddUser(args: string[]): Promise { const role = args.includes("--admin") ? "admin" : "agent"; const filtered = args.filter((a) => a !== "--admin"); if (!filtered[0]) { console.error("Usage: cfg admin add [--admin]"); process.exit(1); } - const { data } = await api("POST", "/admin/token", { agent_id: filtered[0], role }); + const { data } = await api("POST", "/admin/token", { + agent_id: filtered[0], + role, + }); console.log(`✓ Created ${data.role} token for ${data.agent_id}`); console.log(` Token: ${data.token}`); } -async function cmdAdminRemoveUser(args) { + +export async function cmdAdminRemoveUser(args: string[]): Promise { if (!args[0]) { console.error("Usage: cfg admin remove "); process.exit(1); } - const { data } = await api("DELETE", `/admin/token/${encodeURIComponent(args[0])}`); + const { data } = await api( + "DELETE", + `/admin/token/${encodeURIComponent(args[0])}` + ); console.log(`✓ Revoked ${data.tokens_revoked} token(s) for ${data.agent_id}`); } -async function cmdAdminListAgents() { + +export async function cmdAdminListAgents(): Promise { const { data } = await api("GET", "/admin/agents"); if (!data.agents?.length) { console.log("No agents found"); @@ -298,7 +264,8 @@ async function cmdAdminListAgents() { console.log(` ${agent}`); } } -async function cmdAdminRefreshToken(args) { + +export async function cmdAdminRefreshToken(args: string[]): Promise { if (!args[0]) { console.error("Usage: cfg admin refresh "); process.exit(1); @@ -309,12 +276,16 @@ async function cmdAdminRefreshToken(args) { console.log(`✓ Refreshed token for ${data.agent_id}`); console.log(` Token: ${data.token}`); } -async function cmdAdminInspect(args) { + +export async function cmdAdminInspect(args: string[]): Promise { if (!args[0]) { console.error("Usage: cfg admin inspect "); process.exit(1); } - const { data } = await api("GET", `/admin/agent/${encodeURIComponent(args[0])}`); + const { data } = await api( + "GET", + `/admin/agent/${encodeURIComponent(args[0])}` + ); if (!data.secrets || Object.keys(data.secrets).length === 0) { console.log(`No keys for ${data.agent_id}`); return; @@ -323,44 +294,16 @@ async function cmdAdminInspect(args) { const maxLen = Math.max(...keys.map((k) => k.length)); for (const key of keys) { const entry = data.secrets[key]; - const scope = entry.scope === "personal" ? "\x1B[32mpersonal\x1B[0m" : "\x1B[34mshared\x1B[0m "; + const scope = + entry.scope === "personal" + ? "\x1B[32mpersonal\x1B[0m" + : "\x1B[34mshared\x1B[0m "; console.log(` ${key.padEnd(maxLen + 2)}${scope}`); } - console.log(` -Total: ${keys.length} keys (agent: ${data.agent_id})`); + console.log(`\nTotal: ${keys.length} keys (agent: ${data.agent_id})`); } -async function cmdFlags(args) { - const shared = args.includes("--shared"); - const filtered = args.filter((a) => !["--shared"].includes(a)); - if (filtered.length < 1) { - console.error("Usage: cfg flags [--shared] [--env|--no-env] [--secret|--no-secret]"); - process.exit(1); - } - const key = filtered[0]; - const flagArgs = filtered.slice(1); - const body = {}; - if (flagArgs.includes("--env")) body.env = true; - if (flagArgs.includes("--no-env")) body.env = false; - if (flagArgs.includes("--secret")) body.secret = true; - if (flagArgs.includes("--no-secret")) body.secret = false; - if (Object.keys(body).length === 0) { - // Just show current flags - const { data } = await api("GET", `/config/${encodeURIComponent(key)}`); - console.log(`${key}: env=${data.env ?? true}, secret=${data.secret ?? false} (${data.scope})`); - return; - } - const scope = shared ? "shared" : "personal"; - const { data } = await api("PATCH", `/config/${encodeURIComponent(key)}?scope=${scope}`, body); - console.log(`✓ ${key} flags updated: env=${data.env}, secret=${data.secret}`); - // Update cache - const cache = loadCache(); - if (cache && cache.secrets[key]) { - if (body.env !== undefined) cache.secrets[key].env = body.env; - if (body.secret !== undefined) cache.secrets[key].secret = body.secret; - saveCache(cache); - } -} -function showHelp() { + +export function showHelp(): void { console.log(`cfg — config.shazhou.work CLI Usage: @@ -399,76 +342,3 @@ Environment: Shell setup: eval $(cfg env) Add to .profile / .bashrc / .zshrc`); } -var [cmd, ...args] = process.argv.slice(2); -switch (cmd) { - case "sync": - await cmdSync(); - break; - case "env": - await cmdEnv(); - break; - case "get": { - const remote = args.includes("--remote"); - const filtered = args.filter((a) => a !== "--remote"); - if (!filtered[0]) { - console.error("Usage: cfg get [--remote] "); - process.exit(1); - } - await cmdGet(filtered[0], remote); - break; - } - case "set": - await cmdSet(args); - break; - case "unset": - await cmdUnset(args); - break; - case "flags": - await cmdFlags(args); - break; - case "list": - await cmdList(); - break; - case "token": - if (!args[0]) { - console.error("Usage: cfg token "); - process.exit(1); - } - cmdToken(args[0]); - break; - case "admin": { - const [sub, ...subArgs] = args; - switch (sub) { - case "agents": - await cmdAdminListAgents(); - break; - case "add": - await cmdAdminAddUser(subArgs); - break; - case "remove": - await cmdAdminRemoveUser(subArgs); - break; - case "refresh": - await cmdAdminRefreshToken(subArgs); - break; - case "inspect": - await cmdAdminInspect(subArgs); - break; - default: - console.error(`Unknown admin command: ${sub}`); - showHelp(); - process.exit(1); - } - break; - } - case "help": - case "--help": - case "-h": - case undefined: - showHelp(); - break; - default: - console.error(`Unknown command: ${cmd}`); - showHelp(); - process.exit(1); -} diff --git a/packages/cfg/src/config.ts b/packages/cfg/src/config.ts new file mode 100644 index 0000000..a2fd09b --- /dev/null +++ b/packages/cfg/src/config.ts @@ -0,0 +1,73 @@ +import { readFileSync, writeFileSync, mkdirSync } from "fs"; +import { join } from "path"; +import { homedir } from "os"; + +export const CONFIG_DIR = join(homedir(), ".config", "cfg"); +export const CONFIG_FILE = join(CONFIG_DIR, "config.json"); +export const CACHE_FILE = join(CONFIG_DIR, "cache.json"); +export const DEFAULT_ENDPOINT = "https://config.shazhou.work"; +export const ONE_DAY = 24 * 60 * 60 * 1000; +export const TWO_HOURS = 2 * 60 * 60 * 1000; + +export interface Config { + token?: string; + endpoint?: string; + [key: string]: unknown; +} + +export interface SecretEntry { + value: string; + scope?: string; + env?: boolean; + secret?: boolean; + updated_at?: string; + [key: string]: unknown; +} + +export interface Cache { + agent_id: string; + secrets: Record; + synced_at: string; + attempted_at: string; +} + +export function loadConfig(): Config { + try { + return JSON.parse(readFileSync(CONFIG_FILE, "utf-8")); + } catch { + return {}; + } +} + +export function saveConfig(cfg: Config): void { + mkdirSync(CONFIG_DIR, { recursive: true }); + writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n"); +} + +export function loadCache(): Cache | null { + try { + return JSON.parse(readFileSync(CACHE_FILE, "utf-8")); + } catch { + return null; + } +} + +export function saveCache(cache: Cache): void { + mkdirSync(CONFIG_DIR, { recursive: true }); + writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2) + "\n"); +} + +export function getToken(): string { + const cfg = loadConfig(); + const token = process.env.CFG_TOKEN || cfg.token; + if (!token) { + console.error("No token configured. Run: cfg token "); + process.exit(1); + } + return token; +} + +export function getEndpoint(): string { + const cfg = loadConfig(); + return cfg.endpoint || process.env.CFG_ENDPOINT || DEFAULT_ENDPOINT; +} diff --git a/packages/cfg/tsconfig.json b/packages/cfg/tsconfig.json new file mode 100644 index 0000000..bb7a5c0 --- /dev/null +++ b/packages/cfg/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/worker/src/index.ts b/packages/worker/src/index.ts similarity index 100% rename from worker/src/index.ts rename to packages/worker/src/index.ts diff --git a/packages/worker/src/ui.ts b/packages/worker/src/ui.ts new file mode 100644 index 0000000..9b6ee98 --- /dev/null +++ b/packages/worker/src/ui.ts @@ -0,0 +1,381 @@ +export function renderUI(): string { + return ` + + + + +Config Service + + + + + + +
+
+

🔑 Config Service

+
+ + + +
+
+ + + +
+ + +
+ + +
+
+ + + +
KeyValueScopeUpdatedActions
+
+
+ + + +
+ + + +`; +} diff --git a/worker/wrangler.toml b/packages/worker/wrangler.toml similarity index 100% rename from worker/wrangler.toml rename to packages/worker/wrangler.toml diff --git a/scripts/register_agent.py b/scripts/register_agent.py deleted file mode 100644 index 23e3521..0000000 --- a/scripts/register_agent.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -"""Register an agent token in the config service KV. - -Usage: python3 register_agent.py [token] - -If token is omitted, a random one is generated. -Outputs the KV entry to add via wrangler CLI. -""" - -import hashlib -import json -import secrets -import sys - - -def main(): - if len(sys.argv) < 3: - print(f"Usage: {sys.argv[0]} [token]") - print(" role: agent or admin") - sys.exit(1) - - agent_id = sys.argv[1] - role = sys.argv[2] - token = sys.argv[3] if len(sys.argv) > 3 else secrets.token_urlsafe(32) - - if role not in ("agent", "admin"): - print("Role must be 'agent' or 'admin'", file=sys.stderr) - sys.exit(1) - - token_hash = hashlib.sha256(token.encode()).hexdigest() - entry = json.dumps({"agent_id": agent_id, "role": role}) - - print(f"Agent ID: {agent_id}") - print(f"Role: {role}") - print(f"Token: {token}") - print(f"Token hash: {token_hash}") - print() - print("Add to KV:") - print(f' wrangler kv key put --binding CONFIG_KV "auth:{token_hash}" \'{entry}\'') - - -if __name__ == "__main__": - main()