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/dist/main.js
CHANGED
|
@@ -107360,7 +107360,6 @@ var require_main = __commonJS({
|
|
|
107360
107360
|
});
|
|
107361
107361
|
|
|
107362
107362
|
// src/main.ts
|
|
107363
|
-
import { execSync } from "node:child_process";
|
|
107364
107363
|
import { mkdirSync } from "node:fs";
|
|
107365
107364
|
import path13 from "node:path";
|
|
107366
107365
|
|
|
@@ -112529,9 +112528,11 @@ ${recent.join("\n")}` : recent.join("\n");
|
|
|
112529
112528
|
});
|
|
112530
112529
|
elements.push({ tag: "hr" });
|
|
112531
112530
|
}
|
|
112531
|
+
const mentionPrefix = atMentionMarkdown(opts.mentionOpenId);
|
|
112532
112532
|
const cleanBody = opts.bodyText ? stripLeakedToolMarkup(opts.bodyText) : "";
|
|
112533
|
-
|
|
112534
|
-
|
|
112533
|
+
const bodyWithMention = [mentionPrefix, cleanBody].filter(Boolean).join("\n\n");
|
|
112534
|
+
if (bodyWithMention) {
|
|
112535
|
+
const chunks = chunkMarkdown(bodyWithMention);
|
|
112535
112536
|
for (let i = 0; i < chunks.length; i++) {
|
|
112536
112537
|
elements.push({
|
|
112537
112538
|
tag: "markdown",
|
|
@@ -112573,6 +112574,11 @@ ${chunks[i]}`
|
|
|
112573
112574
|
};
|
|
112574
112575
|
return JSON.stringify(card);
|
|
112575
112576
|
}
|
|
112577
|
+
function atMentionMarkdown(userId) {
|
|
112578
|
+
if (!userId) return "";
|
|
112579
|
+
if (!/^[A-Za-z0-9_:-]+$/.test(userId)) return "";
|
|
112580
|
+
return `<at id=${userId}></at>`;
|
|
112581
|
+
}
|
|
112576
112582
|
var CardHandleImpl = class {
|
|
112577
112583
|
messageId;
|
|
112578
112584
|
/** Transport for the leaf PATCH network call (default = lark-cli). */
|
|
@@ -112624,6 +112630,7 @@ var CardHandleImpl = class {
|
|
|
112624
112630
|
showToolSummary: this.showToolSummary,
|
|
112625
112631
|
status: cardStatus,
|
|
112626
112632
|
failureReason: opts.failureReason,
|
|
112633
|
+
mentionOpenId: opts.mentionOpenId,
|
|
112627
112634
|
// V2 dynamic-choice buttons — agent-declared, only on finalize.
|
|
112628
112635
|
choices: opts.choices,
|
|
112629
112636
|
choicePrompt: opts.choicePrompt,
|
|
@@ -113353,6 +113360,33 @@ function renderTurnTakingHint(limit) {
|
|
|
113353
113360
|
"</turn-taking>"
|
|
113354
113361
|
];
|
|
113355
113362
|
}
|
|
113363
|
+
function renderRuntimeWarningsBlock(warnings) {
|
|
113364
|
+
if (warnings.length === 0) return [];
|
|
113365
|
+
const hasMissingLarkCli = warnings.some((warning) => warning.command === "lark-cli");
|
|
113366
|
+
return [
|
|
113367
|
+
"<runtime-warnings>",
|
|
113368
|
+
"Bridge \u68C0\u6D4B\u5230\u4EE5\u4E0B\u672C\u673A\u80FD\u529B\u6682\u4E0D\u53EF\u7528\u3002\u8FD9\u662F\u63D0\u793A,\u4E0D\u662F\u5F3A\u5236\u505C\u6B62\u6761\u4EF6:",
|
|
113369
|
+
...warnings.map((warning) => {
|
|
113370
|
+
const name = warning.command ? `${warning.label} (${warning.command})` : warning.label;
|
|
113371
|
+
const reason = warning.reason ? `: ${warning.reason}` : "";
|
|
113372
|
+
const installHint = warning.installHint ? ` Fix hint: ${warning.installHint}` : "";
|
|
113373
|
+
return `- ${name}${reason}${installHint}`;
|
|
113374
|
+
}),
|
|
113375
|
+
"",
|
|
113376
|
+
"\u5904\u7406\u539F\u5219:",
|
|
113377
|
+
"- \u80FD\u4EC5\u51ED\u5F53\u524D\u6D88\u606F\u7EE7\u7EED\u7684\u4EFB\u52A1,\u7EE7\u7EED\u5904\u7406,\u4E0D\u8981\u56E0\u4E3A warning \u76F4\u63A5\u62D2\u7EDD\u3002",
|
|
113378
|
+
"- \u53EA\u6709\u5F53\u4EFB\u52A1\u786E\u5B9E\u9700\u8981\u7F3A\u5931\u80FD\u529B\u65F6,\u518D\u5728 last_message \u91CC\u7528\u4EA7\u54C1\u5316\u8BED\u8A00\u544A\u8BC9\u7528\u6237\u7F3A\u4EC0\u4E48\u3001\u4F1A\u5F71\u54CD\u4EC0\u4E48\u3001\u5982\u4F55\u7EE7\u7EED\u3002",
|
|
113379
|
+
...hasMissingLarkCli ? [
|
|
113380
|
+
"- \u5BF9\u7F3A\u5C11 lark-cli \u7684\u60C5\u51B5:\u4E0D\u8981\u989D\u5916 @ \u7528\u6237;\u5728\u5361\u7247\u91CC\u8F7B\u91CF\u8BF4\u660E\u5F53\u524D\u65E0\u6CD5\u81EA\u52A8\u8BFB\u53D6\u98DE\u4E66\u8BDD\u9898\u5386\u53F2\u3001\u9644\u4EF6\u6216\u6587\u6863\u5373\u53EF\u3002",
|
|
113381
|
+
'- \u5982\u679C\u5F53\u524D\u4EFB\u52A1\u9700\u8981\u8FD9\u4E9B\u4E0A\u4E0B\u6587,\u7528 choices \u95EE\u662F\u5426\u5141\u8BB8\u5B89\u88C5\u6700\u65B0\u7248\u98DE\u4E66 CLI\u3002\u5EFA\u8BAE: `choice_prompt: "\u8BFB\u53D6\u98DE\u4E66\u5386\u53F2\u9700\u8981\u672C\u673A\u5B89\u88C5\u6700\u65B0\u7248\u98DE\u4E66 CLI,\u662F\u5426\u5141\u8BB8\u6211\u5C1D\u8BD5\u5B89\u88C5?"`, `choices: [{label:"\u5141\u8BB8\u5B89\u88C5", value:"\u5141\u8BB8\u5B89\u88C5 lark-cli"}, {label:"\u5148\u4E0D\u5B89\u88C5", value:"\u5148\u4E0D\u5B89\u88C5 lark-cli,\u6211\u4F1A\u628A\u8981\u5904\u7406\u7684\u5185\u5BB9\u8D34\u5230\u8BDD\u9898\u91CC"}]`\u3002',
|
|
113382
|
+
"- \u7528\u6237\u660E\u786E\u9009\u62E9/\u56DE\u590D\u5141\u8BB8\u5B89\u88C5\u540E,\u518D\u5C1D\u8BD5\u5B89\u88C5;\u4E0D\u8981\u5728\u672A\u786E\u8BA4\u524D\u6539\u5BBF\u4E3B\u673A\u5168\u5C40\u73AF\u5883\u3002",
|
|
113383
|
+
"- \u63A8\u8350\u5B89\u88C5\u547D\u4EE4: `npx -y @larksuite/cli@latest install`,\u7136\u540E\u8FD0\u884C `lark-cli --version` \u9A8C\u8BC1\u3002",
|
|
113384
|
+
'- \u5982\u9047 npm \u5168\u5C40\u76EE\u5F55\u6743\u9650\u9519\u8BEF(EACCES/permission denied),\u4F7F\u7528\u7528\u6237\u7EA7 prefix \u540E\u91CD\u8BD5: `mkdir -p ~/.npm-global && npm config set prefix "$HOME/.npm-global" && export PATH="$HOME/.npm-global/bin:$PATH" && npx -y @larksuite/cli@latest install`\u3002\u4E0D\u8981\u9ED8\u8BA4\u8981\u6C42 sudo\u3002',
|
|
113385
|
+
"- \u5B89\u88C5\u6210\u529F\u540E,\u5982\u679C\u672C\u8F6E\u9700\u8981\u7ACB\u5373\u8BFB\u53D6\u98DE\u4E66\u4E0A\u4E0B\u6587,\u53EF\u5728\u5F53\u524D shell \u4E2D\u5E26\u4E0A\u4FEE\u590D\u540E\u7684 PATH \u7EE7\u7EED\u5C1D\u8BD5;\u82E5\u9700\u8981 bridge \u540E\u7EED\u8F6E\u6B21\u7A33\u5B9A\u4F7F\u7528,\u8BF7\u63D0\u793A owner \u91CD\u542F Larkway\u3002"
|
|
113386
|
+
] : [],
|
|
113387
|
+
"</runtime-warnings>"
|
|
113388
|
+
];
|
|
113389
|
+
}
|
|
113356
113390
|
function renderWorkspaceBlock(primarySlug, primaryCachePath, defaultBranch, extraRepos) {
|
|
113357
113391
|
const lines = [
|
|
113358
113392
|
"<workspace>",
|
|
@@ -113423,7 +113457,17 @@ function sceneFacts(parsed, isNewThread) {
|
|
|
113423
113457
|
};
|
|
113424
113458
|
}
|
|
113425
113459
|
function renderPrompt(input) {
|
|
113426
|
-
const {
|
|
113460
|
+
const {
|
|
113461
|
+
parsed,
|
|
113462
|
+
isNewThread,
|
|
113463
|
+
conventions,
|
|
113464
|
+
peers,
|
|
113465
|
+
turn_taking_limit,
|
|
113466
|
+
agentMemory,
|
|
113467
|
+
extraRepoPaths,
|
|
113468
|
+
larkCliProfile,
|
|
113469
|
+
runtimeWarnings = []
|
|
113470
|
+
} = input;
|
|
113427
113471
|
const backend = input.backend ?? "claude";
|
|
113428
113472
|
const profileFlag = larkCliProfile ? ` --profile ${larkCliProfile}` : "";
|
|
113429
113473
|
const attachmentKeys = parsed.attachments.map((a) => a.fileKey);
|
|
@@ -113436,6 +113480,7 @@ function renderPrompt(input) {
|
|
|
113436
113480
|
const stateContract = renderStateContract(conventions.stateFilePath);
|
|
113437
113481
|
const peersBlock = peers && peers.length > 0 ? renderPeersBlock(peers) : [];
|
|
113438
113482
|
const turnTakingBlock = turn_taking_limit && turn_taking_limit > 0 ? renderTurnTakingHint(turn_taking_limit) : [];
|
|
113483
|
+
const runtimeWarningsBlock = renderRuntimeWarningsBlock(runtimeWarnings);
|
|
113439
113484
|
const extraRepos = extraRepoPaths ?? conventions.extraRepoPaths ?? [];
|
|
113440
113485
|
const workspaceBlock = isAgentWorkspace ? renderAgentWorkspaceBlock(conventions, extraRepos) : hasRepo ? renderWorkspaceBlock(
|
|
113441
113486
|
conventions.defaultProjectSlug ?? "repo",
|
|
@@ -113491,6 +113536,7 @@ function renderPrompt(input) {
|
|
|
113491
113536
|
"",
|
|
113492
113537
|
...agentMemoryBlock,
|
|
113493
113538
|
...skillIntroNew,
|
|
113539
|
+
...runtimeWarningsBlock.length > 0 ? [...runtimeWarningsBlock, ""] : [],
|
|
113494
113540
|
"<thread-context>",
|
|
113495
113541
|
`thread_id: ${parsed.threadId}`,
|
|
113496
113542
|
`message_id: ${parsed.messageId}`,
|
|
@@ -113558,6 +113604,7 @@ function renderPrompt(input) {
|
|
|
113558
113604
|
return [
|
|
113559
113605
|
...agentMemoryBlock,
|
|
113560
113606
|
...skillIntroCont,
|
|
113607
|
+
...runtimeWarningsBlock.length > 0 ? [...runtimeWarningsBlock, ""] : [],
|
|
113561
113608
|
"<thread-context>",
|
|
113562
113609
|
`thread_id: ${parsed.threadId}`,
|
|
113563
113610
|
`message_id: ${parsed.messageId}`,
|
|
@@ -114441,6 +114488,11 @@ var BridgeHandler = class {
|
|
|
114441
114488
|
constructor(deps) {
|
|
114442
114489
|
this.deps = deps;
|
|
114443
114490
|
}
|
|
114491
|
+
runtimeWarnings() {
|
|
114492
|
+
return (this.deps.runtimeRequirements ?? []).filter(
|
|
114493
|
+
(req) => !req.ok && (req.severity === "required" || req.kind === "secret")
|
|
114494
|
+
);
|
|
114495
|
+
}
|
|
114444
114496
|
/**
|
|
114445
114497
|
* Enter the main loop: for-await over client.events(), per-thread concurrent dispatch.
|
|
114446
114498
|
*
|
|
@@ -114742,7 +114794,8 @@ var BridgeHandler = class {
|
|
|
114742
114794
|
botName: this.deps.botConfig?.name,
|
|
114743
114795
|
backend: this.deps.botConfig?.backend,
|
|
114744
114796
|
agentMemory: this.deps.agentMemory,
|
|
114745
|
-
larkCliProfile: this.deps.larkCliProfile
|
|
114797
|
+
larkCliProfile: this.deps.larkCliProfile,
|
|
114798
|
+
runtimeWarnings: this.runtimeWarnings()
|
|
114746
114799
|
});
|
|
114747
114800
|
const backend = this.deps.botConfig?.backend ?? "claude";
|
|
114748
114801
|
const permissionMode = this.deps.permissionMode ?? (isAgentWorkspace ? "acceptEdits" : "bypassPermissions");
|
|
@@ -117852,6 +117905,14 @@ var GitIdentitySchema = external_exports.object({
|
|
|
117852
117905
|
name: external_exports.string().min(1),
|
|
117853
117906
|
email: external_exports.string().email()
|
|
117854
117907
|
});
|
|
117908
|
+
var GitCloneUrlSchema = external_exports.string().min(1).refine(
|
|
117909
|
+
(value) => {
|
|
117910
|
+
if (/^https?:\/\/\S+$/i.test(value)) return true;
|
|
117911
|
+
if (/^ssh:\/\/\S+$/i.test(value)) return true;
|
|
117912
|
+
return /^[A-Za-z0-9_.-]+@[^:\s]+:[^ \t\r\n]+$/.test(value);
|
|
117913
|
+
},
|
|
117914
|
+
"url must be an http(s), ssh://, or scp-like Git clone URL"
|
|
117915
|
+
);
|
|
117855
117916
|
var BotConfigSchema = external_exports.object({
|
|
117856
117917
|
/** Unique identifier, kebab-case. Used as key in sessionStore. */
|
|
117857
117918
|
id: external_exports.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
@@ -117926,7 +117987,7 @@ var BotConfigSchema = external_exports.object({
|
|
|
117926
117987
|
* Agent workspace runtime: URL is a pointer only; the Agent decides clone
|
|
117927
117988
|
* timing and destination.
|
|
117928
117989
|
*/
|
|
117929
|
-
url:
|
|
117990
|
+
url: GitCloneUrlSchema.optional()
|
|
117930
117991
|
})
|
|
117931
117992
|
).default([]),
|
|
117932
117993
|
/**
|
|
@@ -118284,12 +118345,12 @@ var SIGKILL_GRACE_MS = 5e3;
|
|
|
118284
118345
|
function buildEnv(botGitIdentity, gitlabToken) {
|
|
118285
118346
|
const env = { ...process.env };
|
|
118286
118347
|
delete env["ANTHROPIC_API_KEY"];
|
|
118287
|
-
|
|
118288
|
-
|
|
118289
|
-
|
|
118290
|
-
|
|
118291
|
-
|
|
118292
|
-
|
|
118348
|
+
if (botGitIdentity) {
|
|
118349
|
+
env["GIT_AUTHOR_NAME"] = botGitIdentity.name;
|
|
118350
|
+
env["GIT_AUTHOR_EMAIL"] = botGitIdentity.email;
|
|
118351
|
+
env["GIT_COMMITTER_NAME"] = botGitIdentity.name;
|
|
118352
|
+
env["GIT_COMMITTER_EMAIL"] = botGitIdentity.email;
|
|
118353
|
+
}
|
|
118293
118354
|
if (gitlabToken !== void 0) {
|
|
118294
118355
|
env["GITLAB_TOKEN"] = gitlabToken;
|
|
118295
118356
|
}
|
|
@@ -118598,12 +118659,12 @@ function buildCodexEnv(botGitIdentity, gitlabToken) {
|
|
|
118598
118659
|
const env = { ...process.env };
|
|
118599
118660
|
delete env["OPENAI_API_KEY"];
|
|
118600
118661
|
delete env["ANTHROPIC_API_KEY"];
|
|
118601
|
-
|
|
118602
|
-
|
|
118603
|
-
|
|
118604
|
-
|
|
118605
|
-
|
|
118606
|
-
|
|
118662
|
+
if (botGitIdentity) {
|
|
118663
|
+
env["GIT_AUTHOR_NAME"] = botGitIdentity.name;
|
|
118664
|
+
env["GIT_AUTHOR_EMAIL"] = botGitIdentity.email;
|
|
118665
|
+
env["GIT_COMMITTER_NAME"] = botGitIdentity.name;
|
|
118666
|
+
env["GIT_COMMITTER_EMAIL"] = botGitIdentity.email;
|
|
118667
|
+
}
|
|
118607
118668
|
if (gitlabToken !== void 0) {
|
|
118608
118669
|
env["GITLAB_TOKEN"] = gitlabToken;
|
|
118609
118670
|
}
|
|
@@ -118989,33 +119050,144 @@ function checkGrantMatchesBot(normalizedGrantText, bot) {
|
|
|
118989
119050
|
return { ok: true };
|
|
118990
119051
|
}
|
|
118991
119052
|
|
|
118992
|
-
// src/
|
|
118993
|
-
|
|
118994
|
-
|
|
118995
|
-
function checkCli(name) {
|
|
119053
|
+
// src/runtimeRequirements.ts
|
|
119054
|
+
import { execFileSync } from "node:child_process";
|
|
119055
|
+
function checkCli(command) {
|
|
118996
119056
|
try {
|
|
118997
|
-
|
|
118998
|
-
if (!p) return { ok: false, version: "" };
|
|
118999
|
-
const version = execSync(`${name} --version`, {
|
|
119000
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
119001
|
-
}).toString().trim().split("\n")[0] ?? "";
|
|
119002
|
-
return { ok: true, version };
|
|
119057
|
+
execFileSync("which", [command], { stdio: ["pipe", "pipe", "pipe"] });
|
|
119003
119058
|
} catch {
|
|
119004
|
-
return { ok: false
|
|
119059
|
+
return { ok: false };
|
|
119005
119060
|
}
|
|
119061
|
+
try {
|
|
119062
|
+
const version = execFileSync(command, ["--version"], { stdio: ["pipe", "pipe", "pipe"] }).toString().trim().split("\n")[0];
|
|
119063
|
+
return { ok: true, ...version ? { version } : {} };
|
|
119064
|
+
} catch {
|
|
119065
|
+
return { ok: true };
|
|
119066
|
+
}
|
|
119067
|
+
}
|
|
119068
|
+
function repoLooksGitLab(urlOrSlug) {
|
|
119069
|
+
return /(^|[./:@-])gitlab([./:@-]|$)/i.test(urlOrSlug);
|
|
119006
119070
|
}
|
|
119007
|
-
function
|
|
119008
|
-
|
|
119009
|
-
return Array.from(/* @__PURE__ */ new Set(["lark-cli", ...backendBins, "glab"]));
|
|
119071
|
+
function botUsesGitLab(bot) {
|
|
119072
|
+
return bot.repos.some((repo) => repoLooksGitLab(repo.url ?? repo.slug));
|
|
119010
119073
|
}
|
|
119074
|
+
function addCliRequirement(requirements, input) {
|
|
119075
|
+
const existing = requirements.get(`cli:${input.command}`);
|
|
119076
|
+
if (existing) {
|
|
119077
|
+
existing.botIds = Array.from(/* @__PURE__ */ new Set([...existing.botIds, ...input.botIds])).sort();
|
|
119078
|
+
if (existing.severity === "optional" && input.severity === "required") {
|
|
119079
|
+
existing.severity = "required";
|
|
119080
|
+
}
|
|
119081
|
+
return;
|
|
119082
|
+
}
|
|
119083
|
+
const probe = checkCli(input.command);
|
|
119084
|
+
requirements.set(`cli:${input.command}`, {
|
|
119085
|
+
id: `cli:${input.command}`,
|
|
119086
|
+
label: input.label ?? input.command,
|
|
119087
|
+
command: input.command,
|
|
119088
|
+
kind: "cli",
|
|
119089
|
+
severity: input.severity,
|
|
119090
|
+
ok: probe.ok,
|
|
119091
|
+
...probe.version ? { version: probe.version } : {},
|
|
119092
|
+
reason: input.reason,
|
|
119093
|
+
...input.installHint ? { installHint: input.installHint } : {},
|
|
119094
|
+
botIds: Array.from(new Set(input.botIds)).sort()
|
|
119095
|
+
});
|
|
119096
|
+
}
|
|
119097
|
+
function addSecretRequirement(requirements, input) {
|
|
119098
|
+
const id = `secret:${input.envName}`;
|
|
119099
|
+
const existing = requirements.get(id);
|
|
119100
|
+
if (existing) {
|
|
119101
|
+
existing.botIds = Array.from(/* @__PURE__ */ new Set([...existing.botIds, ...input.botIds])).sort();
|
|
119102
|
+
return;
|
|
119103
|
+
}
|
|
119104
|
+
const value = process.env[input.envName];
|
|
119105
|
+
requirements.set(id, {
|
|
119106
|
+
id,
|
|
119107
|
+
label: input.envName,
|
|
119108
|
+
kind: "secret",
|
|
119109
|
+
severity: input.severity ?? "required",
|
|
119110
|
+
ok: value != null && value !== "",
|
|
119111
|
+
reason: input.reason,
|
|
119112
|
+
installHint: `Set ${input.envName} in ~/.larkway/.env and restart Larkway.`,
|
|
119113
|
+
botIds: Array.from(new Set(input.botIds)).sort()
|
|
119114
|
+
});
|
|
119115
|
+
}
|
|
119116
|
+
function runtimeRequirementsForBots(bots) {
|
|
119117
|
+
const requirements = /* @__PURE__ */ new Map();
|
|
119118
|
+
if (bots.length > 0) {
|
|
119119
|
+
addCliRequirement(requirements, {
|
|
119120
|
+
command: "lark-cli",
|
|
119121
|
+
label: "Feishu CLI",
|
|
119122
|
+
severity: "required",
|
|
119123
|
+
botIds: bots.map((bot) => bot.id),
|
|
119124
|
+
reason: "Required for agents to read Feishu topic history, attachments, docs, and other context.",
|
|
119125
|
+
installHint: "Install and configure lark-cli, then restart Larkway."
|
|
119126
|
+
});
|
|
119127
|
+
}
|
|
119128
|
+
for (const bot of bots) {
|
|
119129
|
+
addCliRequirement(requirements, {
|
|
119130
|
+
command: bot.backend ?? "claude",
|
|
119131
|
+
severity: "required",
|
|
119132
|
+
botIds: [bot.id],
|
|
119133
|
+
reason: `Required to run this bot's ${bot.backend ?? "claude"} backend.`
|
|
119134
|
+
});
|
|
119135
|
+
if (bot.runtime === "agent_workspace" && bot.repos.length > 0) {
|
|
119136
|
+
const tokenEnvName = bot.git_token_env ?? bot.gitlab_token_env;
|
|
119137
|
+
if (tokenEnvName) {
|
|
119138
|
+
addSecretRequirement(requirements, {
|
|
119139
|
+
envName: tokenEnvName,
|
|
119140
|
+
botIds: [bot.id],
|
|
119141
|
+
severity: "optional",
|
|
119142
|
+
reason: "Optional Git identity material. When absent, agents use the host's normal Git auth."
|
|
119143
|
+
});
|
|
119144
|
+
} else {
|
|
119145
|
+
requirements.set(`secret:missing-git-token:${bot.id}`, {
|
|
119146
|
+
id: `secret:missing-git-token:${bot.id}`,
|
|
119147
|
+
label: "Git access token env",
|
|
119148
|
+
kind: "secret",
|
|
119149
|
+
severity: "optional",
|
|
119150
|
+
ok: false,
|
|
119151
|
+
reason: `Bot "${bot.id}" has repo pointers but no git_token_env. It will use the host's normal Git auth.`,
|
|
119152
|
+
installHint: "Add a Git access token only when you want this agent to use a specific GitHub/GitLab identity.",
|
|
119153
|
+
botIds: [bot.id]
|
|
119154
|
+
});
|
|
119155
|
+
}
|
|
119156
|
+
}
|
|
119157
|
+
if (botUsesGitLab(bot)) {
|
|
119158
|
+
addCliRequirement(requirements, {
|
|
119159
|
+
command: "glab",
|
|
119160
|
+
label: "GitLab CLI",
|
|
119161
|
+
severity: "optional",
|
|
119162
|
+
botIds: [bot.id],
|
|
119163
|
+
reason: "Only needed when an agent uses GitLab-specific actions such as MRs, pipelines, or releases.",
|
|
119164
|
+
installHint: "Install glab only for bots that need GitLab-specific workflow commands."
|
|
119165
|
+
});
|
|
119166
|
+
}
|
|
119167
|
+
}
|
|
119168
|
+
return [...requirements.values()].sort((a, b) => {
|
|
119169
|
+
if (a.severity !== b.severity) return a.severity === "required" ? -1 : 1;
|
|
119170
|
+
if (a.kind !== b.kind) return a.kind === "cli" ? -1 : 1;
|
|
119171
|
+
return a.label.localeCompare(b.label);
|
|
119172
|
+
});
|
|
119173
|
+
}
|
|
119174
|
+
|
|
119175
|
+
// src/main.ts
|
|
119176
|
+
var STATUS_WRITE_INTERVAL_MS = 3e4;
|
|
119177
|
+
var VERSION = resolveLarkwayVersion(import.meta.url);
|
|
119011
119178
|
function printExternalCliProbe(bots) {
|
|
119012
|
-
console.log("
|
|
119013
|
-
for (const
|
|
119014
|
-
const
|
|
119015
|
-
|
|
119016
|
-
|
|
119179
|
+
console.log("Runtime requirements:");
|
|
119180
|
+
for (const req of runtimeRequirementsForBots(bots)) {
|
|
119181
|
+
const icon = req.ok ? "\u2713" : "\u2717";
|
|
119182
|
+
const tag = req.severity === "optional" ? "optional" : "required";
|
|
119183
|
+
const target = req.command ?? req.label;
|
|
119184
|
+
const botScope = req.botIds.length > 0 ? ` [${req.botIds.join(", ")}]` : "";
|
|
119185
|
+
if (req.ok) {
|
|
119186
|
+
console.log(` ${target.padEnd(14)} ${icon} ${req.version ?? tag}${botScope}`);
|
|
119017
119187
|
} else {
|
|
119018
|
-
|
|
119188
|
+
const message = req.kind === "secret" ? req.reason : `not found \u2014 ${req.reason}`;
|
|
119189
|
+
console.warn(` ${target.padEnd(14)} ${icon} (${tag}) ${message}${botScope}`);
|
|
119190
|
+
if (req.installHint) console.warn(` ${"".padEnd(14)} ${req.installHint}`);
|
|
119019
119191
|
}
|
|
119020
119192
|
}
|
|
119021
119193
|
}
|
|
@@ -119049,18 +119221,16 @@ async function runV2Mode({
|
|
|
119049
119221
|
if (bot.runtime === "agent_workspace") {
|
|
119050
119222
|
const _agentTokenEnvName = bot.git_token_env ?? bot.gitlab_token_env;
|
|
119051
119223
|
if (bot.repos.length > 0 && !_agentTokenEnvName) {
|
|
119052
|
-
console.
|
|
119053
|
-
`[larkway]
|
|
119224
|
+
console.warn(
|
|
119225
|
+
`[larkway] bot "${bot.id}": runtime=agent_workspace has repo pointers but no git_token_env. Starting anyway; the agent will use the host's normal Git identity/auth.`
|
|
119054
119226
|
);
|
|
119055
|
-
continue;
|
|
119056
119227
|
}
|
|
119057
119228
|
if (_agentTokenEnvName) {
|
|
119058
119229
|
const agentToken = process.env[_agentTokenEnvName];
|
|
119059
119230
|
if (agentToken == null || agentToken === "") {
|
|
119060
|
-
console.
|
|
119061
|
-
`[larkway]
|
|
119231
|
+
console.warn(
|
|
119232
|
+
`[larkway] bot "${bot.id}": runtime=agent_workspace declares token env "${_agentTokenEnvName}", but that env var is unset/empty. Starting anyway; the agent will use the host's normal Git identity/auth.`
|
|
119062
119233
|
);
|
|
119063
|
-
continue;
|
|
119064
119234
|
}
|
|
119065
119235
|
}
|
|
119066
119236
|
const permissionGate = await checkWorkspacePermissionGrant(resolveAgentWorkspacePath(bot.id), bot);
|
|
@@ -119091,7 +119261,7 @@ async function runV2Mode({
|
|
|
119091
119261
|
const gitlabToken = tokenEnvName != null ? process.env[tokenEnvName] : void 0;
|
|
119092
119262
|
if (tokenEnvName != null && (gitlabToken == null || gitlabToken === "")) {
|
|
119093
119263
|
console.warn(
|
|
119094
|
-
`[larkway] bot "${bot.id}" declares token env "${tokenEnvName}" but that env var is unset/empty \u2014
|
|
119264
|
+
`[larkway] bot "${bot.id}" declares token env "${tokenEnvName}" but that env var is unset/empty \u2014 leaving the host Git auth environment unchanged.`
|
|
119095
119265
|
);
|
|
119096
119266
|
}
|
|
119097
119267
|
const larkCliProfile = deriveLarkCliProfile(bot.lark_cli_profile, bot.app_id);
|
|
@@ -119162,7 +119332,7 @@ async function runV2Mode({
|
|
|
119162
119332
|
readOnly: bot.read_only,
|
|
119163
119333
|
gitlabTokenEnvName: tokenEnvName
|
|
119164
119334
|
};
|
|
119165
|
-
const effectiveGitlabToken =
|
|
119335
|
+
const effectiveGitlabToken = gitlabToken;
|
|
119166
119336
|
const resolvedPeers = bot.peers.flatMap((peerId) => {
|
|
119167
119337
|
const peer = bots.find((b) => b.id === peerId);
|
|
119168
119338
|
if (!peer) return [];
|
|
@@ -119189,6 +119359,7 @@ async function runV2Mode({
|
|
|
119189
119359
|
gitlabToken: effectiveGitlabToken,
|
|
119190
119360
|
agentMemory: bot.agent_memory,
|
|
119191
119361
|
larkCliProfile,
|
|
119362
|
+
runtimeRequirements: runtimeRequirementsForBots([bot]),
|
|
119192
119363
|
recordRuntimeEvent: async (patch) => {
|
|
119193
119364
|
await upsertRuntimeEvent(larkwayHome(), bot.id, patch);
|
|
119194
119365
|
}
|