Studio: auto-detect server GH_TOKEN and widen Easy-mode detection
The GitHub seed form now fetches /api/data-recipe/seed/github/env-token on mount and, when the server exposes a GH_TOKEN / GITHUB_TOKEN env var and the token field is blank, shows a small 'Using server env var' badge and swaps the placeholder text. The token value itself is never returned to the UI. Widens Easy-mode detection in recipe-studio-page.tsx so that recipes saved before ui.seed_source_type was persisted also get the Easy tab: falls back to recipe.seed_config.source.seed_type, which is always present for github_repo seeds.
This commit is contained in:
parent
dba29845de
commit
6ced30cdaa
4 changed files with 60 additions and 7 deletions
|
|
@ -8,6 +8,7 @@ from __future__ import annotations
|
|||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from itertools import islice
|
||||
from pathlib import Path
|
||||
|
|
@ -627,3 +628,16 @@ def inspect_seed_upload(payload: SeedInspectUploadRequest) -> SeedInspectRespons
|
|||
split = None,
|
||||
subset = None,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/seed/github/env-token")
|
||||
def get_github_env_token_status() -> dict:
|
||||
"""Report whether the server has a GH_TOKEN / GITHUB_TOKEN env var.
|
||||
|
||||
The value is never returned; the UI uses this to tell the user they
|
||||
can leave the token field blank.
|
||||
"""
|
||||
has_token = bool(
|
||||
os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN")
|
||||
)
|
||||
return {"has_token": has_token}
|
||||
|
|
|
|||
|
|
@ -315,6 +315,13 @@ export async function inspectSeedUpload(
|
|||
return postJson<SeedInspectResponse>("/seed/inspect-upload", payload);
|
||||
}
|
||||
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
export type GithubEnvTokenStatus = { has_token: boolean };
|
||||
|
||||
export async function getGithubEnvTokenStatus(): Promise<GithubEnvTokenStatus> {
|
||||
return getJson<GithubEnvTokenStatus>("/seed/github/env-token");
|
||||
}
|
||||
|
||||
export async function listMcpTools(
|
||||
payload: McpToolsListRequest,
|
||||
): Promise<McpToolsListResponse> {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import {
|
|||
import { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { UnstructuredDropZone, type FileEntry } from "./unstructured-drop-zone";
|
||||
import { inspectSeedDataset, inspectSeedUpload } from "../../api";
|
||||
import { getGithubEnvTokenStatus, inspectSeedDataset, inspectSeedUpload } from "../../api";
|
||||
import { resolveImagePreview } from "../../utils/image-preview";
|
||||
import type {
|
||||
SeedConfig,
|
||||
|
|
@ -83,8 +83,26 @@ export function GithubRepoSeedForm({
|
|||
onUpdate: (patch: Partial<SeedConfig>) => void;
|
||||
}): ReactElement {
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||
const [serverHasEnvToken, setServerHasEnvToken] = useState<boolean | null>(
|
||||
null,
|
||||
);
|
||||
const limitStr = (config.github_limit ?? "100").trim();
|
||||
const allMode = limitStr === "" || limitStr === "0";
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
void getGithubEnvTokenStatus()
|
||||
.then((status) => {
|
||||
if (!cancelled) setServerHasEnvToken(status.has_token);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setServerHasEnvToken(false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
const userTokenEmpty = !(config.github_token ?? "").trim();
|
||||
const usingEnvToken = userTokenEmpty && serverHasEnvToken === true;
|
||||
return (
|
||||
<div className="space-y-3 rounded-xl corner-squircle border border-border/60 p-3">
|
||||
<div className="grid gap-1.5">
|
||||
|
|
@ -100,16 +118,27 @@ export function GithubRepoSeedForm({
|
|||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<FieldLabel
|
||||
label="GitHub token"
|
||||
hint="Personal access token with repo scope. Leave blank to use the server's GH_TOKEN / GITHUB_TOKEN env var."
|
||||
/>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<FieldLabel
|
||||
label="GitHub token"
|
||||
hint="Personal access token with repo scope. Leave blank to use the server's GH_TOKEN / GITHUB_TOKEN env var."
|
||||
/>
|
||||
{usingEnvToken && (
|
||||
<span className="shrink-0 rounded-md border border-emerald-500/40 bg-emerald-500/10 px-1.5 py-0.5 text-[10px] font-medium text-emerald-700 dark:text-emerald-300">
|
||||
Using server env var
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
type="password"
|
||||
className="nodrag"
|
||||
value={config.github_token ?? ""}
|
||||
onChange={(e) => onUpdate({ github_token: e.target.value })}
|
||||
placeholder="ghp_... (optional if server has GH_TOKEN)"
|
||||
placeholder={
|
||||
usingEnvToken
|
||||
? "using server's GH_TOKEN env var"
|
||||
: "ghp_... (optional if server has GH_TOKEN)"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
|
|
|
|||
|
|
@ -202,7 +202,10 @@ export function RecipeStudioPage({
|
|||
null,
|
||||
);
|
||||
const flowContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const supportsEasyMode = initialPayload?.ui?.seed_source_type === "github_repo";
|
||||
const supportsEasyMode =
|
||||
initialPayload?.ui?.seed_source_type === "github_repo" ||
|
||||
(initialPayload?.recipe?.seed_config as { source?: { seed_type?: string } } | undefined)
|
||||
?.source?.seed_type === "github_repo";
|
||||
const viewModeStorageKey = `recipe-studio:view-mode:${recipeId}`;
|
||||
const [activeView, setActiveViewState] = useState<RecipeStudioView>(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue