new file: script/seed.ts

This commit is contained in:
MrPiglr 2025-12-24 17:45:22 +00:00
parent f97122135d
commit 670aec8068
4 changed files with 188 additions and 0 deletions

View file

@ -6,6 +6,7 @@ import { ArrowLeft, Terminal as TerminalIcon, Copy, Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";
import { useToast } from "@/hooks/use-toast";
interface TerminalLine {
type: 'input' | 'output' | 'error' | 'system';
@ -27,6 +28,7 @@ export default function Terminal() {
const [cliLabel, setCliLabel] = useState<string>("");
const eventSourceRef = useRef<EventSource | null>(null);
const currentRunId = useRef<string | null>(null);
const { toast } = useToast();
useEffect(() => {
return () => {
@ -188,6 +190,7 @@ export default function Terminal() {
if (cliStatus === "running") return;
setCliStatus("running");
appendCliLine('system', `▸ Running ${cliCommand}...`);
toast({ title: "CLI", description: `Started ${cliCommand}`, variant: "default" });
try {
const res = await fetch('/api/admin/cli/start', {
@ -201,6 +204,7 @@ export default function Terminal() {
const text = await res.text();
appendCliLine('error', `Start failed: ${text || res.status}`);
setCliStatus("error");
toast({ title: "CLI Error", description: `Failed to start ${cliCommand}`, variant: "destructive" });
return;
}
@ -218,6 +222,7 @@ export default function Terminal() {
es.addEventListener('error', (evt) => {
appendCliLine('error', 'Stream error');
setCliStatus("error");
toast({ title: "CLI Error", description: `Stream error for ${cliCommand}`, variant: "destructive" });
es.close();
});
@ -225,6 +230,7 @@ export default function Terminal() {
const status = evt.data === 'success' ? 'done' : 'error';
setCliStatus(status as any);
appendCliLine(status === 'done' ? 'system' : 'error', `${cliLabel || cliCommand} ${status}`);
toast({ title: status === 'done' ? "CLI Success" : "CLI Failed", description: `${cliLabel || cliCommand} ${status}` });
es.close();
currentRunId.current = null;
});
@ -232,6 +238,7 @@ export default function Terminal() {
} catch (err) {
appendCliLine('error', 'Failed to start CLI command');
setCliStatus("error");
toast({ title: "CLI Error", description: `Failed to start ${cliCommand}`, variant: "destructive" });
}
};

View file

@ -0,0 +1,141 @@
# Entitlements Quickstart
This guide helps you set up the OS Kernel (identity + entitlements) and use the API to issue, verify, resolve, and revoke entitlements.
## 1) Run OS Kernel Migration
Ensure `DATABASE_URL` is set in your environment (Railway/Supabase Postgres). Then run the OS Kernel migration. You can do this either from the dev shell or the in-app Terminal (Admin only).
- Dev shell:
- `npx ts-node script/run-os-migration.ts`
- In-app Terminal:
- Command: `migrate-os` (now available in the command dropdown)
This creates tables:
- `aethex_subjects`, `aethex_subject_identities`
- `aethex_issuers`, `aethex_issuer_keys`
- `aethex_entitlements`, `aethex_entitlement_events`
- `aethex_audit_log`
## 2) Create an Issuer
Insert an issuer record with a `public_key` and optional metadata. You can use SQL or your DB console (Supabase) for now.
Example SQL:
```sql
INSERT INTO public.aethex_issuers (name, issuer_class, scopes, public_key, is_active)
VALUES ('AeThex Platform', 'platform', '["issue","revoke"]'::json, 'PUBLIC_KEY_STRING', true)
RETURNING id;
```
Save the returned `id` as your `issuer_id`.
## 3) Issue an Entitlement
Endpoint: `POST /api/os/entitlements/issue`
Headers:
- `x-issuer-id: <issuer_id>`
Body:
```json
{
"subject_id": "<optional: internal subject id>",
"external_subject_ref": "roblox:12345",
"entitlement_type": "achievement",
"scope": "project",
"data": { "name": "Alpha Access" },
"expires_at": null
}
```
Response:
```json
{
"success": true,
"entitlement": {
"id": "...",
"type": "achievement",
"scope": "project",
"created_at": "..."
}
}
```
## 4) Verify an Entitlement
Endpoint: `POST /api/os/entitlements/verify`
Body:
```json
{ "entitlement_id": "..." }
```
Response (valid):
```json
{
"valid": true,
"entitlement": {
"id": "...",
"type": "achievement",
"scope": "project",
"data": { "name": "Alpha Access" },
"issuer": { "id": "...", "name": "AeThex Platform", "class": "platform" },
"issued_at": "...",
"expires_at": null
}
}
```
Response (revoked/expired):
```json
{ "valid": false, "reason": "revoked|expired", ... }
```
## 5) Resolve Entitlements (by subject or external ref)
Endpoint: `GET /api/os/entitlements/resolve`
Query params (choose one path):
- `?subject_id=<internal_subject_id>`
- `?platform=roblox&id=12345`
Response:
```json
{
"entitlements": [
{
"id": "...",
"type": "achievement",
"scope": "project",
"data": { "name": "Alpha Access" },
"issuer": { "name": "AeThex Platform", "class": "platform" },
"issued_at": "...",
"expires_at": null
}
]
}
```
## 6) Revoke an Entitlement
Endpoint: `POST /api/os/entitlements/revoke`
Headers:
- `x-issuer-id: <issuer_id>`
Body:
```json
{ "entitlement_id": "...", "reason": "Fraudulent use" }
```
Response:
```json
{ "success": true, "message": "Entitlement revoked" }
```
## Notes
- All OS routes are protected by the capability guard and expect authenticated context where relevant.
- Use Supabase console to inspect tables and audit logs.
- For production, plan issuer key rotation via `aethex_issuer_keys`; rotation endpoints can be added similarly.

39
script/seed.ts Normal file
View file

@ -0,0 +1,39 @@
import dotenv from "dotenv";
import pkg from "pg";
dotenv.config();
const { Client } = pkg as any;
async function main() {
const client = new Client({ connectionString: process.env.DATABASE_URL, ssl: { rejectUnauthorized: false } });
await client.connect();
try {
console.log("Seeding default issuer if missing...");
const name = "AeThex Platform";
const existing = await client.query(
'SELECT id FROM public.aethex_issuers WHERE name = $1 LIMIT 1;',
[name]
);
if (existing.rows.length) {
console.log(`Issuer exists: ${existing.rows[0].id}`);
} else {
const insert = await client.query(
'INSERT INTO public.aethex_issuers (name, issuer_class, scopes, public_key, is_active) VALUES ($1, $2, $3::json, $4, $5) RETURNING id;',
[name, 'platform', JSON.stringify(["issue","revoke"]), 'PUBLIC_KEY_STRING', true]
);
console.log(`Issuer created: ${insert.rows[0].id}`);
}
console.log("Done.");
} catch (err: any) {
console.error("Seed failed:", err.message || err);
process.exit(1);
} finally {
await client.end();
}
}
main();

View file

@ -47,6 +47,7 @@ export async function registerRoutes(
build: { cmd: "npm", args: ["run", "build"], label: "npm run build" },
"migrate-status": { cmd: "npx", args: ["drizzle-kit", "status"], label: "drizzle status" },
migrate: { cmd: "npx", args: ["drizzle-kit", "migrate:push"], label: "drizzle migrate" },
"migrate-os": { cmd: "npx", args: ["ts-node", "script/run-os-migration.ts"], label: "os kernel migrate" },
seed: { cmd: "npx", args: ["ts-node", "script/seed.ts"], label: "seed" },
test: { cmd: "bash", args: ["./test-implementation.sh"], label: "test-implementation" },
};