Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7c84c3b
Initial plan
Claude May 22, 2026
f1a37df
Initial plan
Claude May 22, 2026
5f97e8e
fix(sandbox-ai): graceful fallback when Clerk publishable key is missing
Huynhthuongg May 22, 2026
071a51c
feat(sandbox-ai): add auth mode switch and health-config diagnostics
Huynhthuongg May 22, 2026
a8970a7
Merge pull request #21 from Huynhthuongg/codex/explain-codebase-struc…
Huynhthuongg May 22, 2026
1646f3f
Merge pull request #20 from Huynhthuongg/claude/feature-tinh-nang
Huynhthuongg May 22, 2026
8a58d37
Merge pull request #18 from Huynhthuongg/claude/design-black-screen-t…
Huynhthuongg May 22, 2026
2b13095
Merge pull request #26 from Huynhthuongg/main
Huynhthuongg May 22, 2026
eb7024e
Initial plan
Claude May 23, 2026
8aea644
Merge pull request #32 from Huynhthuongg/claude/grant-execute-permiss…
Huynhthuongg May 23, 2026
fd19a0e
Merge pull request #30 from Huynhthuongg/revert-13-revert-12-revert-1…
Huynhthuongg May 23, 2026
1d97c30
fix: correct branding typo and update repository link
Huynhthuongg May 27, 2026
a992a51
Merge pull request #37 from Huynhthuongg/codex/review-and-fix-reposit…
Huynhthuongg May 27, 2026
0cd30a6
feat: add OpenTelemetry instrumentation with Drizzle ORM tracing
Jun 10, 2026
1701d38
Merge pull request #48 from Huynhthuongg/feature/add-opentelemetry-dr…
Huynhthuongg Jun 10, 2026
00b6589
build(deps-dev): bump the npm_and_yarn group across 2 directories wit…
dependabot[bot] Jun 12, 2026
d66a028
Merge pull request #49 from Huynhthuongg/dependabot/npm_and_yarn/npm_…
Huynhthuongg Jun 12, 2026
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
18 changes: 18 additions & 0 deletions artifacts/api-server/.env.otel.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# OpenTelemetry Configuration for Kubiks

# The endpoint where telemetry data will be sent
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.kubiks.app

# The protocol used for sending data (http/protobuf is the standard)
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

# Authentication header with your Kubiks API key
# Replace with your actual API key from Kubiks dashboard
OTEL_EXPORTER_OTLP_HEADERS=x-kubiks-key=YOUR_KUBIKS_API_KEY

# Service name identifies your application in Kubiks
OTEL_SERVICE_NAME=api-server

# Optional: Set log level for OpenTelemetry SDK
# Options: debug, info, warn, error
# OTEL_LOG_LEVEL=info
8 changes: 7 additions & 1 deletion artifacts/api-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@
"zod": "catalog:"
},
"devDependencies": {
"@kubiks/otel-drizzle": "^2.1.0",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
"@opentelemetry/sdk-node": "^0.218.0",
"@opentelemetry/sdk-trace-node": "^2.7.1",
"@types/cookie-parser": "^1.4.10",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/node": "catalog:",
"esbuild": "^0.27.3",
"esbuild": "^0.28.1",
"esbuild-plugin-pino": "^2.3.3",
"pino-pretty": "^13",
"thread-stream": "3.1.0"
Expand Down
1 change: 1 addition & 0 deletions artifacts/api-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "./instrumentation.js";
import app from "./app";
import { logger } from "./lib/logger";

Expand Down
42 changes: 42 additions & 0 deletions artifacts/api-server/src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { DrizzleInstrumentation } from "@kubiks/otel-drizzle";
import { logger } from "./lib/logger";

const otelSDK = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "https://ingest.kubiks.app/v1/traces",
headers: {
"x-kubiks-key": process.env.OTEL_EXPORTER_OTLP_HEADERS?.split("=")[1] || "",
},
}),
instrumentations: [
getNodeAutoInstrumentations({
"@opentelemetry/instrumentation-express": {
enabled: true,
},
"@opentelemetry/instrumentation-http": {
enabled: true,
},
}),
new DrizzleInstrumentation(),
],
serviceName: process.env.OTEL_SERVICE_NAME || "api-server",
});

otelSDK
.start()
.then(() => {
logger.info("OpenTelemetry started successfully");
})
.catch((err) => {
logger.error({ err }, "Failed to start OpenTelemetry");
});

process.on("SIGTERM", () => {
otelSDK
.shutdown()
.then(() => logger.info("OpenTelemetry shutdown successfully"))
.catch((err) => logger.error({ err }, "Failed to shutdown OpenTelemetry"));
});
104 changes: 100 additions & 4 deletions artifacts/sandbox-ai/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@ const queryClient = new QueryClient();
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
Comment thread
Huynhthuongg marked this conversation as resolved.
const clerkProxyUrl = import.meta.env.VITE_CLERK_PROXY_URL;
const basePath = import.meta.env.BASE_URL.replace(/\/$/, "");
const requestedAuthMode = (import.meta.env.VITE_AUTH_MODE ?? "clerk").toLowerCase();
const authMode = requestedAuthMode === "public" ? "public" : "clerk";
const isClerkConfigured = Boolean(clerkPubKey);
const isAuthEnabled = authMode === "clerk" && isClerkConfigured;

