update
This commit is contained in:
1 parent
f482f25856
commit
d126c4cf21
165 files changed
+123
-541297
No files matched your search
@@ -24,24 +24,12 @@ function connectorAuthSpec(connectorId: ConnectorId, command: string) {
|
||||
shellCommand: `${quoteShell(command)} auth login`,
|
||||
intro: "AgentCompany is opening the Claude Code login flow.",
|
||||
};
|
||||
case "opencode_local":
|
||||
return {
|
||||
title: "OpenCode Login",
|
||||
shellCommand: `${quoteShell(command)} auth login`,
|
||||
intro: "AgentCompany is opening the OpenCode provider login flow.",
|
||||
};
|
||||
case "gemini_local":
|
||||
return {
|
||||
title: "Gemini CLI Login",
|
||||
shellCommand: `${quoteShell(command)}`,
|
||||
intro: "AgentCompany is opening Gemini CLI. If authentication is required, use /auth in the Gemini session.",
|
||||
};
|
||||
case "kimi_local":
|
||||
return {
|
||||
title: "Kimi Code Login",
|
||||
shellCommand: `${quoteShell(command)}`,
|
||||
intro: "AgentCompany is opening Kimi Code. Complete the interactive login flow there if prompted.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-55
@@ -9,10 +9,6 @@ import {
|
||||
import { execute as codexExecute, sessionCodec as codexSessionCodec, testEnvironment as codexTestEnvironment } from "@agentcompany/adapter-codex-local/server";
|
||||
import { agentConfigurationDoc as geminiDoc, DEFAULT_GEMINI_LOCAL_MODEL, models as geminiModels } from "@agentcompany/adapter-gemini-local";
|
||||
import { execute as geminiExecute, sessionCodec as geminiSessionCodec, testEnvironment as geminiTestEnvironment } from "@agentcompany/adapter-gemini-local/server";
|
||||
import { agentConfigurationDoc as kimiDoc, DEFAULT_KIMI_LOCAL_MODEL, models as kimiModels } from "@agentcompany/adapter-kimi-local";
|
||||
import { execute as kimiExecute, testEnvironment as kimiTestEnvironment } from "@agentcompany/adapter-kimi-local/server";
|
||||
import { agentConfigurationDoc as opencodeDoc } from "@agentcompany/adapter-opencode-local";
|
||||
import { execute as opencodeExecute, listOpenCodeModels, sessionCodec as opencodeSessionCodec, testEnvironment as opencodeTestEnvironment } from "@agentcompany/adapter-opencode-local/server";
|
||||
import type { CapabilityMatrix, ConnectorId, ConnectorRecord } from "@shared/types";
|
||||
|
||||
const RESERVED_ENV_BINDING_KEYS = new Set([
|
||||
@@ -47,7 +43,7 @@ const CONNECTORS: ConnectorDefinition[] = [
|
||||
description: "Local Codex CLI with session resume, structured event parsing, and model selection.",
|
||||
defaultCommand: "codex",
|
||||
defaultModel: DEFAULT_CODEX_LOCAL_MODEL,
|
||||
notes: "Best fit for high-context coding tasks and shell-heavy automation.",
|
||||
notes: "",
|
||||
capabilityMatrix: {
|
||||
sessionResume: true,
|
||||
gracefulCancel: true,
|
||||
@@ -71,7 +67,7 @@ const CONNECTORS: ConnectorDefinition[] = [
|
||||
description: "Local Claude Code CLI with session persistence and structured result parsing.",
|
||||
defaultCommand: "claude",
|
||||
defaultModel: claudeModels[0]?.id ?? null,
|
||||
notes: "Useful for long-running coding sessions and agent-style loops.",
|
||||
notes: "",
|
||||
capabilityMatrix: {
|
||||
sessionResume: true,
|
||||
gracefulCancel: true,
|
||||
@@ -95,7 +91,7 @@ const CONNECTORS: ConnectorDefinition[] = [
|
||||
description: "Local Gemini CLI with resume support and JSON event parsing.",
|
||||
defaultCommand: "gemini",
|
||||
defaultModel: DEFAULT_GEMINI_LOCAL_MODEL,
|
||||
notes: "Good for multi-provider local workflows when Google auth is already in place.",
|
||||
notes: "",
|
||||
capabilityMatrix: {
|
||||
sessionResume: true,
|
||||
gracefulCancel: true,
|
||||
@@ -113,54 +109,6 @@ const CONNECTORS: ConnectorDefinition[] = [
|
||||
},
|
||||
listModels: async () => geminiModels,
|
||||
},
|
||||
{
|
||||
id: "opencode_local",
|
||||
label: "OpenCode",
|
||||
description: "Local OpenCode runtime with provider/model routing and session restore.",
|
||||
defaultCommand: "opencode",
|
||||
defaultModel: null,
|
||||
notes: "Best when model routing belongs to the runtime rather than the app.",
|
||||
capabilityMatrix: {
|
||||
sessionResume: true,
|
||||
gracefulCancel: true,
|
||||
modelDiscovery: true,
|
||||
structuredTranscript: true,
|
||||
costAttribution: true,
|
||||
},
|
||||
configurationDoc: opencodeDoc,
|
||||
module: {
|
||||
type: "opencode_local",
|
||||
execute: opencodeExecute,
|
||||
testEnvironment: opencodeTestEnvironment,
|
||||
sessionCodec: opencodeSessionCodec,
|
||||
models: [],
|
||||
listModels: listOpenCodeModels,
|
||||
},
|
||||
listModels: listOpenCodeModels,
|
||||
},
|
||||
{
|
||||
id: "kimi_local",
|
||||
label: "Kimi Code",
|
||||
description: "Official Kimi Code CLI surfaced through a generic execution contract with explicit capability downgrade.",
|
||||
defaultCommand: "kimi",
|
||||
defaultModel: DEFAULT_KIMI_LOCAL_MODEL,
|
||||
notes: "MVP support is explicit generic mode until richer runtime contract evidence is implemented.",
|
||||
capabilityMatrix: {
|
||||
sessionResume: false,
|
||||
gracefulCancel: false,
|
||||
modelDiscovery: false,
|
||||
structuredTranscript: false,
|
||||
costAttribution: false,
|
||||
},
|
||||
configurationDoc: kimiDoc,
|
||||
module: {
|
||||
type: "kimi_local",
|
||||
execute: kimiExecute,
|
||||
testEnvironment: kimiTestEnvironment,
|
||||
models: kimiModels,
|
||||
},
|
||||
listModels: async () => kimiModels,
|
||||
},
|
||||
];
|
||||
|
||||
export function getConnectorDefinition(id: ConnectorId): ConnectorDefinition {
|
||||
|
||||
@@ -124,12 +124,6 @@ function buildConnectorExecutionConfig(job: RunJob, sanitizedEnv: Record<string,
|
||||
approvalMode: "yolo",
|
||||
sandbox: true,
|
||||
};
|
||||
case "kimi_local":
|
||||
return {
|
||||
...baseConfig,
|
||||
extraArgs: ["--yolo"],
|
||||
};
|
||||
case "opencode_local":
|
||||
default:
|
||||
return baseConfig;
|
||||
}
|
||||
|
||||
@@ -702,7 +702,7 @@ Body: {
|
||||
"title": "Descriptive title",
|
||||
"department": "engineering|product|marketing|sales|hr|finance|legal|operations|customer_support|research|design|executive",
|
||||
"reportsTo": "{managerAgentId — who this person reports to}",
|
||||
"connectorId": "codex_local|claude_local|gemini_local|opencode_local|kimi_local",
|
||||
"connectorId": "codex_local|claude_local|gemini_local",
|
||||
"workspaceId": "{optionalWorkspaceId}",
|
||||
"model": "model-identifier",
|
||||
"capabilities": "Detailed description of what this hire should own and be able to do",
|
||||
|
||||
@@ -5,11 +5,13 @@ import {
|
||||
CheckCircle,
|
||||
Cpu,
|
||||
FolderOpen,
|
||||
GearSix,
|
||||
GlobeSimple,
|
||||
Lightning,
|
||||
Rocket,
|
||||
Target,
|
||||
WarningCircle,
|
||||
X,
|
||||
XCircle,
|
||||
} from "@phosphor-icons/react";
|
||||
import { getConnectorExecutionReadinessIssue, isConnectorExecutionReady } from "@shared/connector-policy";
|
||||
@@ -50,6 +52,11 @@ export function CompanyOnboarding({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [launchSummary, setLaunchSummary] = useState<OnboardingBootstrapResult | null>(null);
|
||||
const [claudeSettingsOpen, setClaudeSettingsOpen] = useState(false);
|
||||
const [claudeApiKey, setClaudeApiKey] = useState("");
|
||||
const [claudeBaseUrl, setClaudeBaseUrl] = useState("");
|
||||
const [claudeModel, setClaudeModel] = useState("");
|
||||
const [claudeSettingsSaving, setClaudeSettingsSaving] = useState(false);
|
||||
|
||||
const executionReadyConnectors = useMemo(
|
||||
() => connectors.filter((connector) => isConnectorExecutionReady(connector)),
|
||||
@@ -70,6 +77,44 @@ export function CompanyOnboarding({
|
||||
}
|
||||
}, [executionReadyConnectors, selectedConnectorId]);
|
||||
|
||||
const openClaudeSettings = useCallback(() => {
|
||||
const claude = connectors.find((c) => c.id === "claude_local");
|
||||
if (!claude) return;
|
||||
const lines = claude.envBindingText.split(/\r?\n/);
|
||||
setClaudeApiKey(lines.find((l) => l.startsWith("ANTHROPIC_API_KEY="))?.split("=").slice(1).join("=") ?? "");
|
||||
setClaudeBaseUrl(lines.find((l) => l.startsWith("ANTHROPIC_BASE_URL="))?.split("=").slice(1).join("=") ?? "");
|
||||
setClaudeModel(claude.model ?? "");
|
||||
setClaudeSettingsOpen(true);
|
||||
}, [connectors]);
|
||||
|
||||
const saveClaudeSettings = useCallback(async () => {
|
||||
setClaudeSettingsSaving(true);
|
||||
try {
|
||||
const envLines: string[] = [];
|
||||
if (claudeApiKey.trim()) envLines.push(`ANTHROPIC_API_KEY=${claudeApiKey.trim()}`);
|
||||
if (claudeBaseUrl.trim()) envLines.push(`ANTHROPIC_BASE_URL=${claudeBaseUrl.trim()}`);
|
||||
const claude = connectors.find((c) => c.id === "claude_local");
|
||||
if (!claude) return;
|
||||
const existingLines = claude.envBindingText.split(/\r?\n/).filter((l) => {
|
||||
const key = l.split("=")[0]?.trim();
|
||||
return key && key !== "ANTHROPIC_API_KEY" && key !== "ANTHROPIC_BASE_URL" && l.trim();
|
||||
});
|
||||
const newEnvText = [...envLines, ...existingLines].join("\n");
|
||||
unwrap(await window.agentCompany.saveConnector({
|
||||
id: "claude_local",
|
||||
command: claude.command,
|
||||
model: claudeModel.trim() || null,
|
||||
envBindingText: newEnvText,
|
||||
notes: claude.notes,
|
||||
}));
|
||||
setClaudeSettingsOpen(false);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
setClaudeSettingsSaving(false);
|
||||
}
|
||||
}, [claudeApiKey, claudeBaseUrl, claudeModel, connectors]);
|
||||
|
||||
const stepLabels = [
|
||||
{ label: t("onboarding.stepLabel1"), icon: Buildings },
|
||||
{ label: t("onboarding.stepLabel2"), icon: Target },
|
||||
@@ -221,7 +266,7 @@ export function CompanyOnboarding({
|
||||
) : (
|
||||
<WarningCircle size={16} weight="fill" className="shrink-0 text-[color:var(--warn)]" />
|
||||
)}
|
||||
<div className="min-w-0">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={isReady ? "font-medium text-[color:var(--text)]" : isChecking ? "text-[color:var(--text)]" : "text-[color:var(--muted)]"}>
|
||||
{connector.label}
|
||||
@@ -236,6 +281,16 @@ export function CompanyOnboarding({
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{connector.id === "claude_local" ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => { e.stopPropagation(); openClaudeSettings(); }}
|
||||
className="shrink-0 rounded-[6px] p-1.5 text-[color:var(--muted)] transition hover:bg-[color:var(--panel-soft)] hover:text-[color:var(--text)]"
|
||||
title={currentLocale === "zh" ? "Claude Code 设置" : "Claude Code Settings"}
|
||||
>
|
||||
<GearSix size={14} />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -569,6 +624,63 @@ export function CompanyOnboarding({
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{claudeSettingsOpen ? (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-[color:var(--bg)]/72 backdrop-blur-sm" onClick={() => setClaudeSettingsOpen(false)}>
|
||||
<div className="w-full max-w-[420px] rounded-[12px] border border-[color:var(--line)] bg-[color:var(--panel)] p-6 shadow-lg" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="mb-5 flex items-center justify-between">
|
||||
<h3 className="text-[16px] font-semibold text-[color:var(--text)]">Claude Code {currentLocale === "zh" ? "设置" : "Settings"}</h3>
|
||||
<button type="button" onClick={() => setClaudeSettingsOpen(false)} className="rounded-[6px] p-1 text-[color:var(--muted)] transition hover:text-[color:var(--text)]"><X size={16} /></button>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="mb-1.5 block text-[12px] font-medium text-[color:var(--muted-strong)]">API Key</label>
|
||||
<input
|
||||
type="password"
|
||||
className="focus-ring w-full rounded-[8px] border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2.5 text-[13px] text-[color:var(--text)] placeholder:text-[color:var(--muted)]"
|
||||
value={claudeApiKey}
|
||||
onChange={(e) => setClaudeApiKey(e.target.value)}
|
||||
placeholder="sk-ant-..."
|
||||
/>
|
||||
<div className="mt-1 text-[11px] text-[color:var(--muted)]">{currentLocale === "zh" ? "留空使用订阅认证" : "Leave empty to use subscription auth"}</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1.5 block text-[12px] font-medium text-[color:var(--muted-strong)]">Base URL</label>
|
||||
<input
|
||||
className="focus-ring w-full rounded-[8px] border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2.5 text-[13px] text-[color:var(--text)] placeholder:text-[color:var(--muted)]"
|
||||
value={claudeBaseUrl}
|
||||
onChange={(e) => setClaudeBaseUrl(e.target.value)}
|
||||
placeholder="https://api.anthropic.com"
|
||||
/>
|
||||
<div className="mt-1 text-[11px] text-[color:var(--muted)]">{currentLocale === "zh" ? "留空使用默认端点" : "Leave empty for default endpoint"}</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1.5 block text-[12px] font-medium text-[color:var(--muted-strong)]">Model</label>
|
||||
<input
|
||||
className="focus-ring w-full rounded-[8px] border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2.5 text-[13px] text-[color:var(--text)] placeholder:text-[color:var(--muted)]"
|
||||
value={claudeModel}
|
||||
onChange={(e) => setClaudeModel(e.target.value)}
|
||||
placeholder="claude-sonnet-4-20250514"
|
||||
/>
|
||||
<div className="mt-1 text-[11px] text-[color:var(--muted)]">{currentLocale === "zh" ? "留空使用连接器默认模型" : "Leave empty for connector default"}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end gap-3">
|
||||
<button type="button" onClick={() => setClaudeSettingsOpen(false)} className="rounded-[8px] px-4 py-2 text-[13px] font-medium text-[color:var(--muted)] transition hover:text-[color:var(--text)]">
|
||||
{currentLocale === "zh" ? "取消" : "Cancel"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void saveClaudeSettings()}
|
||||
disabled={claudeSettingsSaving}
|
||||
className="rounded-[8px] bg-[color:var(--accent)] px-4 py-2 text-[13px] font-medium text-white transition hover:opacity-90 disabled:opacity-50"
|
||||
>
|
||||
{claudeSettingsSaving ? (currentLocale === "zh" ? "保存中..." : "Saving...") : (currentLocale === "zh" ? "保存" : "Save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -19,24 +19,12 @@ function getAuthActionCopy(connector: ConnectorRecord) {
|
||||
message: "Claude Code is installed, but the local CLI is not authenticated for this desktop runtime.",
|
||||
buttonLabel: "Open Terminal To Sign In",
|
||||
};
|
||||
case "opencode_local":
|
||||
return {
|
||||
title: "OpenCode provider login required",
|
||||
message: "OpenCode is installed, but the provider login must be completed before runs can start.",
|
||||
buttonLabel: "Open Terminal To Sign In",
|
||||
};
|
||||
case "gemini_local":
|
||||
return {
|
||||
title: "Gemini authentication required",
|
||||
message: "Gemini CLI is available, but the runtime still needs an authenticated session. AgentCompany will open Gemini in a terminal and you can complete its interactive auth flow there.",
|
||||
buttonLabel: "Open Terminal",
|
||||
};
|
||||
case "kimi_local":
|
||||
return {
|
||||
title: "Kimi interactive login required",
|
||||
message: "Kimi Code is available in generic mode. AgentCompany can open the CLI in a terminal so you can complete the interactive login flow there if prompted.",
|
||||
buttonLabel: "Open Terminal",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +43,8 @@ export function ConnectorEditor({ connector, onSaved }: { connector: ConnectorRe
|
||||
const authPollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const authActionCopy = getAuthActionCopy(connector);
|
||||
const needsLogin = connector.authState === "required" || connector.status === "auth_required";
|
||||
const requiresExplicitModel = connector.id === "opencode_local";
|
||||
const shouldLoadModelOptions = connector.capabilityMatrix.modelDiscovery || connector.id === "opencode_local";
|
||||
const requiresExplicitModel = false;
|
||||
const shouldLoadModelOptions = connector.capabilityMatrix.modelDiscovery;
|
||||
|
||||
useEffect(() => {
|
||||
setModel(connector.model ?? "");
|
||||
|
||||
@@ -1176,8 +1176,6 @@ const adapterLabels: Record<string, string> = {
|
||||
claude_local: "Claude",
|
||||
codex_local: "Codex",
|
||||
gemini_local: "Gemini",
|
||||
opencode_local: "OpenCode",
|
||||
kimi_local: "Kimi",
|
||||
};
|
||||
|
||||
export function OrgChart({
|
||||
|
||||
@@ -88,7 +88,7 @@ export const companyInputSchema = z.object({
|
||||
});
|
||||
|
||||
export const connectorInputSchema = z.object({
|
||||
id: z.enum(["codex_local", "claude_local", "gemini_local", "opencode_local", "kimi_local"]),
|
||||
id: z.enum(["codex_local", "claude_local", "gemini_local"]),
|
||||
command: z.string().min(1),
|
||||
model: z.string().nullable().optional(),
|
||||
envBindingText: z.string().default(""),
|
||||
@@ -288,7 +288,7 @@ export const bootstrapOnboardingSchema = z.object({
|
||||
autoApproveHires: z.boolean().default(true),
|
||||
goalTitle: z.string().default(""),
|
||||
goalDescription: z.string().default(""),
|
||||
connectorId: z.enum(["codex_local", "claude_local", "gemini_local", "opencode_local", "kimi_local"]).optional(),
|
||||
connectorId: z.enum(["codex_local", "claude_local", "gemini_local"]).optional(),
|
||||
});
|
||||
|
||||
export const socialAccountInputSchema = z.object({
|
||||
|
||||
+1
-3
@@ -27,9 +27,7 @@ export type SectionId =
|
||||
export type ConnectorId =
|
||||
| "codex_local"
|
||||
| "claude_local"
|
||||
| "gemini_local"
|
||||
| "opencode_local"
|
||||
| "kimi_local";
|
||||
| "gemini_local";
|
||||
|
||||
export type ConnectorStatus =
|
||||
| "not_installed"
|
||||
|
||||
Reference in new issue
Block a user