feishu-devops 26.627.1 → 26.627.2
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/dist/cli.js +413 -23
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Command } from "commander";
|
|
|
4
4
|
// package.json
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "feishu-devops",
|
|
7
|
-
version: "26.627.
|
|
7
|
+
version: "26.627.2",
|
|
8
8
|
description: "\u98DE\u4E66\u6D88\u606F bot\uFF1A\u626B\u7801\u521B\u5EFA\u5E94\u7528\uFF0C\u901A\u8FC7 Claude Code / Codex \u5904\u7406\u6D88\u606F",
|
|
9
9
|
type: "module",
|
|
10
10
|
bin: {
|
|
@@ -1926,7 +1926,7 @@ function getAgentPreflightDiagnostic(err) {
|
|
|
1926
1926
|
function isAgentPreflightDiagnostic(input) {
|
|
1927
1927
|
if (!input || typeof input !== "object") return false;
|
|
1928
1928
|
const raw = input;
|
|
1929
|
-
return typeof raw.code === "string" && raw.code.startsWith("agent-") && (raw.agentId === "claude" || raw.agentId === "codex" || raw.agentId === "cursor") && typeof raw.agentName === "string" && typeof raw.command === "string";
|
|
1929
|
+
return typeof raw.code === "string" && raw.code.startsWith("agent-") && (raw.agentId === "claude" || raw.agentId === "codex" || raw.agentId === "cursor" || raw.agentId === "pi") && typeof raw.agentName === "string" && typeof raw.command === "string";
|
|
1930
1930
|
}
|
|
1931
1931
|
function codeForSpawnError(err) {
|
|
1932
1932
|
if (err.code === "ENOENT") return "agent-binary-not-found";
|
|
@@ -2987,6 +2987,354 @@ function isWindowsCommandNotFoundLine3(line) {
|
|
|
2987
2987
|
return process.platform === "win32" && /is not recognized as an internal or external command|operable program or batch file/i.test(line);
|
|
2988
2988
|
}
|
|
2989
2989
|
|
|
2990
|
+
// src/agent/pi/adapter.ts
|
|
2991
|
+
import { createInterface as createInterface5 } from "readline";
|
|
2992
|
+
|
|
2993
|
+
// src/agent/pi/stream-json.ts
|
|
2994
|
+
function createPiStreamState() {
|
|
2995
|
+
return {
|
|
2996
|
+
sawPartialAssistantText: false,
|
|
2997
|
+
emittedToolCalls: /* @__PURE__ */ new Set(),
|
|
2998
|
+
doneEmitted: false
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
function* translateEvent3(raw, state) {
|
|
3002
|
+
if (!raw || typeof raw !== "object") return;
|
|
3003
|
+
const evt = raw;
|
|
3004
|
+
if (evt.type === "session") {
|
|
3005
|
+
const header = evt;
|
|
3006
|
+
state.sessionId = header.id;
|
|
3007
|
+
state.cwd = header.cwd;
|
|
3008
|
+
yield {
|
|
3009
|
+
type: "system",
|
|
3010
|
+
sessionId: header.id,
|
|
3011
|
+
cwd: header.cwd
|
|
3012
|
+
};
|
|
3013
|
+
return;
|
|
3014
|
+
}
|
|
3015
|
+
if (evt.type === "message_update" && evt.assistantMessageEvent) {
|
|
3016
|
+
const assistantEvt = evt.assistantMessageEvent;
|
|
3017
|
+
if (assistantEvt.type === "text_delta" && typeof assistantEvt.delta === "string" && assistantEvt.delta) {
|
|
3018
|
+
state.sawPartialAssistantText = true;
|
|
3019
|
+
yield { type: "text", delta: assistantEvt.delta };
|
|
3020
|
+
return;
|
|
3021
|
+
}
|
|
3022
|
+
if (assistantEvt.type === "thinking_delta" && typeof assistantEvt.delta === "string" && assistantEvt.delta) {
|
|
3023
|
+
yield { type: "thinking", delta: assistantEvt.delta };
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
if (assistantEvt.type === "toolcall_end" && assistantEvt.toolCall) {
|
|
3027
|
+
yield* emitToolUse(
|
|
3028
|
+
assistantEvt.toolCall.id,
|
|
3029
|
+
assistantEvt.toolCall.name,
|
|
3030
|
+
assistantEvt.toolCall.arguments,
|
|
3031
|
+
state
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
3034
|
+
return;
|
|
3035
|
+
}
|
|
3036
|
+
if (evt.type === "tool_execution_start" && evt.toolCallId && evt.toolName) {
|
|
3037
|
+
yield* emitToolUse(evt.toolCallId, evt.toolName, evt.args, state);
|
|
3038
|
+
return;
|
|
3039
|
+
}
|
|
3040
|
+
if (evt.type === "tool_execution_end" && evt.toolCallId) {
|
|
3041
|
+
const output = typeof evt.result === "string" ? evt.result : JSON.stringify(evt.result ?? "");
|
|
3042
|
+
yield {
|
|
3043
|
+
type: "tool_result",
|
|
3044
|
+
id: evt.toolCallId,
|
|
3045
|
+
output,
|
|
3046
|
+
isError: evt.isError === true
|
|
3047
|
+
};
|
|
3048
|
+
return;
|
|
3049
|
+
}
|
|
3050
|
+
if (evt.type === "message_end" && evt.message?.role === "assistant") {
|
|
3051
|
+
const message = evt.message;
|
|
3052
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
3053
|
+
yield {
|
|
3054
|
+
type: "error",
|
|
3055
|
+
message: message.errorMessage || `pi agent run ${message.stopReason}`,
|
|
3056
|
+
terminationReason: message.stopReason === "aborted" ? "interrupted" : "failed"
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3061
|
+
if (evt.type === "turn_end" && evt.message?.role === "assistant") {
|
|
3062
|
+
yield* emitUsage(evt.message.usage);
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
if (evt.type === "agent_end") {
|
|
3066
|
+
const lastAssistant = [...evt.messages ?? []].reverse().find((message) => message.role === "assistant");
|
|
3067
|
+
if (lastAssistant?.usage) {
|
|
3068
|
+
yield* emitUsage(lastAssistant.usage);
|
|
3069
|
+
}
|
|
3070
|
+
if (lastAssistant?.stopReason === "error" || lastAssistant?.stopReason === "aborted") {
|
|
3071
|
+
yield {
|
|
3072
|
+
type: "error",
|
|
3073
|
+
message: lastAssistant.errorMessage || `pi agent run ${lastAssistant.stopReason}`,
|
|
3074
|
+
terminationReason: lastAssistant.stopReason === "aborted" ? "interrupted" : "failed"
|
|
3075
|
+
};
|
|
3076
|
+
return;
|
|
3077
|
+
}
|
|
3078
|
+
if (!state.doneEmitted) {
|
|
3079
|
+
state.doneEmitted = true;
|
|
3080
|
+
yield {
|
|
3081
|
+
type: "done",
|
|
3082
|
+
sessionId: state.sessionId,
|
|
3083
|
+
terminationReason: "normal"
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
function* emitToolUse(id, name, input, state) {
|
|
3089
|
+
if (!id || !name || state.emittedToolCalls.has(id)) return;
|
|
3090
|
+
state.emittedToolCalls.add(id);
|
|
3091
|
+
yield { type: "tool_use", id, name, input };
|
|
3092
|
+
}
|
|
3093
|
+
function* emitUsage(usage) {
|
|
3094
|
+
if (!usage) return;
|
|
3095
|
+
yield {
|
|
3096
|
+
type: "usage",
|
|
3097
|
+
inputTokens: usage.input,
|
|
3098
|
+
outputTokens: usage.output,
|
|
3099
|
+
cachedInputTokens: usage.cacheRead,
|
|
3100
|
+
costUsd: usage.cost?.total
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
// src/agent/pi/adapter.ts
|
|
3105
|
+
function isPiDebugEnabled(explicit) {
|
|
3106
|
+
if (explicit === true) return true;
|
|
3107
|
+
if (explicit === false) return false;
|
|
3108
|
+
const raw = process.env.FEISHU_DEVOPS_PI_DEBUG ?? process.env.LARK_CHANNEL_PI_DEBUG;
|
|
3109
|
+
return raw === "1" || raw === "true";
|
|
3110
|
+
}
|
|
3111
|
+
var PiAdapter = class {
|
|
3112
|
+
id = "pi";
|
|
3113
|
+
displayName = "Pi Agent";
|
|
3114
|
+
binary;
|
|
3115
|
+
larkChannel;
|
|
3116
|
+
debug;
|
|
3117
|
+
botIdentity;
|
|
3118
|
+
constructor(opts = {}) {
|
|
3119
|
+
this.binary = opts.binary ?? "pi";
|
|
3120
|
+
this.larkChannel = opts.larkChannel;
|
|
3121
|
+
this.debug = isPiDebugEnabled(opts.debug);
|
|
3122
|
+
}
|
|
3123
|
+
setBotIdentity(identity) {
|
|
3124
|
+
this.botIdentity = identity;
|
|
3125
|
+
}
|
|
3126
|
+
async isAvailable() {
|
|
3127
|
+
return (await this.checkAvailability()).ok;
|
|
3128
|
+
}
|
|
3129
|
+
async checkAvailability() {
|
|
3130
|
+
return checkAgentAvailability({
|
|
3131
|
+
agentId: "pi",
|
|
3132
|
+
agentName: "Pi Agent",
|
|
3133
|
+
command: this.binary,
|
|
3134
|
+
binaryPath: this.binary,
|
|
3135
|
+
args: ["--version"]
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
run(opts) {
|
|
3139
|
+
if (!opts.cwd) {
|
|
3140
|
+
throw new Error("cwd is required for PiAdapter.run");
|
|
3141
|
+
}
|
|
3142
|
+
const systemPrompt = buildBridgeSystemPrompt(this.botIdentity);
|
|
3143
|
+
const args = ["--mode", "json", "-p"];
|
|
3144
|
+
if (systemPrompt) {
|
|
3145
|
+
args.push("--append-system-prompt", systemPrompt);
|
|
3146
|
+
}
|
|
3147
|
+
if (opts.sessionId) args.push("--session-id", opts.sessionId);
|
|
3148
|
+
if (opts.model) args.push("--model", opts.model);
|
|
3149
|
+
args.push(opts.prompt);
|
|
3150
|
+
if (this.debug) {
|
|
3151
|
+
console.log("[pi debug] spawn", {
|
|
3152
|
+
binary: this.binary,
|
|
3153
|
+
cwd: opts.cwd,
|
|
3154
|
+
args: formatDebugArgs2(args)
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
const child = spawnProcess(this.binary, args, {
|
|
3158
|
+
cwd: opts.cwd,
|
|
3159
|
+
env: mergeProcessEnv(process.env, buildLarkChannelEnv(this.larkChannel)),
|
|
3160
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3161
|
+
});
|
|
3162
|
+
log.info("agent", "spawn", {
|
|
3163
|
+
pid: child.pid ?? null,
|
|
3164
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
3165
|
+
hasSession: Boolean(opts.sessionId),
|
|
3166
|
+
promptChars: opts.prompt.length,
|
|
3167
|
+
model: opts.model,
|
|
3168
|
+
agent: "pi"
|
|
3169
|
+
});
|
|
3170
|
+
const stderrChunks = [];
|
|
3171
|
+
let runtimeError = null;
|
|
3172
|
+
let stderrBuffer = "";
|
|
3173
|
+
child.stderr.on("data", (chunk) => {
|
|
3174
|
+
stderrChunks.push(chunk);
|
|
3175
|
+
stderrBuffer += chunk.toString("utf8");
|
|
3176
|
+
let nl = stderrBuffer.indexOf("\n");
|
|
3177
|
+
while (nl !== -1) {
|
|
3178
|
+
const line = stderrBuffer.slice(0, nl);
|
|
3179
|
+
stderrBuffer = stderrBuffer.slice(nl + 1);
|
|
3180
|
+
if (line.trim()) log.warn("agent", "stderr", { line, agent: "pi" });
|
|
3181
|
+
if (isWindowsCommandNotFoundLine4(line)) {
|
|
3182
|
+
runtimeError = new Error(`failed to spawn pi: ${line.trim()}`);
|
|
3183
|
+
child.stdout.destroy();
|
|
3184
|
+
child.kill();
|
|
3185
|
+
}
|
|
3186
|
+
nl = stderrBuffer.indexOf("\n");
|
|
3187
|
+
}
|
|
3188
|
+
});
|
|
3189
|
+
child.on("error", (err) => {
|
|
3190
|
+
runtimeError = err;
|
|
3191
|
+
});
|
|
3192
|
+
child.on("exit", (code, signal) => {
|
|
3193
|
+
log.info("agent", "exit", { pid: child.pid ?? null, code, signal, agent: "pi" });
|
|
3194
|
+
});
|
|
3195
|
+
const stopGraceMs = opts.stopGraceMs ?? 5e3;
|
|
3196
|
+
return {
|
|
3197
|
+
runId: opts.runId,
|
|
3198
|
+
events: createEventStream4(child, stderrChunks, () => runtimeError, this.debug),
|
|
3199
|
+
async stop() {
|
|
3200
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
3201
|
+
log.info("agent", "stop-sigterm", {
|
|
3202
|
+
pid: child.pid ?? null,
|
|
3203
|
+
graceMs: stopGraceMs,
|
|
3204
|
+
agent: "pi"
|
|
3205
|
+
});
|
|
3206
|
+
child.kill("SIGTERM");
|
|
3207
|
+
await new Promise((resolve3) => {
|
|
3208
|
+
const timer = setTimeout(() => {
|
|
3209
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
3210
|
+
log.warn("agent", "stop-sigkill", {
|
|
3211
|
+
pid: child.pid ?? null,
|
|
3212
|
+
graceMs: stopGraceMs,
|
|
3213
|
+
reason: "grace-period-expired",
|
|
3214
|
+
agent: "pi"
|
|
3215
|
+
});
|
|
3216
|
+
child.kill("SIGKILL");
|
|
3217
|
+
}
|
|
3218
|
+
resolve3();
|
|
3219
|
+
}, stopGraceMs);
|
|
3220
|
+
child.once("exit", () => {
|
|
3221
|
+
clearTimeout(timer);
|
|
3222
|
+
resolve3();
|
|
3223
|
+
});
|
|
3224
|
+
});
|
|
3225
|
+
},
|
|
3226
|
+
waitForExit(timeoutMs) {
|
|
3227
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
3228
|
+
return Promise.resolve(true);
|
|
3229
|
+
}
|
|
3230
|
+
return new Promise((resolve3) => {
|
|
3231
|
+
const onExit = () => {
|
|
3232
|
+
clearTimeout(timer);
|
|
3233
|
+
resolve3(true);
|
|
3234
|
+
};
|
|
3235
|
+
const timer = setTimeout(() => {
|
|
3236
|
+
child.removeListener("exit", onExit);
|
|
3237
|
+
resolve3(false);
|
|
3238
|
+
}, timeoutMs);
|
|
3239
|
+
child.once("exit", onExit);
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
};
|
|
3245
|
+
async function* createEventStream4(child, stderrChunks, getError, debug) {
|
|
3246
|
+
if (!child.pid) {
|
|
3247
|
+
const err = getError();
|
|
3248
|
+
yield {
|
|
3249
|
+
type: "error",
|
|
3250
|
+
message: err ? `failed to spawn pi: ${err.message}` : "spawn returned no pid",
|
|
3251
|
+
terminationReason: "failed"
|
|
3252
|
+
};
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
const streamState = createPiStreamState();
|
|
3256
|
+
const rl = createInterface5({ input: child.stdout, crlfDelay: Infinity });
|
|
3257
|
+
let sawStdout = false;
|
|
3258
|
+
let silentExitTimer;
|
|
3259
|
+
const closeSilentStdout = () => {
|
|
3260
|
+
silentExitTimer = setTimeout(() => {
|
|
3261
|
+
if (!sawStdout && !child.stdout.readableEnded) child.stdout.destroy();
|
|
3262
|
+
}, 50);
|
|
3263
|
+
};
|
|
3264
|
+
child.once("exit", closeSilentStdout);
|
|
3265
|
+
try {
|
|
3266
|
+
for await (const line of rl) {
|
|
3267
|
+
sawStdout = true;
|
|
3268
|
+
const trimmed = line.trim();
|
|
3269
|
+
if (!trimmed) continue;
|
|
3270
|
+
let parsed;
|
|
3271
|
+
try {
|
|
3272
|
+
parsed = JSON.parse(trimmed);
|
|
3273
|
+
} catch {
|
|
3274
|
+
continue;
|
|
3275
|
+
}
|
|
3276
|
+
if (debug) {
|
|
3277
|
+
console.log("[pi debug] event", parsed);
|
|
3278
|
+
}
|
|
3279
|
+
yield* translateEvent3(parsed, streamState);
|
|
3280
|
+
}
|
|
3281
|
+
} finally {
|
|
3282
|
+
if (silentExitTimer) clearTimeout(silentExitTimer);
|
|
3283
|
+
child.removeListener("exit", closeSilentStdout);
|
|
3284
|
+
rl.close();
|
|
3285
|
+
}
|
|
3286
|
+
const earlyRuntimeError = getError();
|
|
3287
|
+
if (earlyRuntimeError && child.exitCode === null && child.signalCode === null) {
|
|
3288
|
+
yield {
|
|
3289
|
+
type: "error",
|
|
3290
|
+
message: `pi runtime error: ${earlyRuntimeError.message}`,
|
|
3291
|
+
terminationReason: "failed"
|
|
3292
|
+
};
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
const exitCode = await new Promise((resolve3) => {
|
|
3296
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
3297
|
+
resolve3(child.exitCode);
|
|
3298
|
+
} else {
|
|
3299
|
+
child.once("exit", (code) => resolve3(code));
|
|
3300
|
+
}
|
|
3301
|
+
});
|
|
3302
|
+
const runtimeError = getError();
|
|
3303
|
+
if (!streamState.doneEmitted && exitCode === 0) {
|
|
3304
|
+
yield {
|
|
3305
|
+
type: "done",
|
|
3306
|
+
sessionId: streamState.sessionId,
|
|
3307
|
+
terminationReason: "normal"
|
|
3308
|
+
};
|
|
3309
|
+
} else if (exitCode !== 0 && exitCode !== null) {
|
|
3310
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
|
|
3311
|
+
const detail = stderr ? `: ${stderr.slice(0, 500)}` : "";
|
|
3312
|
+
yield {
|
|
3313
|
+
type: "error",
|
|
3314
|
+
message: `pi agent exited with code ${exitCode}${detail}`,
|
|
3315
|
+
terminationReason: "failed"
|
|
3316
|
+
};
|
|
3317
|
+
} else if (runtimeError) {
|
|
3318
|
+
yield {
|
|
3319
|
+
type: "error",
|
|
3320
|
+
message: `pi runtime error: ${runtimeError.message}`,
|
|
3321
|
+
terminationReason: "failed"
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
function formatDebugArgs2(args) {
|
|
3326
|
+
const promptIndex = args.length - 1;
|
|
3327
|
+
if (promptIndex < 0) return args;
|
|
3328
|
+
const prompt = args[promptIndex];
|
|
3329
|
+
if (typeof prompt !== "string" || prompt.length <= 200) return args;
|
|
3330
|
+
const copy = [...args];
|
|
3331
|
+
copy[promptIndex] = `${prompt.slice(0, 200)}\u2026 (${prompt.length} chars)`;
|
|
3332
|
+
return copy;
|
|
3333
|
+
}
|
|
3334
|
+
function isWindowsCommandNotFoundLine4(line) {
|
|
3335
|
+
return process.platform === "win32" && /is not recognized as an internal or external command|operable program or batch file/i.test(line);
|
|
3336
|
+
}
|
|
3337
|
+
|
|
2990
3338
|
// src/config/permissions.ts
|
|
2991
3339
|
var ACCESS_ORDER = {
|
|
2992
3340
|
"read-only": 0,
|
|
@@ -3188,8 +3536,8 @@ function normalizeProfileConfig(input) {
|
|
|
3188
3536
|
if (raw.schemaVersion !== 2) {
|
|
3189
3537
|
throw new Error("profile schemaVersion must be 2");
|
|
3190
3538
|
}
|
|
3191
|
-
if (raw.agentKind !== "claude" && raw.agentKind !== "codex" && raw.agentKind !== "cursor") {
|
|
3192
|
-
throw new Error("agentKind must be claude, codex, or
|
|
3539
|
+
if (raw.agentKind !== "claude" && raw.agentKind !== "codex" && raw.agentKind !== "cursor" && raw.agentKind !== "pi") {
|
|
3540
|
+
throw new Error("agentKind must be claude, codex, cursor, or pi");
|
|
3193
3541
|
}
|
|
3194
3542
|
const accounts = normalizeAccounts(raw.accounts);
|
|
3195
3543
|
if (raw.agentKind === "codex" && !raw.codex) {
|
|
@@ -3333,7 +3681,7 @@ function numberOr(value, fallback) {
|
|
|
3333
3681
|
|
|
3334
3682
|
// src/config/agent-config.ts
|
|
3335
3683
|
function toProfileConfig(cfg) {
|
|
3336
|
-
const agentKind = cfg.agentKind === "codex" ? "codex" : cfg.agentKind === "cursor" ? "cursor" : "claude";
|
|
3684
|
+
const agentKind = cfg.agentKind === "codex" ? "codex" : cfg.agentKind === "cursor" ? "cursor" : cfg.agentKind === "pi" ? "pi" : "claude";
|
|
3337
3685
|
const base = {
|
|
3338
3686
|
schemaVersion: 2,
|
|
3339
3687
|
agentKind,
|
|
@@ -3426,6 +3774,14 @@ function createRuntimeAgent(cfg, opts = {}) {
|
|
|
3426
3774
|
...opts.cursorDebug === true ? { debug: true } : {}
|
|
3427
3775
|
});
|
|
3428
3776
|
}
|
|
3777
|
+
if (profileConfig.agentKind === "pi") {
|
|
3778
|
+
const command = process.env.FEISHU_DEVOPS_PI_BIN ?? process.env.LARK_CHANNEL_PI_BIN ?? "pi";
|
|
3779
|
+
return new PiAdapter({
|
|
3780
|
+
binary: command,
|
|
3781
|
+
larkChannel,
|
|
3782
|
+
...opts.cursorDebug === true ? { debug: true } : {}
|
|
3783
|
+
});
|
|
3784
|
+
}
|
|
3429
3785
|
return new ClaudeAdapter({ larkChannel });
|
|
3430
3786
|
}
|
|
3431
3787
|
async function checkRuntimeAgentAvailability(agent) {
|
|
@@ -3453,6 +3809,10 @@ async function resolveCursorBinaryPath() {
|
|
|
3453
3809
|
const command = process.env.FEISHU_DEVOPS_CURSOR_BIN ?? process.env.LARK_CHANNEL_CURSOR_BIN ?? "agent";
|
|
3454
3810
|
return resolveExecutablePath(command);
|
|
3455
3811
|
}
|
|
3812
|
+
async function resolvePiBinaryPath() {
|
|
3813
|
+
const command = process.env.FEISHU_DEVOPS_PI_BIN ?? process.env.LARK_CHANNEL_PI_BIN ?? "pi";
|
|
3814
|
+
return resolveExecutablePath(command);
|
|
3815
|
+
}
|
|
3456
3816
|
function applyAgentKindToConfig(cfg, agentKind, codexBinaryPath) {
|
|
3457
3817
|
const next = { ...cfg, agentKind };
|
|
3458
3818
|
if (agentKind === "codex" && codexBinaryPath) {
|
|
@@ -4006,7 +4366,7 @@ var SessionCatalog = class {
|
|
|
4006
4366
|
function normalizeEntry(input) {
|
|
4007
4367
|
if (!input || typeof input !== "object") return void 0;
|
|
4008
4368
|
const raw = input;
|
|
4009
|
-
if (typeof raw.key !== "string" || typeof raw.scopeId !== "string" || raw.agentId !== "claude" && raw.agentId !== "codex" && raw.agentId !== "cursor" || typeof raw.cwdRealpath !== "string" || typeof raw.policyFingerprint !== "string" || raw.status !== "active" && raw.status !== "archived" || typeof raw.updatedAt !== "number") {
|
|
4369
|
+
if (typeof raw.key !== "string" || typeof raw.scopeId !== "string" || raw.agentId !== "claude" && raw.agentId !== "codex" && raw.agentId !== "cursor" && raw.agentId !== "pi" || typeof raw.cwdRealpath !== "string" || typeof raw.policyFingerprint !== "string" || raw.status !== "active" && raw.status !== "archived" || typeof raw.updatedAt !== "number") {
|
|
4010
4370
|
return void 0;
|
|
4011
4371
|
}
|
|
4012
4372
|
return {
|
|
@@ -4026,15 +4386,15 @@ function matchesIdentity(entry, input) {
|
|
|
4026
4386
|
return entry.scopeId === input.scopeId && entry.agentId === input.agentId && entry.cwdRealpath === input.cwdRealpath && entry.policyFingerprint === input.policyFingerprint && entry.key === sessionCatalogKey(input);
|
|
4027
4387
|
}
|
|
4028
4388
|
function isValidAgentEntry(entry) {
|
|
4029
|
-
if (entry.agentId === "claude" || entry.agentId === "cursor") {
|
|
4389
|
+
if (entry.agentId === "claude" || entry.agentId === "cursor" || entry.agentId === "pi") {
|
|
4030
4390
|
return Boolean(entry.sessionId) && !entry.threadId;
|
|
4031
4391
|
}
|
|
4032
4392
|
return Boolean(entry.threadId) && !entry.sessionId;
|
|
4033
4393
|
}
|
|
4034
4394
|
function assertAgentIdentity(input) {
|
|
4035
|
-
if (input.agentId === "claude" || input.agentId === "cursor") {
|
|
4395
|
+
if (input.agentId === "claude" || input.agentId === "cursor" || input.agentId === "pi") {
|
|
4036
4396
|
if (!input.sessionId || input.threadId) {
|
|
4037
|
-
throw new Error("Claude/Cursor catalog entries require sessionId and must not include threadId");
|
|
4397
|
+
throw new Error("Claude/Cursor/Pi catalog entries require sessionId and must not include threadId");
|
|
4038
4398
|
}
|
|
4039
4399
|
return;
|
|
4040
4400
|
}
|
|
@@ -4231,6 +4591,23 @@ function cursorCapability(profile) {
|
|
|
4231
4591
|
}
|
|
4232
4592
|
};
|
|
4233
4593
|
}
|
|
4594
|
+
function piCapability(profile) {
|
|
4595
|
+
const maxAccess = profile?.permissions.maxAccess ?? "full";
|
|
4596
|
+
return {
|
|
4597
|
+
agentId: "pi",
|
|
4598
|
+
sessionKind: "pi-session",
|
|
4599
|
+
promptInjection: "append-system-prompt",
|
|
4600
|
+
systemPrompt: BRIDGE_SYSTEM_PROMPT,
|
|
4601
|
+
supportsNativeHistory: true,
|
|
4602
|
+
callback: {
|
|
4603
|
+
marker: "__bridge_cb",
|
|
4604
|
+
legacyMarkers: []
|
|
4605
|
+
},
|
|
4606
|
+
permissions: {
|
|
4607
|
+
maxAccess
|
|
4608
|
+
}
|
|
4609
|
+
};
|
|
4610
|
+
}
|
|
4234
4611
|
function codexCapability(profile) {
|
|
4235
4612
|
const maxAccess = profile.permissions.maxAccess;
|
|
4236
4613
|
return {
|
|
@@ -4893,9 +5270,6 @@ function classifyHighRiskWorkingDirectory(real, requestedCwd, tempRealpath) {
|
|
|
4893
5270
|
return reject2("filesystem-root", requestedCwd, "\u4E0D\u80FD\u628A\u6587\u4EF6\u7CFB\u7EDF\u6839\u76EE\u5F55\u8BBE\u4E3A\u5DE5\u4F5C\u76EE\u5F55\u3002");
|
|
4894
5271
|
}
|
|
4895
5272
|
const home = resolve2(homedir4());
|
|
4896
|
-
if (real === home) {
|
|
4897
|
-
return reject2("home-root", requestedCwd, "\u4E0D\u80FD\u628A Home \u6839\u76EE\u5F55\u8BBE\u4E3A\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u8BF7\u9009\u62E9\u66F4\u5177\u4F53\u7684\u5B50\u76EE\u5F55\u3002");
|
|
4898
|
-
}
|
|
4899
5273
|
if (real === dirname7(home)) {
|
|
4900
5274
|
return reject2("user-root", requestedCwd, "\u4E0D\u80FD\u628A\u7528\u6237\u76EE\u5F55\u6839\u8BBE\u4E3A\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u8BF7\u9009\u62E9\u66F4\u5177\u4F53\u7684\u5B50\u76EE\u5F55\u3002");
|
|
4901
5275
|
}
|
|
@@ -4970,7 +5344,7 @@ async function startRunFlow(input) {
|
|
|
4970
5344
|
cwdRealpath: workspace.cwdRealpath,
|
|
4971
5345
|
policyFingerprint: policy.policyFingerprint
|
|
4972
5346
|
});
|
|
4973
|
-
if (catalogEntry?.agentId === "claude" || catalogEntry?.agentId === "cursor") {
|
|
5347
|
+
if (catalogEntry?.agentId === "claude" || catalogEntry?.agentId === "cursor" || catalogEntry?.agentId === "pi") {
|
|
4974
5348
|
sessionId = catalogEntry.sessionId;
|
|
4975
5349
|
resumeFrom = sessionId;
|
|
4976
5350
|
} else if (catalogEntry?.agentId === "codex") {
|
|
@@ -4978,7 +5352,7 @@ async function startRunFlow(input) {
|
|
|
4978
5352
|
resumeFrom = threadId;
|
|
4979
5353
|
}
|
|
4980
5354
|
}
|
|
4981
|
-
if (!resumeFrom && (input.capability.agentId === "claude" || input.capability.agentId === "cursor")) {
|
|
5355
|
+
if (!resumeFrom && (input.capability.agentId === "claude" || input.capability.agentId === "cursor" || input.capability.agentId === "pi")) {
|
|
4982
5356
|
resumeFrom = input.sessions.resumeFor(input.scopeId, workspace.cwdRealpath);
|
|
4983
5357
|
sessionId = resumeFrom;
|
|
4984
5358
|
const stale = input.sessions.getRaw(input.scopeId);
|
|
@@ -5020,7 +5394,7 @@ async function startRunFlow(input) {
|
|
|
5020
5394
|
}
|
|
5021
5395
|
function recordRunSessionEvent(input) {
|
|
5022
5396
|
if (input.event.type !== "system") return;
|
|
5023
|
-
if ((input.capability.agentId === "claude" || input.capability.agentId === "cursor") && input.event.sessionId) {
|
|
5397
|
+
if ((input.capability.agentId === "claude" || input.capability.agentId === "cursor" || input.capability.agentId === "pi") && input.event.sessionId) {
|
|
5024
5398
|
const cwdRealpath = input.event.cwd ?? input.policy.cwdRealpath;
|
|
5025
5399
|
input.sessions.set(input.scopeId, input.event.sessionId, cwdRealpath);
|
|
5026
5400
|
input.sessionCatalog?.upsertActive({
|
|
@@ -5072,7 +5446,7 @@ async function runAgentMessage(deps, msg) {
|
|
|
5072
5446
|
replyTo: msg.messageId,
|
|
5073
5447
|
...msg.threadId ? { replyInThread: true } : {}
|
|
5074
5448
|
};
|
|
5075
|
-
const capability = profileConfig.agentKind === "codex" ? codexCapability(profileConfig) : profileConfig.agentKind === "cursor" ? cursorCapability(profileConfig) : claudeCapability(profileConfig);
|
|
5449
|
+
const capability = profileConfig.agentKind === "codex" ? codexCapability(profileConfig) : profileConfig.agentKind === "cursor" ? cursorCapability(profileConfig) : profileConfig.agentKind === "pi" ? piCapability(profileConfig) : claudeCapability(profileConfig);
|
|
5076
5450
|
const scopeContext = {
|
|
5077
5451
|
source: "im",
|
|
5078
5452
|
chatId: msg.chatId,
|
|
@@ -5439,6 +5813,9 @@ async function buildConfigForKind(cfg, kind) {
|
|
|
5439
5813
|
if (kind === "cursor") {
|
|
5440
5814
|
return applyAgentKindToConfig(cfg, "cursor");
|
|
5441
5815
|
}
|
|
5816
|
+
if (kind === "pi") {
|
|
5817
|
+
return applyAgentKindToConfig(cfg, "pi");
|
|
5818
|
+
}
|
|
5442
5819
|
return applyAgentKindToConfig(cfg, "claude");
|
|
5443
5820
|
}
|
|
5444
5821
|
function clearAgentSessionState(sessions, sessionCatalog) {
|
|
@@ -5458,6 +5835,7 @@ function clearAgentSessionState(sessions, sessionCatalog) {
|
|
|
5458
5835
|
function agentLabel(kind) {
|
|
5459
5836
|
if (kind === "codex") return "Codex CLI";
|
|
5460
5837
|
if (kind === "cursor") return "Cursor Agent";
|
|
5838
|
+
if (kind === "pi") return "Pi Agent";
|
|
5461
5839
|
return "Claude Code";
|
|
5462
5840
|
}
|
|
5463
5841
|
|
|
@@ -5565,7 +5943,7 @@ function helpCard(agentName = "Agent") {
|
|
|
5565
5943
|
"- `/cwd view` \u2014 \u67E5\u770B\u5DE5\u4F5C\u76EE\u5F55",
|
|
5566
5944
|
"- `/status` \u2014 \u5F53\u524D\u72B6\u6001",
|
|
5567
5945
|
"- `/stop` \u2014 \u7ED3\u675F\u5F53\u524D\u6B63\u5728\u8DD1\u7684\u4EFB\u52A1",
|
|
5568
|
-
"- `/use claude|codex|cursor` \u2014 \u5207\u6362 agent\uFF08\u65E0\u9700\u91CD\u542F\uFF09",
|
|
5946
|
+
"- `/use claude|codex|cursor|pi` \u2014 \u5207\u6362 agent\uFF08\u65E0\u9700\u91CD\u542F\uFF09",
|
|
5569
5947
|
"- `/cmd <shell>` / `$ <shell>` \u2014 \u672C\u673A\u6267\u884C shell",
|
|
5570
5948
|
"- `/help` \u2014 \u672C\u5E2E\u52A9",
|
|
5571
5949
|
"",
|
|
@@ -5990,7 +6368,7 @@ async function handleResume(args, ctx) {
|
|
|
5990
6368
|
const entries = sessionId ? [
|
|
5991
6369
|
{
|
|
5992
6370
|
sessionId,
|
|
5993
|
-
preview: isCodex ? "\u5F53\u524D Codex thread" : profile?.agentKind === "cursor" ? "\u5F53\u524D Cursor \u4F1A\u8BDD" : "\u5F53\u524D Claude \u4F1A\u8BDD",
|
|
6371
|
+
preview: isCodex ? "\u5F53\u524D Codex thread" : profile?.agentKind === "cursor" ? "\u5F53\u524D Cursor \u4F1A\u8BDD" : profile?.agentKind === "pi" ? "\u5F53\u524D Pi \u4F1A\u8BDD" : "\u5F53\u524D Claude \u4F1A\u8BDD",
|
|
5994
6372
|
relTime: "\u5F53\u524D",
|
|
5995
6373
|
current: true
|
|
5996
6374
|
}
|
|
@@ -6018,7 +6396,7 @@ async function handleResumeUse(sessionId, ctx) {
|
|
|
6018
6396
|
return;
|
|
6019
6397
|
}
|
|
6020
6398
|
ctx.sessions?.set(ctx.scope, sessionId, cwd);
|
|
6021
|
-
const agentLabel2 = profile?.agentKind === "cursor" ? "Cursor" : "Claude";
|
|
6399
|
+
const agentLabel2 = profile?.agentKind === "cursor" ? "Cursor" : profile?.agentKind === "pi" ? "Pi" : "Claude";
|
|
6022
6400
|
await replyMarkdown(ctx, `\u2713 \u5DF2\u6062\u590D ${agentLabel2} \u4F1A\u8BDD\uFF0C\u8BF7\u7EE7\u7EED\u53D1\u9001\u6D88\u606F\u3002`);
|
|
6023
6401
|
}
|
|
6024
6402
|
async function handleStop(_args, ctx) {
|
|
@@ -6034,7 +6412,7 @@ async function handleSwitch(args, ctx) {
|
|
|
6034
6412
|
}
|
|
6035
6413
|
const kind = args.trim().toLowerCase();
|
|
6036
6414
|
if (!isSwitchableAgentKind(kind)) {
|
|
6037
|
-
await replyText(ctx, "\u7528\u6CD5: `/use claude` | `/use codex` | `/use cursor`");
|
|
6415
|
+
await replyText(ctx, "\u7528\u6CD5: `/use claude` | `/use codex` | `/use cursor` | `/use pi`");
|
|
6038
6416
|
return;
|
|
6039
6417
|
}
|
|
6040
6418
|
const current = ctx.runtime.fullCfg.agentKind ?? "claude";
|
|
@@ -6067,7 +6445,7 @@ async function handleSwitch(args, ctx) {
|
|
|
6067
6445
|
);
|
|
6068
6446
|
}
|
|
6069
6447
|
function isSwitchableAgentKind(value) {
|
|
6070
|
-
return value === "claude" || value === "codex" || value === "cursor";
|
|
6448
|
+
return value === "claude" || value === "codex" || value === "cursor" || value === "pi";
|
|
6071
6449
|
}
|
|
6072
6450
|
function resolveStatusAgentName(ctx) {
|
|
6073
6451
|
if (ctx.runtime?.agentUnavailable) {
|
|
@@ -6090,6 +6468,9 @@ function runtimeAccessStatus(profile) {
|
|
|
6090
6468
|
if (profile.agentKind === "cursor") {
|
|
6091
6469
|
return { label: "mode", value: "force" };
|
|
6092
6470
|
}
|
|
6471
|
+
if (profile.agentKind === "pi") {
|
|
6472
|
+
return { label: "mode", value: "json" };
|
|
6473
|
+
}
|
|
6093
6474
|
return {
|
|
6094
6475
|
label: "sandbox",
|
|
6095
6476
|
value: `${profile.sandbox.defaultMode}/${profile.sandbox.maxMode}`
|
|
@@ -6457,6 +6838,8 @@ async function runStart(opts) {
|
|
|
6457
6838
|
fullCfg = applyAgentKindToConfig(fullCfg, "codex", binaryPath);
|
|
6458
6839
|
} else if (agentKind === "cursor") {
|
|
6459
6840
|
fullCfg = applyAgentKindToConfig(fullCfg, "cursor");
|
|
6841
|
+
} else if (agentKind === "pi") {
|
|
6842
|
+
fullCfg = applyAgentKindToConfig(fullCfg, "pi");
|
|
6460
6843
|
} else {
|
|
6461
6844
|
fullCfg = applyAgentKindToConfig(fullCfg, "claude");
|
|
6462
6845
|
}
|
|
@@ -6493,6 +6876,13 @@ async function runStart(opts) {
|
|
|
6493
6876
|
} catch {
|
|
6494
6877
|
console.log(`\u2713 Cursor Agent: agent${opts.debug ? " (debug)" : ""}`);
|
|
6495
6878
|
}
|
|
6879
|
+
} else if (agentKind === "pi") {
|
|
6880
|
+
try {
|
|
6881
|
+
const piPath = await resolvePiBinaryPath();
|
|
6882
|
+
console.log(`\u2713 Pi Agent: ${piPath}${opts.debug ? " (debug)" : ""}`);
|
|
6883
|
+
} catch {
|
|
6884
|
+
console.log(`\u2713 Pi Agent: pi${opts.debug ? " (debug)" : ""}`);
|
|
6885
|
+
}
|
|
6496
6886
|
} else {
|
|
6497
6887
|
console.log(`\u2713 Codex CLI: ${fullCfg.codex?.binaryPath ?? "codex"}`);
|
|
6498
6888
|
}
|
|
@@ -6543,11 +6933,11 @@ var runOptions = [
|
|
|
6543
6933
|
["--app-id <id>", "\u4F7F\u7528\u5DF2\u6709\u98DE\u4E66\u5E94\u7528\uFF08\u8DF3\u8FC7\u626B\u7801\u521B\u5EFA\uFF09"],
|
|
6544
6934
|
["--app-secret <secret>", "App Secret\uFF08\u914D\u5408 --app-id\uFF09"],
|
|
6545
6935
|
["--tenant <tenant>", "\u79DF\u6237\u57DF\u540D\uFF1Afeishu \u6216 lark\uFF08\u9ED8\u8BA4 feishu\uFF09"],
|
|
6546
|
-
["--agent <kind>", "agent \u7C7B\u578B\uFF1Aclaude / codex / cursor / disabled\uFF08\u9ED8\u8BA4 claude\uFF09"],
|
|
6936
|
+
["--agent <kind>", "agent \u7C7B\u578B\uFF1Aclaude / codex / cursor / pi / disabled\uFF08\u9ED8\u8BA4 claude\uFF09"],
|
|
6547
6937
|
["--debug", "Cursor agent \u8C03\u8BD5\uFF1A\u6253\u5370 spawn args \u4E0E\u539F\u751F stream-json \u4E8B\u4EF6"]
|
|
6548
6938
|
];
|
|
6549
6939
|
function parseRunOpts(opts) {
|
|
6550
|
-
const agent = opts.agent === "claude" || opts.agent === "codex" || opts.agent === "cursor" || opts.agent === "disabled" ? opts.agent : void 0;
|
|
6940
|
+
const agent = opts.agent === "claude" || opts.agent === "codex" || opts.agent === "cursor" || opts.agent === "pi" || opts.agent === "disabled" ? opts.agent : void 0;
|
|
6551
6941
|
return { ...opts, agent, debug: opts.debug === true };
|
|
6552
6942
|
}
|
|
6553
6943
|
program.command("run").description("\u524D\u53F0\u8FD0\u884C bot\uFF0C\u63A5\u6536\u6D88\u606F\u5E76\u901A\u8FC7 Claude Code / Codex / Cursor \u56DE\u590D").option(...runOptions[0]).option(...runOptions[1]).option(...runOptions[2]).option(...runOptions[3]).option(...runOptions[4]).option(...runOptions[5]).action(async (opts) => {
|