const envChecklist = [
{
key: "VITE_AUTH_MODE",
value: import.meta.env.VITE_AUTH_MODE,
required: false,
status: "info" as const,
hint: "Optional. Use 'clerk' (default) or 'public'.",
},
{
key: "VITE_CLERK_PUBLISHABLE_KEY",
value: clerkPubKey,
required: authMode === "clerk",
status: isClerkConfigured ? ("ok" as const) : ("missing" as const),
hint: "Required when VITE_AUTH_MODE=clerk.",
},
{
key: "VITE_CLERK_PROXY_URL",
value: clerkProxyUrl,
required: false,
status: "info" as const,
hint: "Optional proxy URL for Clerk.",
},
];

function stripBase(path: string): string {
return basePath && path.startsWith(basePath)
Expand Down Expand Up @@ -198,6 +226,33 @@ function ClerkAuthTokenSetter() {
return null;
}


function HealthConfigPage() {
return (
<div className="min-h-screen" style={{ backgroundColor: "var(--sb-bg)", color: "#fff", padding: 24 }}>
<h1 style={{ fontSize: 28, fontWeight: 800, marginBottom: 12 }}>Health Config</h1>
Comment thread
Huynhthuongg marked this conversation as resolved.
<p style={{ opacity: 0.8, marginBottom: 20 }}>
Runtime auth mode: <strong>{authMode}</strong>. Auth enabled: <strong>{String(isAuthEnabled)}</strong>.
</p>
<div style={{ display: "grid", gap: 10 }}>
{envChecklist.map((item) => (
<div key={item.key} style={{ border: "1px solid rgba(255,255,255,0.15)", borderRadius: 10, padding: 12 }}>
<div style={{ display: "flex", justifyContent: "space-between", gap: 12 }}>
<code>{item.key}</code>
<strong>
{item.status === "ok" ? "✅ configured" : item.status === "missing" ? "❌ missing" : "ℹ️ optional"}
</strong>
</div>
<div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>required: {String(item.required)}</div>
<div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>value: {item.value ? "set" : "empty"}</div>
<div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>{item.hint}</div>
</div>
))}
</div>
</div>
);
}

function Router() {
return (
<Switch>
Expand All @@ -207,6 +262,7 @@ function Router() {
<Route path="/sign-in/*?" component={SignInPage} />
<Route path="/sign-up/*?" component={SignUpPage} />
<Route path="/auth-check" component={AuthCheck} />
<Route path="/health-config" component={HealthConfigPage} />
<Route path="/prompts">
<ProtectedRoute><Prompts /></ProtectedRoute>
</Route>
Expand Down Expand Up @@ -258,14 +314,54 @@ function ClerkProviderWithRoutes() {
);
}


function PublicOnlyRouter() {
return (
<Switch>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/pricing" component={Pricing} />
<Route path="/get-app" component={GetApp} />
<Route path="/download" component={Download} />
<Route path="/health-config" component={HealthConfigPage} />
<Route path="/sign-in/*?">
<Redirect to="/" />
</Route>
<Route path="/sign-up/*?">
<Redirect to="/" />
</Route>
<Route path="/chat">
<Redirect to="/" />
</Route>
<Route path="/chat/:id">
<Redirect to="/" />
</Route>
<Route component={NotFound} />
</Switch>
);
}

function PublicAppWithoutAuth() {
return (
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<div style={{ color: "#fff", padding: 16, fontSize: 14, opacity: 0.85 }}>
Auth is running in public mode. Open <code>/health-config</code> to inspect environment readiness.
</div>
<PublicOnlyRouter />
<Toaster />
</TooltipProvider>
</QueryClientProvider>
);
}
function App() {
const [showSplash, setShowSplash] = useState(true);

if (!clerkPubKey) {
if (!isAuthEnabled) {
return (
<div style={{ color: "#fff", padding: 24 }}>
Missing VITE_CLERK_PUBLISHABLE_KEY — please check environment variables.
</div>
<WouterRouter base={basePath}>
<PublicAppWithoutAuth />
</WouterRouter>
);
}

Expand Down
2 changes: 1 addition & 1 deletion artifacts/sandbox-ai/src/pages/about.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@ export default function About() {
</div>
<div className="flex items-center gap-4">
<a
href="https://github.com/Huynhthuongg/Sanbox/tree/main?tab=readme-ov-file"
href="https://github.com/Huynhthuongg/Rkix-v1"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 transition-colors hover:text-white"
Expand Down
2 changes: 1 addition & 1 deletion assets/demo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions docs_self_healing_process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Self-healing reminder workflow (OpenClaw/Eios style)

## Goal
Automatically remind maintainers when deployment-critical configuration or code artifacts are missing.

## 1) Detect
- On every deploy, open `/health-config` and validate required env keys.
- On CI, run `pnpm --filter @workspace/sandbox-ai run build` and fail on errors.

## 2) Classify
- **Config missing** (e.g. `VITE_CLERK_PUBLISHABLE_KEY` when `VITE_AUTH_MODE=clerk`).
- **Artifact missing** (route/page/module referenced but not found).
- **Type/build breakage** (TypeScript/build errors).

## 3) Notify owner
- Config missing -> notify DevOps/release owner.
- Artifact missing -> notify feature owner/repo maintainer.
- Build/type breakage -> notify author of latest PR and reviewer.

## 4) Auto-remediation checklist
- If auth env missing in non-production: set `VITE_AUTH_MODE=public` as temporary fallback.
- If auth env missing in production: block release and require secret injection.
- If artifact missing: create placeholder file + TODO with owner and deadline.

## 5) Prevent regressions
- Add a PR checklist item: “Did you verify `/health-config` and auth mode behavior?”
- Keep a short runbook for required variables per environment.
Loading
Loading