+
+
+
API Tokens
+
+ Bearer tokens for authenticating API calls to this workspace.
+
+
+
+
+
+ {/* Newly created token — show once */}
+ {newToken && (
+
+
+ New Token Created
+ Copy now — it won't be shown again
+
+
+
+ {newToken}
+
+
+
+
+
+ )}
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {/* Token list */}
+ {loading ? (
+
+ Loading tokens...
+
+ ) : tokens.length === 0 ? (
+
+
No active tokens
+
+ Create a token to authenticate API calls.
+
+
+ ) : (
+
+ {tokens.map((t) => (
+
+
+
+ {t.prefix}...
+
+
+ Created {formatAge(t.created_at)}
+ {t.last_used_at && (
+ Last used {formatAge(t.last_used_at)}
+ )}
+
+
+
+
+ ))}
+
+ )}
+
+ {/* Revoke confirmation */}
+
revokeTarget && handleRevoke(revokeTarget)}
+ onCancel={() => setRevokeTarget(null)}
+ />
+
+ );
+}
+
+function formatAge(timestamp: string): string {
+ const diff = Date.now() - new Date(timestamp).getTime();
+ if (diff < 60000) return 'just now';
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
+ return `${Math.floor(diff / 86400000)}d ago`;
+}
diff --git a/canvas/src/components/tabs/TerminalTab.tsx b/canvas/src/components/tabs/TerminalTab.tsx
index f306b63a..835cface 100644
--- a/canvas/src/components/tabs/TerminalTab.tsx
+++ b/canvas/src/components/tabs/TerminalTab.tsx
@@ -6,18 +6,9 @@ interface Props {
workspaceId: string;
}
-// Derive base WebSocket URL (without /ws path) for terminal connections.
-const WS_URL = (() => {
- const explicit = process.env.NEXT_PUBLIC_WS_URL;
- if (explicit) return explicit.replace("/ws", "");
- const platform = process.env.NEXT_PUBLIC_PLATFORM_URL;
- if (platform) return platform.replace(/^http/, "ws");
- if (typeof window !== "undefined") {
- const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
- return `${proto}//${window.location.host}`;
- }
- return "ws://localhost:8080";
-})();
+import { deriveWsBaseUrl } from "@/lib/ws-url";
+
+const WS_URL = deriveWsBaseUrl();
export function TerminalTab({ workspaceId }: Props) {
const containerRef = useRef