larkway 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/cli/index.js +179 -45
- package/dist/main.js +217 -46
- package/dist/web/public/app.js +192 -105
- package/dist/web/public/style.css +34 -7
- package/package.json +14 -16
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
You @ the bot in a Feishu thread. It runs on your machine — reading your real codebase, executing commands, opening MRs — and posts the result back. You define what the agent knows and what it can do. Larkway just carries the messages.
|
|
10
10
|
|
|
11
|
-
**Current release: v0.3.
|
|
11
|
+
**Current release: v0.3.10**
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
package/README.zh.md
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -119646,6 +119646,14 @@ var GitIdentitySchema = external_exports.object({
|
|
|
119646
119646
|
name: external_exports.string().min(1),
|
|
119647
119647
|
email: external_exports.string().email()
|
|
119648
119648
|
});
|
|
119649
|
+
var GitCloneUrlSchema = external_exports.string().min(1).refine(
|
|
119650
|
+
(value) => {
|
|
119651
|
+
if (/^https?:\/\/\S+$/i.test(value)) return true;
|
|
119652
|
+
if (/^ssh:\/\/\S+$/i.test(value)) return true;
|
|
119653
|
+
return /^[A-Za-z0-9_.-]+@[^:\s]+:[^ \t\r\n]+$/.test(value);
|
|
119654
|
+
},
|
|
119655
|
+
"url must be an http(s), ssh://, or scp-like Git clone URL"
|
|
119656
|
+
);
|
|
119649
119657
|
var BotConfigSchema = external_exports.object({
|
|
119650
119658
|
/** Unique identifier, kebab-case. Used as key in sessionStore. */
|
|
119651
119659
|
id: external_exports.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
@@ -119720,7 +119728,7 @@ var BotConfigSchema = external_exports.object({
|
|
|
119720
119728
|
* Agent workspace runtime: URL is a pointer only; the Agent decides clone
|
|
119721
119729
|
* timing and destination.
|
|
119722
119730
|
*/
|
|
119723
|
-
url:
|
|
119731
|
+
url: GitCloneUrlSchema.optional()
|
|
119724
119732
|
})
|
|
119725
119733
|
).default([]),
|
|
119726
119734
|
/**
|
|
@@ -120016,7 +120024,7 @@ function renderBotYaml(config) {
|
|
|
120016
120024
|
const header = [
|
|
120017
120025
|
"# Larkway bot config (L1) \u2014 generated/edited via `larkway` CLI.",
|
|
120018
120026
|
"# Credentials are env-var NAMES, never values:",
|
|
120019
|
-
"# app_secret_env /
|
|
120027
|
+
"# app_secret_env / git_token_env reference keys in ~/.larkway/.env (chmod 0600).",
|
|
120020
120028
|
"# L2 \u804C\u80FD memory lives alongside in <id>.memory.md (memory_file points at it).",
|
|
120021
120029
|
""
|
|
120022
120030
|
].join("\n");
|
|
@@ -122196,14 +122204,14 @@ async function runAdd(ctx, args) {
|
|
|
122196
122204
|
});
|
|
122197
122205
|
if (repoSlug) {
|
|
122198
122206
|
const repoBranch = await ui.prompt("\u9ED8\u8BA4\u5206\u652F:", {
|
|
122199
|
-
default: setMap.get("repo_branch") ?? "
|
|
122207
|
+
default: setMap.get("repo_branch") ?? "main",
|
|
122200
122208
|
nonInteractive: flags.nonInteractive
|
|
122201
122209
|
});
|
|
122202
|
-
const repoUrl = await ui.prompt("clone URL (\
|
|
122210
|
+
const repoUrl = await ui.prompt("clone URL (\u5EFA\u8BAE\u586B\u5199;agent \u7528\u5B83 clone):", {
|
|
122203
122211
|
default: setMap.get("repo_url") ?? "",
|
|
122204
122212
|
nonInteractive: flags.nonInteractive
|
|
122205
122213
|
});
|
|
122206
|
-
const repoEntry = { slug: repoSlug, branch: repoBranch || "
|
|
122214
|
+
const repoEntry = { slug: repoSlug, branch: repoBranch || "main" };
|
|
122207
122215
|
if (repoUrl) repoEntry.url = repoUrl;
|
|
122208
122216
|
repos.push(repoEntry);
|
|
122209
122217
|
}
|
|
@@ -122212,7 +122220,7 @@ async function runAdd(ctx, args) {
|
|
|
122212
122220
|
if (repoSlug) {
|
|
122213
122221
|
const repoEntry = {
|
|
122214
122222
|
slug: repoSlug,
|
|
122215
|
-
branch: setMap.get("repo_branch") ?? "
|
|
122223
|
+
branch: setMap.get("repo_branch") ?? "main"
|
|
122216
122224
|
};
|
|
122217
122225
|
const repoUrl = setMap.get("repo_url");
|
|
122218
122226
|
if (repoUrl) repoEntry.url = repoUrl;
|
|
@@ -122220,12 +122228,11 @@ async function runAdd(ctx, args) {
|
|
|
122220
122228
|
}
|
|
122221
122229
|
}
|
|
122222
122230
|
let gitlab_token_env;
|
|
122223
|
-
|
|
122224
|
-
if (flags.advanced || setMap.has("gitlab_token_env") || repos.length > 0) {
|
|
122231
|
+
if (flags.advanced || setMap.has("gitlab_token_env")) {
|
|
122225
122232
|
const rawGitlab = await ui.prompt(
|
|
122226
|
-
"Git access token \u73AF\u5883\u53D8\u91CF\u540D(\
|
|
122233
|
+
"Git access token \u73AF\u5883\u53D8\u91CF\u540D(\u53EF\u9009;\u79C1\u6709\u5E93/push/MR \u624D\u9700\u8981):",
|
|
122227
122234
|
{
|
|
122228
|
-
default: setMap.get("gitlab_token_env") ??
|
|
122235
|
+
default: setMap.get("gitlab_token_env") ?? "",
|
|
122229
122236
|
nonInteractive: flags.nonInteractive
|
|
122230
122237
|
}
|
|
122231
122238
|
);
|
|
@@ -122236,10 +122243,6 @@ async function runAdd(ctx, args) {
|
|
|
122236
122243
|
return 1;
|
|
122237
122244
|
}
|
|
122238
122245
|
}
|
|
122239
|
-
if (repos.length > 0 && !gitlab_token_env) {
|
|
122240
|
-
ui.failure("agent_workspace repo bot \u5FC5\u987B\u58F0\u660E git_token_env(\u6216 gitlab_token_env);v0.3 \u4E0D\u7EE7\u627F\u5168\u5C40 GITLAB_TOKEN\u3002");
|
|
122241
|
-
return 1;
|
|
122242
|
-
}
|
|
122243
122246
|
let lark_cli_profile;
|
|
122244
122247
|
if (flags.advanced || setMap.has("lark_cli_profile")) {
|
|
122245
122248
|
const rawProfile = await ui.prompt(
|
|
@@ -124003,24 +124006,16 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
124003
124006
|
import { readFile as readFile10, access as access9 } from "node:fs/promises";
|
|
124004
124007
|
import "node:module";
|
|
124005
124008
|
import path17 from "node:path";
|
|
124006
|
-
var
|
|
124007
|
-
|
|
124008
|
-
|
|
124009
|
-
function buildLatestUrl(gitlabHost, repo) {
|
|
124010
|
-
return `https://${gitlabHost}/git/${repo}/-/releases/permalink/latest/downloads/${LATEST_TGZ_NAME}`;
|
|
124009
|
+
var DEFAULT_NPM_PACKAGE_SPEC = "larkway@latest";
|
|
124010
|
+
function resolveNpmPackageSpec() {
|
|
124011
|
+
return process.env["LARKWAY_UPDATE_URL"] ?? DEFAULT_NPM_PACKAGE_SPEC;
|
|
124011
124012
|
}
|
|
124012
|
-
function
|
|
124013
|
-
const gitlabHost = process.env["GITLAB_HOST"] ?? DEFAULT_GITLAB_HOST;
|
|
124014
|
-
const repo = process.env["LARKWAY_RELEASE_REPO"] ?? DEFAULT_RELEASE_REPO;
|
|
124015
|
-
const latestUrl = process.env["LARKWAY_UPDATE_URL"] ?? buildLatestUrl(gitlabHost, repo);
|
|
124016
|
-
return { gitlabHost, repo, latestUrl };
|
|
124017
|
-
}
|
|
124018
|
-
function buildReleaseSteps(latestUrl, cwd) {
|
|
124013
|
+
function buildNpmSteps(packageSpec, cwd) {
|
|
124019
124014
|
return [
|
|
124020
124015
|
{
|
|
124021
|
-
label: `npm i -g ${
|
|
124016
|
+
label: `npm i -g ${packageSpec}`,
|
|
124022
124017
|
cmd: "npm",
|
|
124023
|
-
args: ["i", "-g",
|
|
124018
|
+
args: ["i", "-g", packageSpec],
|
|
124024
124019
|
cwd
|
|
124025
124020
|
},
|
|
124026
124021
|
{
|
|
@@ -124174,24 +124169,24 @@ async function run7(ctx, args) {
|
|
|
124174
124169
|
const { ui, flags, cwd } = ctx;
|
|
124175
124170
|
const isDryRun = args.includes("--dry-run");
|
|
124176
124171
|
const forceGitPull = args.includes("--git-pull");
|
|
124177
|
-
const
|
|
124172
|
+
const packageSpec = resolveNpmPackageSpec();
|
|
124178
124173
|
if (!forceGitPull) {
|
|
124179
|
-
const
|
|
124174
|
+
const npmSteps = buildNpmSteps(packageSpec, cwd);
|
|
124180
124175
|
const lifecycleLabels2 = /* @__PURE__ */ new Set(["larkway stop (bridge lifecycle)", "larkway start (bridge lifecycle)"]);
|
|
124181
124176
|
if (isDryRun) {
|
|
124182
124177
|
if (flags.json) {
|
|
124183
124178
|
ui.emitJson({
|
|
124184
124179
|
ok: true,
|
|
124185
124180
|
dryRun: true,
|
|
124186
|
-
mode: "
|
|
124187
|
-
|
|
124188
|
-
steps:
|
|
124181
|
+
mode: "npm",
|
|
124182
|
+
packageSpec,
|
|
124183
|
+
steps: npmSteps.map((s) => ({ label: s.label, cmd: s.cmd, args: s.args, cwd: s.cwd }))
|
|
124189
124184
|
});
|
|
124190
124185
|
} else {
|
|
124191
|
-
ui.print(ui.bold("larkway update --dry-run (
|
|
124192
|
-
ui.print(ui.dim(`
|
|
124186
|
+
ui.print(ui.bold("larkway update --dry-run (npm \u6E90\u6A21\u5F0F)"));
|
|
124187
|
+
ui.print(ui.dim(` package: ${packageSpec}`));
|
|
124193
124188
|
ui.print("");
|
|
124194
|
-
|
|
124189
|
+
npmSteps.forEach((s, i) => {
|
|
124195
124190
|
ui.print(` ${ui.cyan(String(i + 1) + ".")} ${s.label}`);
|
|
124196
124191
|
ui.print(ui.dim(` $ ${s.cmd} ${s.args.join(" ")}`));
|
|
124197
124192
|
});
|
|
@@ -124202,14 +124197,14 @@ async function run7(ctx, args) {
|
|
|
124202
124197
|
return 0;
|
|
124203
124198
|
}
|
|
124204
124199
|
if (flags.json) {
|
|
124205
|
-
ui.emitJson({ ok: true, status: "starting", mode: "
|
|
124200
|
+
ui.emitJson({ ok: true, status: "starting", mode: "npm", packageSpec, stepCount: npmSteps.length });
|
|
124206
124201
|
} else {
|
|
124207
|
-
ui.print(ui.bold("larkway update (
|
|
124208
|
-
ui.print(ui.dim(`
|
|
124202
|
+
ui.print(ui.bold("larkway update (npm \u6E90)"));
|
|
124203
|
+
ui.print(ui.dim(` package: ${packageSpec}`));
|
|
124209
124204
|
}
|
|
124210
|
-
const { ok: ok3, lifecycleWarnings: lifecycleWarnings2 } = await runSteps(
|
|
124205
|
+
const { ok: ok3, lifecycleWarnings: lifecycleWarnings2 } = await runSteps(npmSteps, lifecycleLabels2, flags, ui);
|
|
124211
124206
|
if (!ok3) {
|
|
124212
|
-
const fallbackMsg = "
|
|
124207
|
+
const fallbackMsg = "npm \u5347\u7EA7\u5931\u8D25\u3002\u82E5\u7F51\u7EDC\u4E0D\u901A\u3001npm \u6743\u9650\u4E0D\u8DB3\u6216\u6E90\u4E0D\u53EF\u7528,\u53EF\u6539\u7528 `larkway update --git-pull` \u8D70 git pull + build \u8DEF\u5F84\u3002";
|
|
124213
124208
|
if (flags.json) {
|
|
124214
124209
|
ui.emitJson({ ok: false, hint: fallbackMsg });
|
|
124215
124210
|
} else {
|
|
@@ -124223,14 +124218,14 @@ async function run7(ctx, args) {
|
|
|
124223
124218
|
if (hadWarning2) {
|
|
124224
124219
|
ui.emitJson({ ok: true, status: "complete_with_warnings", warning: lifecycleWarnings2.join("; ") });
|
|
124225
124220
|
} else {
|
|
124226
|
-
ui.emitJson({ ok: true, status: "complete", mode: "
|
|
124221
|
+
ui.emitJson({ ok: true, status: "complete", mode: "npm" });
|
|
124227
124222
|
}
|
|
124228
124223
|
} else {
|
|
124229
124224
|
ui.print("");
|
|
124230
124225
|
if (hadWarning2) {
|
|
124231
124226
|
ui.success("larkway update \u5B8C\u6210,\u4F46 bridge \u9700\u624B\u52A8\u91CD\u542F(\u8BF7\u8FD0\u884C `larkway start`)\u3002");
|
|
124232
124227
|
} else {
|
|
124233
|
-
ui.success("larkway update \u5B8C\u6210\u3002\u5DF2\u5347\u7EA7\u5230\u6700\u65B0
|
|
124228
|
+
ui.success("larkway update \u5B8C\u6210\u3002\u5DF2\u5347\u7EA7\u5230\u6700\u65B0 npm \u7248\u672C,bridge \u5DF2\u91CD\u542F\u3002");
|
|
124234
124229
|
}
|
|
124235
124230
|
}
|
|
124236
124231
|
return 0;
|
|
@@ -124556,10 +124551,10 @@ function defaultOnboardPermissionRequests(input) {
|
|
|
124556
124551
|
items.push(`Feishu chat allowlist: ${input.chats.join(", ")}`);
|
|
124557
124552
|
}
|
|
124558
124553
|
for (const repo of input.repos) {
|
|
124559
|
-
items.push(`
|
|
124554
|
+
items.push(`Git repo pointer: ${repo.slug} (${repo.branch})`);
|
|
124560
124555
|
}
|
|
124561
124556
|
if (input.gitlab_token_env) {
|
|
124562
|
-
items.push(`
|
|
124557
|
+
items.push(`Agent-level Git token env name: ${input.gitlab_token_env}`);
|
|
124563
124558
|
}
|
|
124564
124559
|
items.push("Local shell inside the Agent Workspace for task execution and verification");
|
|
124565
124560
|
return items;
|
|
@@ -124706,6 +124701,128 @@ async function cancelOnboard(sessionId) {
|
|
|
124706
124701
|
return { cancelled: true };
|
|
124707
124702
|
}
|
|
124708
124703
|
|
|
124704
|
+
// src/runtimeRequirements.ts
|
|
124705
|
+
import { execFileSync } from "node:child_process";
|
|
124706
|
+
function checkCli(command) {
|
|
124707
|
+
try {
|
|
124708
|
+
execFileSync("which", [command], { stdio: ["pipe", "pipe", "pipe"] });
|
|
124709
|
+
} catch {
|
|
124710
|
+
return { ok: false };
|
|
124711
|
+
}
|
|
124712
|
+
try {
|
|
124713
|
+
const version = execFileSync(command, ["--version"], { stdio: ["pipe", "pipe", "pipe"] }).toString().trim().split("\n")[0];
|
|
124714
|
+
return { ok: true, ...version ? { version } : {} };
|
|
124715
|
+
} catch {
|
|
124716
|
+
return { ok: true };
|
|
124717
|
+
}
|
|
124718
|
+
}
|
|
124719
|
+
function repoLooksGitLab(urlOrSlug) {
|
|
124720
|
+
return /(^|[./:@-])gitlab([./:@-]|$)/i.test(urlOrSlug);
|
|
124721
|
+
}
|
|
124722
|
+
function botUsesGitLab(bot) {
|
|
124723
|
+
return bot.repos.some((repo) => repoLooksGitLab(repo.url ?? repo.slug));
|
|
124724
|
+
}
|
|
124725
|
+
function addCliRequirement(requirements, input) {
|
|
124726
|
+
const existing = requirements.get(`cli:${input.command}`);
|
|
124727
|
+
if (existing) {
|
|
124728
|
+
existing.botIds = Array.from(/* @__PURE__ */ new Set([...existing.botIds, ...input.botIds])).sort();
|
|
124729
|
+
if (existing.severity === "optional" && input.severity === "required") {
|
|
124730
|
+
existing.severity = "required";
|
|
124731
|
+
}
|
|
124732
|
+
return;
|
|
124733
|
+
}
|
|
124734
|
+
const probe = checkCli(input.command);
|
|
124735
|
+
requirements.set(`cli:${input.command}`, {
|
|
124736
|
+
id: `cli:${input.command}`,
|
|
124737
|
+
label: input.label ?? input.command,
|
|
124738
|
+
command: input.command,
|
|
124739
|
+
kind: "cli",
|
|
124740
|
+
severity: input.severity,
|
|
124741
|
+
ok: probe.ok,
|
|
124742
|
+
...probe.version ? { version: probe.version } : {},
|
|
124743
|
+
reason: input.reason,
|
|
124744
|
+
...input.installHint ? { installHint: input.installHint } : {},
|
|
124745
|
+
botIds: Array.from(new Set(input.botIds)).sort()
|
|
124746
|
+
});
|
|
124747
|
+
}
|
|
124748
|
+
function addSecretRequirement(requirements, input) {
|
|
124749
|
+
const id = `secret:${input.envName}`;
|
|
124750
|
+
const existing = requirements.get(id);
|
|
124751
|
+
if (existing) {
|
|
124752
|
+
existing.botIds = Array.from(/* @__PURE__ */ new Set([...existing.botIds, ...input.botIds])).sort();
|
|
124753
|
+
return;
|
|
124754
|
+
}
|
|
124755
|
+
const value = process.env[input.envName];
|
|
124756
|
+
requirements.set(id, {
|
|
124757
|
+
id,
|
|
124758
|
+
label: input.envName,
|
|
124759
|
+
kind: "secret",
|
|
124760
|
+
severity: input.severity ?? "required",
|
|
124761
|
+
ok: value != null && value !== "",
|
|
124762
|
+
reason: input.reason,
|
|
124763
|
+
installHint: `Set ${input.envName} in ~/.larkway/.env and restart Larkway.`,
|
|
124764
|
+
botIds: Array.from(new Set(input.botIds)).sort()
|
|
124765
|
+
});
|
|
124766
|
+
}
|
|
124767
|
+
function runtimeRequirementsForBots(bots) {
|
|
124768
|
+
const requirements = /* @__PURE__ */ new Map();
|
|
124769
|
+
if (bots.length > 0) {
|
|
124770
|
+
addCliRequirement(requirements, {
|
|
124771
|
+
command: "lark-cli",
|
|
124772
|
+
label: "Feishu CLI",
|
|
124773
|
+
severity: "required",
|
|
124774
|
+
botIds: bots.map((bot) => bot.id),
|
|
124775
|
+
reason: "Required for agents to read Feishu topic history, attachments, docs, and other context.",
|
|
124776
|
+
installHint: "Install and configure lark-cli, then restart Larkway."
|
|
124777
|
+
});
|
|
124778
|
+
}
|
|
124779
|
+
for (const bot of bots) {
|
|
124780
|
+
addCliRequirement(requirements, {
|
|
124781
|
+
command: bot.backend ?? "claude",
|
|
124782
|
+
severity: "required",
|
|
124783
|
+
botIds: [bot.id],
|
|
124784
|
+
reason: `Required to run this bot's ${bot.backend ?? "claude"} backend.`
|
|
124785
|
+
});
|
|
124786
|
+
if (bot.runtime === "agent_workspace" && bot.repos.length > 0) {
|
|
124787
|
+
const tokenEnvName = bot.git_token_env ?? bot.gitlab_token_env;
|
|
124788
|
+
if (tokenEnvName) {
|
|
124789
|
+
addSecretRequirement(requirements, {
|
|
124790
|
+
envName: tokenEnvName,
|
|
124791
|
+
botIds: [bot.id],
|
|
124792
|
+
severity: "optional",
|
|
124793
|
+
reason: "Optional Git identity material. When absent, agents use the host's normal Git auth."
|
|
124794
|
+
});
|
|
124795
|
+
} else {
|
|
124796
|
+
requirements.set(`secret:missing-git-token:${bot.id}`, {
|
|
124797
|
+
id: `secret:missing-git-token:${bot.id}`,
|
|
124798
|
+
label: "Git access token env",
|
|
124799
|
+
kind: "secret",
|
|
124800
|
+
severity: "optional",
|
|
124801
|
+
ok: false,
|
|
124802
|
+
reason: `Bot "${bot.id}" has repo pointers but no git_token_env. It will use the host's normal Git auth.`,
|
|
124803
|
+
installHint: "Add a Git access token only when you want this agent to use a specific GitHub/GitLab identity.",
|
|
124804
|
+
botIds: [bot.id]
|
|
124805
|
+
});
|
|
124806
|
+
}
|
|
124807
|
+
}
|
|
124808
|
+
if (botUsesGitLab(bot)) {
|
|
124809
|
+
addCliRequirement(requirements, {
|
|
124810
|
+
command: "glab",
|
|
124811
|
+
label: "GitLab CLI",
|
|
124812
|
+
severity: "optional",
|
|
124813
|
+
botIds: [bot.id],
|
|
124814
|
+
reason: "Only needed when an agent uses GitLab-specific actions such as MRs, pipelines, or releases.",
|
|
124815
|
+
installHint: "Install glab only for bots that need GitLab-specific workflow commands."
|
|
124816
|
+
});
|
|
124817
|
+
}
|
|
124818
|
+
}
|
|
124819
|
+
return [...requirements.values()].sort((a, b) => {
|
|
124820
|
+
if (a.severity !== b.severity) return a.severity === "required" ? -1 : 1;
|
|
124821
|
+
if (a.kind !== b.kind) return a.kind === "cli" ? -1 : 1;
|
|
124822
|
+
return a.label.localeCompare(b.label);
|
|
124823
|
+
});
|
|
124824
|
+
}
|
|
124825
|
+
|
|
124709
124826
|
// src/web/api.ts
|
|
124710
124827
|
var execFileAsync7 = promisify8(execFileCallback2);
|
|
124711
124828
|
var CHAT_NAME_CACHE_MS = 5 * 60 * 1e3;
|
|
@@ -125395,6 +125512,22 @@ var getBridgeLogs = async (req) => {
|
|
|
125395
125512
|
json: result
|
|
125396
125513
|
};
|
|
125397
125514
|
};
|
|
125515
|
+
var getRuntimeRequirements = async (req) => {
|
|
125516
|
+
const { ctx } = req;
|
|
125517
|
+
const ids = await ctx.stores.botsStore.listBots();
|
|
125518
|
+
const bots = await Promise.all(
|
|
125519
|
+
ids.map(async (id) => ctx.stores.botsStore.readBot(id))
|
|
125520
|
+
);
|
|
125521
|
+
const requirements = runtimeRequirementsForBots(bots);
|
|
125522
|
+
return {
|
|
125523
|
+
status: 200,
|
|
125524
|
+
json: {
|
|
125525
|
+
requirements,
|
|
125526
|
+
missingRequired: requirements.filter((req2) => req2.severity === "required" && !req2.ok),
|
|
125527
|
+
missingOptional: requirements.filter((req2) => req2.severity === "optional" && !req2.ok)
|
|
125528
|
+
}
|
|
125529
|
+
};
|
|
125530
|
+
};
|
|
125398
125531
|
var BACKEND_META = {
|
|
125399
125532
|
claude: { id: "claude", name: "Claude Code", short: "Claude", vendor: "Anthropic \u8BA2\u9605", mono: "CC" },
|
|
125400
125533
|
codex: { id: "codex", name: "Codex", short: "Codex", vendor: "OpenAI \u8BA2\u9605", mono: "CX" }
|
|
@@ -125436,6 +125569,7 @@ var ROUTES = {
|
|
|
125436
125569
|
"GET /api/bridge": getBridge,
|
|
125437
125570
|
"POST /api/bridge/restart": postBridgeRestart,
|
|
125438
125571
|
"GET /api/bridge/logs": getBridgeLogs,
|
|
125572
|
+
"GET /api/runtime/requirements": getRuntimeRequirements,
|
|
125439
125573
|
"GET /api/backends": getBackends
|
|
125440
125574
|
};
|
|
125441
125575
|
function matchRoute(method, pathname, routes = ROUTES) {
|