chore(telemetry): flatten the project slug layer and remove cd path instructions from system prompt

This commit is contained in:
2026-06-09 13:28:57 -07:00
parent b5b18ccd32
commit 6ec312f716
3 changed files with 45 additions and 34 deletions

View File

@@ -34,21 +34,21 @@
* widen it.
*/
import { execInDevContainer } from '@/lib/dev-container';
import { execInDevContainer } from "@/lib/dev-container";
const GITEA_API_URL = process.env.GITEA_API_URL ?? '';
const GITEA_API_URL = process.env.GITEA_API_URL ?? "";
// Falls back to GITEA_ADMIN_USER because production historically only set the
// admin var; missing GITEA_USERNAME used to silently disable auto-clone.
const GITEA_USERNAME =
process.env.GITEA_USERNAME || process.env.GITEA_ADMIN_USER || '';
const GITEA_API_TOKEN = process.env.GITEA_API_TOKEN ?? '';
process.env.GITEA_USERNAME || process.env.GITEA_ADMIN_USER || "";
const GITEA_API_TOKEN = process.env.GITEA_API_TOKEN ?? "";
const AI_GIT_AUTHOR_NAME = 'Vibn AI';
const AI_GIT_AUTHOR_EMAIL = 'ai@vibnai.com';
const AI_GIT_AUTHOR_NAME = "Vibn AI";
const AI_GIT_AUTHOR_EMAIL = "ai@vibnai.com";
/** Where each project's repo lives inside the dev container. */
export function projectRepoPath(projectSlug: string): string {
return `/workspace/${projectSlug}`;
return `/workspace`;
}
function isGiteaConfigured(): boolean {
@@ -100,10 +100,14 @@ export async function ensureProjectRepoCloned(
opts: EnsureRepoClonedOpts,
): Promise<EnsureRepoClonedResult> {
if (!isGiteaConfigured()) {
return { cloned: false, alreadyPresent: false, reason: 'gitea_not_configured' };
return {
cloned: false,
alreadyPresent: false,
reason: "gitea_not_configured",
};
}
if (!opts.giteaCloneUrl) {
return { cloned: false, alreadyPresent: false, reason: 'no_clone_url' };
return { cloned: false, alreadyPresent: false, reason: "no_clone_url" };
}
const repoDir = projectRepoPath(opts.projectSlug);
@@ -121,18 +125,18 @@ export async function ensureProjectRepoCloned(
const probe = await execInDevContainer({
projectId: opts.projectId,
command:
`if [ -d ${shellQ(repoDir + '/.git')} ]; then echo git; ` +
`if [ -d ${shellQ(repoDir + "/.git")} ]; then echo git; ` +
`elif [ -d ${shellQ(repoDir)} ]; then echo dir; ` +
`else echo absent; fi`,
timeoutMs: 5_000,
});
const probeState = probe.stdout.trim();
if (probeState === 'git') {
if (probeState === "git") {
return { cloned: false, alreadyPresent: true };
}
if (probeState === 'dir') {
if (probeState === "dir") {
// Init in place, hook up the remote, fetch, set tracking. We
// don't try to merge — the directory's contents become whatever
// the next auto-commit-and-push picks up. If the remote has
@@ -147,7 +151,7 @@ export async function ensureProjectRepoCloned(
// Best-effort fetch; if remote is empty this errors out and
// we proceed anyway. The `|| true` keeps the chain going.
`(git fetch origin main 2>/dev/null && git reset --soft origin/main 2>/dev/null) || true`,
].join(' && ');
].join(" && ");
const result = await execInDevContainer({
projectId: opts.projectId,
@@ -182,7 +186,7 @@ export async function ensureProjectRepoCloned(
`git config user.email ${shellQ(AI_GIT_AUTHOR_EMAIL)}`,
`git remote set-url origin ${shellQ(authed)}`,
`cd /workspace && mv ${shellQ(tmpDir)} ${shellQ(repoDir)}`,
].join(' && ');
].join(" && ");
const result = await execInDevContainer({
projectId: opts.projectId,
@@ -201,7 +205,11 @@ export async function ensureProjectRepoCloned(
// (warning: "remote HEAD refers to nonexistent ref"), clone
// didn't actually fail — just there's no history to check
// out. Make an empty dir + init so subsequent commits land.
if (/empty|warning: You appear to have cloned an empty/i.test(result.stderr + result.stdout)) {
if (
/empty|warning: You appear to have cloned an empty/i.test(
result.stderr + result.stdout,
)
) {
const initEmpty = [
`mkdir -p ${shellQ(repoDir)}`,
`cd ${shellQ(repoDir)}`,
@@ -209,7 +217,7 @@ export async function ensureProjectRepoCloned(
`git config user.name ${shellQ(AI_GIT_AUTHOR_NAME)}`,
`git config user.email ${shellQ(AI_GIT_AUTHOR_EMAIL)}`,
`git remote add origin ${shellQ(authed)}`,
].join(' && ');
].join(" && ");
const initR = await execInDevContainer({
projectId: opts.projectId,
command: initEmpty,
@@ -260,21 +268,21 @@ export async function commitAndPushIfDirty(
opts: CommitAndPushOpts,
): Promise<CommitAndPushResult> {
const repoDir = projectRepoPath(opts.projectSlug);
const message = (opts.message ?? '').trim() || 'AI checkpoint';
const message = (opts.message ?? "").trim() || "AI checkpoint";
// Sanitize: keep messages single-line and bounded so we can't be
// tricked into shell-escape or commit-message injection.
const safeMessage = message.replace(/[\r\n]+/g, ' ').slice(0, 200);
const safeMessage = message.replace(/[\r\n]+/g, " ").slice(0, 200);
// Bail fast if there's no repo to commit against. Don't treat as
// an error — projects without a clone (e.g. cloning failed earlier)
// should still be able to chat.
const probe = await execInDevContainer({
projectId: opts.projectId,
command: `test -d ${shellQ(repoDir + '/.git')} && echo present || echo absent`,
command: `test -d ${shellQ(repoDir + "/.git")} && echo present || echo absent`,
timeoutMs: 5_000,
}).catch(() => null);
if (!probe || probe.stdout.trim() !== 'present') {
return { committed: false, pushed: false, reason: 'no_repo' };
if (!probe || probe.stdout.trim() !== "present") {
return { committed: false, pushed: false, reason: "no_repo" };
}
const cmd = [
@@ -287,21 +295,24 @@ export async function commitAndPushIfDirty(
`SHA=$(git rev-parse --short HEAD)`,
`git push -u origin HEAD 2>&1 | tail -n 5`,
`echo COMMITTED $SHA`,
].join(' && ');
].join(" && ");
const result = await execInDevContainer({
projectId: opts.projectId,
command: cmd,
timeoutMs: 30_000,
}).catch((err: unknown) => ({
stdout: '',
stderr: err instanceof Error ? err.message : String(err),
exitCode: -1,
} satisfies { stdout: string; stderr: string; exitCode: number }));
}).catch(
(err: unknown) =>
({
stdout: "",
stderr: err instanceof Error ? err.message : String(err),
exitCode: -1,
}) satisfies { stdout: string; stderr: string; exitCode: number },
);
const out = result.stdout || '';
if (out.includes('CLEAN')) {
return { committed: false, pushed: false, reason: 'clean' };
const out = result.stdout || "";
if (out.includes("CLEAN")) {
return { committed: false, pushed: false, reason: "clean" };
}
const m = /COMMITTED\s+([0-9a-f]+)/.exec(out);
if (m) {