@wopr-network/platform-ui-core 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wopr-network/platform-ui-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Brand-agnostic AI agent platform UI — deploy as any brand via env vars",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/wopr-network/platform-ui-core.git"
|
|
8
8
|
},
|
|
9
9
|
"license": "UNLICENSED",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/lib/index.ts",
|
|
12
|
+
"./app/*": "./src/app/*",
|
|
13
|
+
"./components/*": "./src/components/*",
|
|
14
|
+
"./hooks/*": "./src/hooks/*",
|
|
15
|
+
"./lib/*": "./src/lib/*",
|
|
16
|
+
"./globals.css": "./src/app/globals.css",
|
|
17
|
+
"./proxy": "./src/proxy.ts"
|
|
18
|
+
},
|
|
10
19
|
"publishConfig": {
|
|
11
20
|
"registry": "https://registry.npmjs.org",
|
|
12
21
|
"access": "public"
|
|
@@ -20,8 +29,7 @@
|
|
|
20
29
|
"tailwind.config.ts",
|
|
21
30
|
"biome.json",
|
|
22
31
|
"vitest.config.ts",
|
|
23
|
-
".env.wopr"
|
|
24
|
-
".env.paperclip"
|
|
32
|
+
".env.wopr"
|
|
25
33
|
],
|
|
26
34
|
"dependencies": {
|
|
27
35
|
"@hookform/resolvers": "^5.2.2",
|
|
@@ -67,11 +67,9 @@ import {
|
|
|
67
67
|
renameInstance,
|
|
68
68
|
restoreSnapshot,
|
|
69
69
|
toggleInstancePlugin,
|
|
70
|
-
updateInstanceBudget,
|
|
71
70
|
updateInstanceConfig,
|
|
72
71
|
updateInstanceSecrets,
|
|
73
72
|
} from "@/lib/api";
|
|
74
|
-
import { getBrandConfig } from "@/lib/brand-config";
|
|
75
73
|
import { toUserMessage } from "@/lib/errors";
|
|
76
74
|
import { cn } from "@/lib/utils";
|
|
77
75
|
|
|
@@ -118,10 +116,6 @@ export function InstanceDetailClient({ instanceId }: { instanceId: string }) {
|
|
|
118
116
|
const [renaming, setRenaming] = useState(false);
|
|
119
117
|
const [renameValue, setRenameValue] = useState("");
|
|
120
118
|
const [renameSaving, setRenameSaving] = useState(false);
|
|
121
|
-
const [budgetCents, setBudgetCents] = useState<number>(0);
|
|
122
|
-
const [budgetSaving, setBudgetSaving] = useState(false);
|
|
123
|
-
const [budgetError, setBudgetError] = useState<string | null>(null);
|
|
124
|
-
|
|
125
119
|
useEffect(() => {
|
|
126
120
|
if (!configText.trim()) return;
|
|
127
121
|
try {
|
|
@@ -193,7 +187,6 @@ export function InstanceDetailClient({ instanceId }: { instanceId: string }) {
|
|
|
193
187
|
const data = await getInstance(instanceId);
|
|
194
188
|
setInstance(data);
|
|
195
189
|
setConfigText(JSON.stringify(data.config, null, 2));
|
|
196
|
-
if (data.budgetCents !== undefined) setBudgetCents(data.budgetCents);
|
|
197
190
|
} catch (err) {
|
|
198
191
|
setError(toUserMessage(err, "Failed to load instance"));
|
|
199
192
|
} finally {
|
|
@@ -306,19 +299,6 @@ export function InstanceDetailClient({ instanceId }: { instanceId: string }) {
|
|
|
306
299
|
}
|
|
307
300
|
}
|
|
308
301
|
|
|
309
|
-
async function handleSaveBudget() {
|
|
310
|
-
setBudgetSaving(true);
|
|
311
|
-
setBudgetError(null);
|
|
312
|
-
try {
|
|
313
|
-
await updateInstanceBudget(instanceId, budgetCents);
|
|
314
|
-
await load();
|
|
315
|
-
} catch (err) {
|
|
316
|
-
setBudgetError(toUserMessage(err, "Failed to update budget"));
|
|
317
|
-
} finally {
|
|
318
|
-
setBudgetSaving(false);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
302
|
async function handleCreateSnapshot() {
|
|
323
303
|
setSnapshotsError(null);
|
|
324
304
|
setCreating(true);
|
|
@@ -496,16 +476,6 @@ export function InstanceDetailClient({ instanceId }: { instanceId: string }) {
|
|
|
496
476
|
</Badge>
|
|
497
477
|
)}
|
|
498
478
|
<span>{instance.provider}</span>
|
|
499
|
-
{instance.subdomain && (
|
|
500
|
-
<a
|
|
501
|
-
href={`https://${instance.subdomain}.${getBrandConfig().domain}`}
|
|
502
|
-
target="_blank"
|
|
503
|
-
rel="noopener noreferrer"
|
|
504
|
-
className="font-mono text-xs text-terminal hover:underline"
|
|
505
|
-
>
|
|
506
|
-
{instance.subdomain}.{getBrandConfig().domain}
|
|
507
|
-
</a>
|
|
508
|
-
)}
|
|
509
479
|
</div>
|
|
510
480
|
</div>
|
|
511
481
|
<div className="flex gap-2">
|
|
@@ -599,48 +569,6 @@ export function InstanceDetailClient({ instanceId }: { instanceId: string }) {
|
|
|
599
569
|
<MetricCard title="Active Sessions" value={String(instance.sessions.length)} />
|
|
600
570
|
<MetricCard title="Created" value={new Date(instance.createdAt).toLocaleDateString()} />
|
|
601
571
|
</div>
|
|
602
|
-
|
|
603
|
-
{/* Budget management — shown when backend provides budget data */}
|
|
604
|
-
{instance.budgetCents !== undefined && (
|
|
605
|
-
<Card className="mt-4">
|
|
606
|
-
<CardHeader className="pb-3">
|
|
607
|
-
<CardTitle className="text-sm font-medium">Spending Budget</CardTitle>
|
|
608
|
-
</CardHeader>
|
|
609
|
-
<CardContent className="space-y-4">
|
|
610
|
-
<div className="flex items-center justify-between">
|
|
611
|
-
<span className="text-2xl font-bold font-mono">
|
|
612
|
-
${(budgetCents / 100).toFixed(2)}
|
|
613
|
-
</span>
|
|
614
|
-
<span className="text-xs text-muted-foreground">per month</span>
|
|
615
|
-
</div>
|
|
616
|
-
<input
|
|
617
|
-
type="range"
|
|
618
|
-
min={0}
|
|
619
|
-
max={10000}
|
|
620
|
-
step={100}
|
|
621
|
-
value={budgetCents}
|
|
622
|
-
onChange={(e) => setBudgetCents(Number(e.target.value))}
|
|
623
|
-
className="w-full accent-terminal"
|
|
624
|
-
aria-label="Budget slider"
|
|
625
|
-
/>
|
|
626
|
-
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
|
627
|
-
<span>$0</span>
|
|
628
|
-
<span>$100</span>
|
|
629
|
-
</div>
|
|
630
|
-
{budgetError && <p className="text-sm text-destructive">{budgetError}</p>}
|
|
631
|
-
<div className="flex justify-end">
|
|
632
|
-
<Button
|
|
633
|
-
size="sm"
|
|
634
|
-
variant="terminal"
|
|
635
|
-
onClick={handleSaveBudget}
|
|
636
|
-
disabled={budgetSaving || budgetCents === instance.budgetCents}
|
|
637
|
-
>
|
|
638
|
-
{budgetSaving ? "Saving..." : "Update Budget"}
|
|
639
|
-
</Button>
|
|
640
|
-
</div>
|
|
641
|
-
</CardContent>
|
|
642
|
-
</Card>
|
|
643
|
-
)}
|
|
644
572
|
</TabsContent>
|
|
645
573
|
|
|
646
574
|
{/* Health Tab */}
|
|
@@ -25,7 +25,7 @@ const panelVariants = {
|
|
|
25
25
|
opacity: 1,
|
|
26
26
|
y: 0,
|
|
27
27
|
scale: 1,
|
|
28
|
-
transition: { type: "spring", damping: 25, stiffness: 300 },
|
|
28
|
+
transition: { type: "spring" as const, damping: 25, stiffness: 300 },
|
|
29
29
|
},
|
|
30
30
|
exit: { opacity: 0, y: 20, scale: 0.95, transition: { duration: 0.15 } },
|
|
31
31
|
};
|
package/src/lib/api.ts
CHANGED
|
@@ -61,8 +61,6 @@ export interface Instance {
|
|
|
61
61
|
plugins: PluginInfo[];
|
|
62
62
|
uptime: number | null;
|
|
63
63
|
createdAt: string;
|
|
64
|
-
subdomain?: string;
|
|
65
|
-
nodeId?: string;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
export interface PluginInfo {
|
|
@@ -95,8 +93,6 @@ export interface InstanceDetail extends Instance {
|
|
|
95
93
|
memoryMb: number;
|
|
96
94
|
cpuPercent: number;
|
|
97
95
|
};
|
|
98
|
-
budgetCents?: number;
|
|
99
|
-
perAgentCents?: number;
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
// --- API client ---
|
|
@@ -323,7 +319,6 @@ export async function getInstance(id: string): Promise<InstanceDetail> {
|
|
|
323
319
|
id,
|
|
324
320
|
})) as BotStatusResponse;
|
|
325
321
|
const uptimeMs = bot.uptime ? new Date(bot.uptime).getTime() : NaN;
|
|
326
|
-
const extra = bot as Record<string, unknown>;
|
|
327
322
|
return {
|
|
328
323
|
id: bot.id,
|
|
329
324
|
name: bot.name,
|
|
@@ -333,8 +328,6 @@ export async function getInstance(id: string): Promise<InstanceDetail> {
|
|
|
333
328
|
plugins: parsePluginsFromEnv(bot.env as Record<string, string> | undefined),
|
|
334
329
|
uptime: Number.isNaN(uptimeMs) ? null : Math.floor((Date.now() - uptimeMs) / 1000),
|
|
335
330
|
createdAt: (bot.createdAt as string | undefined) ?? new Date().toISOString(),
|
|
336
|
-
subdomain: (extra.subdomain as string | undefined) ?? undefined,
|
|
337
|
-
nodeId: (extra.nodeId as string | undefined) ?? undefined,
|
|
338
331
|
config: bot.env ?? {},
|
|
339
332
|
channelDetails: [],
|
|
340
333
|
sessions: [],
|
|
@@ -342,8 +335,6 @@ export async function getInstance(id: string): Promise<InstanceDetail> {
|
|
|
342
335
|
memoryMb: bot.stats?.memoryUsageMb ?? 0,
|
|
343
336
|
cpuPercent: bot.stats?.cpuPercent ?? 0,
|
|
344
337
|
},
|
|
345
|
-
budgetCents: typeof extra.budgetCents === "number" ? extra.budgetCents : undefined,
|
|
346
|
-
perAgentCents: typeof extra.perAgentCents === "number" ? extra.perAgentCents : undefined,
|
|
347
338
|
};
|
|
348
339
|
}
|
|
349
340
|
|
|
@@ -471,18 +462,6 @@ export async function updateInstanceConfig(id: string, env: Record<string, strin
|
|
|
471
462
|
});
|
|
472
463
|
}
|
|
473
464
|
|
|
474
|
-
/** PUT /api/provision/budget — Update instance spending budget. */
|
|
475
|
-
export async function updateInstanceBudget(
|
|
476
|
-
id: string,
|
|
477
|
-
budgetCents: number,
|
|
478
|
-
perAgentCents?: number,
|
|
479
|
-
): Promise<void> {
|
|
480
|
-
await apiFetch("/provision/budget", {
|
|
481
|
-
method: "PUT",
|
|
482
|
-
body: JSON.stringify({ instanceId: id, budgetCents, perAgentCents }),
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
465
|
/** PATCH /fleet/bots/:id — Rename a bot instance. */
|
|
487
466
|
export async function renameInstance(id: string, name: string): Promise<void> {
|
|
488
467
|
await fleetFetch(`/bots/${id}`, {
|
package/.env.paperclip
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Paperclip Brand Configuration
|
|
2
|
-
# Copy to .env.local to deploy as Paperclip
|
|
3
|
-
NEXT_PUBLIC_BRAND_PRODUCT_NAME="Paperclip"
|
|
4
|
-
NEXT_PUBLIC_BRAND_NAME="Paperclip"
|
|
5
|
-
NEXT_PUBLIC_BRAND_DOMAIN="runpaperclip.com"
|
|
6
|
-
NEXT_PUBLIC_BRAND_APP_DOMAIN="app.runpaperclip.com"
|
|
7
|
-
NEXT_PUBLIC_BRAND_TAGLINE="AI agents that run your business."
|
|
8
|
-
NEXT_PUBLIC_BRAND_EMAIL_PRIVACY="privacy@runpaperclip.com"
|
|
9
|
-
NEXT_PUBLIC_BRAND_EMAIL_LEGAL="legal@runpaperclip.com"
|
|
10
|
-
NEXT_PUBLIC_BRAND_EMAIL_SUPPORT="support@runpaperclip.com"
|
|
11
|
-
NEXT_PUBLIC_BRAND_DEFAULT_IMAGE="ghcr.io/wopr-network/wopr:latest"
|
|
12
|
-
NEXT_PUBLIC_BRAND_STORAGE_PREFIX="paperclip"
|
|
13
|
-
NEXT_PUBLIC_BRAND_EVENT_PREFIX="paperclip"
|
|
14
|
-
NEXT_PUBLIC_BRAND_ENV_PREFIX="PAPERCLIP"
|
|
15
|
-
NEXT_PUBLIC_BRAND_TOOL_PREFIX="paperclip"
|
|
16
|
-
NEXT_PUBLIC_BRAND_TENANT_COOKIE="paperclip_tenant_id"
|
|
17
|
-
NEXT_PUBLIC_BRAND_COMPANY_LEGAL="Paperclip AI Inc."
|
|
18
|
-
NEXT_PUBLIC_BRAND_PRICE="$5/month"
|