nolo-cli 0.1.41 → 0.1.43
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 +27 -12
- package/agent-runtime/agentThread.ts +222 -0
- package/agent-runtime/agentThreadAdmission.ts +74 -0
- package/agent-runtime/dialogWritePlan.ts +42 -0
- package/agent-runtime/hostAdapter.ts +1 -0
- package/agent-runtime/index.ts +43 -0
- package/agent-runtime/localLoop.ts +31 -2
- package/agent-runtime/localToolPolicy.ts +1 -0
- package/agent-runtime/localWorkspaceTools.ts +1258 -78
- package/agent-runtime/noloWorkspaceTools.ts +20 -1
- package/agent-runtime/runtimeToolPolicy.ts +1 -5
- package/agent-runtime/runtimeToolSurface.ts +1 -0
- package/agentAliases.ts +29 -9
- package/agentInternalCommandEntries.ts +11 -3
- package/agentMachineCommands.ts +1 -0
- package/agentRecordCommands.ts +2 -2
- package/agentRecordHelpers.ts +59 -6
- package/agentRunCommand.ts +84 -19
- package/ai/agent/buildSystemPrompt.ts +14 -0
- package/ai/agent/cliExecutor.ts +202 -43
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/liveAudioModel.ts +1 -1
- package/ai/agent/streamAgentChatTurn.ts +154 -20
- package/ai/agent/streamAgentChatTurnUtils.ts +63 -0
- package/ai/ai.locale.ts +70 -0
- package/ai/chat/inlineImageUrlsForCustomProvider.ts +7 -2
- package/ai/chat/sendOpenAICompletionsRequest.ts +25 -1
- package/ai/llm/kimi.ts +0 -1
- package/ai/llm/openrouterModels.ts +27 -2
- package/ai/memory/policy.ts +49 -0
- package/ai/memory/queryShared.ts +10 -4
- package/ai/memory/recentRelationshipRecap.ts +4 -0
- package/ai/memory/remember.ts +48 -104
- package/ai/memory/scope.ts +74 -0
- package/ai/memory/types.ts +6 -0
- package/ai/memory/understanding.ts +10 -18
- package/ai/policy/runtimePolicy.ts +95 -23
- package/ai/tools/agent/agentTools.ts +0 -21
- package/ai/tools/agent/startAgentDialogTool.ts +2 -1
- package/ai/tools/index.ts +2 -20
- package/ai/tools/prepareTools.ts +171 -1
- package/ai/tools/readXhsProfileTool.ts +80 -69
- package/ai/tools/table/createTableTool.ts +11 -0
- package/ai/tools/toolPacks.ts +19 -0
- package/ai/types.ts +3 -0
- package/app/favorite/deletedFavoriteProjection.ts +31 -0
- package/app/hooks/deleteDbKey.ts +24 -3
- package/app/i18n/translations/interface.locale.ts +48 -16
- package/app/types/appSummary.ts +4 -0
- package/app/utils/desktopAgentRuntimeTurnClient.ts +2 -0
- package/auth/adminPermissions.ts +64 -4
- package/auth/routes.ts +5 -0
- package/chat/chat.locale.ts +4 -0
- package/chat/dialog/actions/handleSendMessageAction.ts +7 -2
- package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
- package/chat/dialog/dialogSlice.ts +117 -4
- package/chat/messages/activityCompletion.ts +97 -0
- package/chat/messages/imagePayloadPersistence.ts +22 -0
- package/chat/messages/messageContent.ts +20 -16
- package/chat/messages/messageSlice.ts +20 -1
- package/chat/messages/toolThunks.ts +32 -11
- package/chat/messages/types.ts +8 -1
- package/chat/messages/web/toolDisplayName.ts +686 -0
- package/cli/agentAliases.ts +29 -9
- package/cli/agentInternalCommandEntries.ts +11 -3
- package/cli/agentMachineCommands.ts +1 -0
- package/cli/agentRecordCommands.ts +2 -2
- package/cli/agentRecordHelpers.ts +59 -6
- package/cli/agentRunCommand.ts +84 -19
- package/cli/client/agentRun.ts +65 -253
- package/cli/client/localRuntimeAdapter.ts +179 -53
- package/cli/commandRegistry.ts +7 -4
- package/cli/dialogAttachmentCleanup.ts +151 -0
- package/cli/dialogCommands.ts +163 -61
- package/cli/dialogInternalCommandEntries.ts +1 -1
- package/cli/docCreateCommands.ts +345 -0
- package/cli/docInternalCommandEntries.ts +11 -0
- package/cli/docPageHelpers.ts +80 -0
- package/cli/docSkillHelpers.ts +67 -0
- package/cli/docSpaceHelpers.ts +46 -0
- package/cli/internalCommandEntries.ts +4 -0
- package/cli/machineCommands.ts +18 -34
- package/cli/memoryCommands.ts +253 -0
- package/cli/memoryInternalCommandEntries.ts +9 -0
- package/cli/qoderUsageProbe.ts +252 -0
- package/cli/scriptCommandEntries.ts +0 -3
- package/cli/secretScan.ts +31 -0
- package/cli/tableCommands.ts +248 -13
- package/cli/workflowInternalCommandEntries.ts +2 -3
- package/client/agentRun.test.ts +68 -84
- package/client/agentRun.ts +65 -253
- package/client/localRuntimeAdapter.test.ts +505 -74
- package/client/localRuntimeAdapter.ts +179 -53
- package/commandRegistry.ts +7 -4
- package/connector-experimental/capabilities.ts +10 -9
- package/create/space/sidebarVisibleTypes.ts +19 -0
- package/dialogAttachmentCleanup.ts +151 -0
- package/dialogCommands.ts +163 -61
- package/dialogInternalCommandEntries.ts +1 -1
- package/docCreateCommands.ts +345 -0
- package/docInternalCommandEntries.ts +11 -0
- package/docPageHelpers.ts +80 -0
- package/docSkillHelpers.ts +67 -0
- package/docSpaceHelpers.ts +46 -0
- package/integrations/google/models.ts +4 -4
- package/integrations/openai/generateOpenAIRequestBody.ts +0 -3
- package/integrations/xhs-reader/types.ts +36 -3
- package/integrations/xhs-reader/url.ts +24 -1
- package/internalCommandEntries.ts +4 -0
- package/machineCommands.ts +18 -34
- package/memoryCommands.ts +253 -0
- package/memoryInternalCommandEntries.ts +9 -0
- package/package.json +141 -114
- package/qoderUsageProbe.ts +252 -0
- package/render/contentIcon/types.ts +167 -0
- package/render/page/docSlice.ts +30 -3
- package/render/page/types.ts +2 -0
- package/render/table/createTableAction.ts +4 -0
- package/render/table/tableSlice.ts +61 -0
- package/render/table/types.ts +10 -0
- package/scriptCommandEntries.ts +0 -3
- package/secretScan.ts +31 -0
- package/server/handlers/agentRun/types.ts +24 -23
- package/tableCommands.ts +248 -13
- package/workflowInternalCommandEntries.ts +2 -3
- package/ai/tools/agent/taskRunTool.ts +0 -112
- package/cli/client/taskRunPrompt.ts +0 -27
- package/cli/taskRunCommand.ts +0 -265
- package/client/taskRunPrompt.ts +0 -27
- package/integrations/xhs-reader/analyze/profileAnalyzer.ts +0 -205
- package/integrations/xhs-reader/backends/playwrightProfileCollector.ts +0 -624
- package/integrations/xhs-reader/bridge/readXhsProfileWithBridge.ts +0 -227
- package/integrations/xhs-reader/client/xhsWebApiClient.ts +0 -229
- package/integrations/xhs-reader/normalize.ts +0 -390
- package/integrations/xhs-reader/orchestrator.ts +0 -178
- package/taskRunCommand.ts +0 -265
package/ai/agent/cliExecutor.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CLI Executor - 通过命令行工具执行 AI 任务
|
|
3
3
|
*
|
|
4
|
-
* 设计为可扩展:当前支持 Copilot CLI、Gemini CLI、Codex CLI、Claude CLI 与
|
|
4
|
+
* 设计为可扩展:当前支持 Copilot CLI、Gemini CLI、Codex CLI、Claude CLI、Antigravity CLI 与 Qoder CLI。
|
|
5
5
|
*
|
|
6
6
|
* 注意:
|
|
7
7
|
* - CLI provider 与普通 model 路由共享 prompt / model / 最近文本上下文这些能力面
|
|
@@ -18,13 +18,14 @@ import { randomUUID } from "node:crypto";
|
|
|
18
18
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { isAbsolute, join, resolve } from "node:path";
|
|
20
20
|
import { tmpdir } from "node:os";
|
|
21
|
+
import { StringDecoder } from "node:string_decoder";
|
|
21
22
|
import { resolveLaunchableCodexCommand } from "../../connector-experimental/codexBinary";
|
|
22
23
|
import { buildCliPrompt } from "./cliPrompt";
|
|
23
24
|
|
|
24
25
|
// ── 类型定义 ──────────────────────────────────────────────────────────────────
|
|
25
26
|
|
|
26
27
|
/** 已支持的 CLI 工具。新增时在此联合类型追加,并在 EXECUTORS 里注册实现 */
|
|
27
|
-
export type CliProvider = "copilot" | "gemini" | "codex" | "claude" | "agy";
|
|
28
|
+
export type CliProvider = "copilot" | "gemini" | "codex" | "claude" | "agy" | "qoder";
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* CLI provider 图片输入。
|
|
@@ -71,6 +72,16 @@ type CliSessionMessage = {
|
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
const DEFAULT_AGY_TIMEOUT_MS = 600_000;
|
|
75
|
+
const BUFFERED_STREAMING_PROVIDERS = new Set<CliProvider>([
|
|
76
|
+
"codex",
|
|
77
|
+
"claude",
|
|
78
|
+
"agy",
|
|
79
|
+
"qoder",
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
export function isBufferedCliStreamingProvider(provider: CliProvider): boolean {
|
|
83
|
+
return BUFFERED_STREAMING_PROVIDERS.has(provider);
|
|
84
|
+
}
|
|
74
85
|
|
|
75
86
|
export interface CliSessionHandle {
|
|
76
87
|
sessionId: string;
|
|
@@ -159,12 +170,39 @@ function normalizeProxyEnv(env: Record<string, string | undefined>): Record<stri
|
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
function buildCliProcessEnv(extraEnv?: Record<string, string | undefined>) {
|
|
173
|
+
const baseEnv = {
|
|
174
|
+
...process.env,
|
|
175
|
+
...extraEnv,
|
|
176
|
+
NO_COLOR: "1",
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Enhance PATH on macOS so GUI-launched app can find brew/local tools
|
|
180
|
+
if (process.platform === "darwin") {
|
|
181
|
+
const defaultMacPaths = [
|
|
182
|
+
`${process.env.HOME || ""}/.local/bin`,
|
|
183
|
+
"/opt/homebrew/bin",
|
|
184
|
+
"/opt/homebrew/sbin",
|
|
185
|
+
"/usr/local/bin",
|
|
186
|
+
"/usr/local/sbin",
|
|
187
|
+
`${process.env.HOME || ""}/.bun/bin`,
|
|
188
|
+
`${process.env.HOME || ""}/Library/pnpm`,
|
|
189
|
+
`${process.env.HOME || ""}/.npm-global/bin`,
|
|
190
|
+
`${process.env.HOME || ""}/bin`,
|
|
191
|
+
];
|
|
192
|
+
const existingPath = baseEnv.PATH || "/usr/bin:/bin:/usr/sbin:/sbin";
|
|
193
|
+
const paths = existingPath.split(":");
|
|
194
|
+
for (const p of defaultMacPaths) {
|
|
195
|
+
if (p && !paths.includes(p)) {
|
|
196
|
+
paths.unshift(p); // Put brew/local paths at the front
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
baseEnv.PATH = paths.join(":");
|
|
200
|
+
}
|
|
201
|
+
|
|
162
202
|
return Object.fromEntries(
|
|
163
|
-
Object.entries(normalizeProxyEnv(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
NO_COLOR: "1",
|
|
167
|
-
})).filter((entry): entry is [string, string] => typeof entry[1] === "string")
|
|
203
|
+
Object.entries(normalizeProxyEnv(baseEnv)).filter(
|
|
204
|
+
(entry): entry is [string, string] => typeof entry[1] === "string"
|
|
205
|
+
)
|
|
168
206
|
);
|
|
169
207
|
}
|
|
170
208
|
|
|
@@ -178,6 +216,8 @@ function getCliProviderLabel(provider: CliProvider): string {
|
|
|
178
216
|
return "Copilot CLI";
|
|
179
217
|
case "agy":
|
|
180
218
|
return "Antigravity CLI";
|
|
219
|
+
case "qoder":
|
|
220
|
+
return "Qoder CLI";
|
|
181
221
|
case "gemini":
|
|
182
222
|
return "Gemini CLI";
|
|
183
223
|
}
|
|
@@ -194,7 +234,7 @@ function supportsModel(provider: CliProvider): boolean {
|
|
|
194
234
|
}
|
|
195
235
|
|
|
196
236
|
function supportsReasoningEffort(provider: CliProvider): boolean {
|
|
197
|
-
return provider === "claude" || provider === "copilot";
|
|
237
|
+
return provider === "claude" || provider === "copilot" || provider === "qoder";
|
|
198
238
|
}
|
|
199
239
|
|
|
200
240
|
function shouldPassClaudeModel(options: CliExecuteOptions): boolean {
|
|
@@ -626,8 +666,8 @@ function executeCodex(
|
|
|
626
666
|
env: buildCliProcessEnv(options.env),
|
|
627
667
|
stdio: ["ignore", "pipe", "pipe"],
|
|
628
668
|
});
|
|
629
|
-
|
|
630
|
-
|
|
669
|
+
const stdout = createUtf8Collector();
|
|
670
|
+
const stderr = createUtf8Collector();
|
|
631
671
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
632
672
|
|
|
633
673
|
if (timeout > 0) {
|
|
@@ -638,22 +678,24 @@ function executeCodex(
|
|
|
638
678
|
}
|
|
639
679
|
|
|
640
680
|
proc.stdout.on("data", (data: Buffer) => {
|
|
641
|
-
stdout
|
|
681
|
+
stdout.write(data);
|
|
642
682
|
});
|
|
643
683
|
|
|
644
684
|
proc.stderr.on("data", (data: Buffer) => {
|
|
645
|
-
stderr
|
|
685
|
+
stderr.write(data);
|
|
646
686
|
});
|
|
647
687
|
|
|
648
688
|
proc.on("close", (code) => {
|
|
649
689
|
if (timer) clearTimeout(timer);
|
|
690
|
+
const stdoutText = stdout.end();
|
|
691
|
+
const stderrText = stderr.end();
|
|
650
692
|
if (code !== 0 && code !== null) {
|
|
651
693
|
rmSync(tempDir, { recursive: true, force: true });
|
|
652
|
-
reject(new Error(`Codex CLI exited with code ${code}\nstderr: ${
|
|
694
|
+
reject(new Error(`Codex CLI exited with code ${code}\nstderr: ${stderrText}`));
|
|
653
695
|
return;
|
|
654
696
|
}
|
|
655
697
|
|
|
656
|
-
let text =
|
|
698
|
+
let text = stdoutText.trim();
|
|
657
699
|
try {
|
|
658
700
|
const lastMessage = readFileSync(outputFile, "utf8").trim();
|
|
659
701
|
if (lastMessage) text = lastMessage;
|
|
@@ -665,7 +707,7 @@ function executeCodex(
|
|
|
665
707
|
|
|
666
708
|
resolve({
|
|
667
709
|
text,
|
|
668
|
-
raw:
|
|
710
|
+
raw: stdoutText,
|
|
669
711
|
elapsed: Date.now() - start,
|
|
670
712
|
});
|
|
671
713
|
});
|
|
@@ -716,8 +758,8 @@ function executeClaude(
|
|
|
716
758
|
env: buildCliProcessEnv(options.env),
|
|
717
759
|
stdio: ["ignore", "pipe", "pipe"],
|
|
718
760
|
});
|
|
719
|
-
|
|
720
|
-
|
|
761
|
+
const stdout = createUtf8Collector();
|
|
762
|
+
const stderr = createUtf8Collector();
|
|
721
763
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
722
764
|
|
|
723
765
|
if (timeout > 0) {
|
|
@@ -728,23 +770,25 @@ function executeClaude(
|
|
|
728
770
|
}
|
|
729
771
|
|
|
730
772
|
proc.stdout.on("data", (data: Buffer) => {
|
|
731
|
-
stdout
|
|
773
|
+
stdout.write(data);
|
|
732
774
|
});
|
|
733
775
|
|
|
734
776
|
proc.stderr.on("data", (data: Buffer) => {
|
|
735
|
-
stderr
|
|
777
|
+
stderr.write(data);
|
|
736
778
|
});
|
|
737
779
|
|
|
738
780
|
proc.on("close", (code) => {
|
|
739
781
|
if (timer) clearTimeout(timer);
|
|
782
|
+
const stdoutText = stdout.end();
|
|
783
|
+
const stderrText = stderr.end();
|
|
740
784
|
if (code !== 0 && code !== null) {
|
|
741
|
-
reject(new Error(`Claude CLI exited with code ${code}\nstderr: ${
|
|
785
|
+
reject(new Error(`Claude CLI exited with code ${code}\nstderr: ${stderrText}`));
|
|
742
786
|
return;
|
|
743
787
|
}
|
|
744
788
|
|
|
745
789
|
resolve({
|
|
746
|
-
text:
|
|
747
|
-
raw:
|
|
790
|
+
text: stdoutText.trim(),
|
|
791
|
+
raw: stdoutText,
|
|
748
792
|
elapsed: Date.now() - start,
|
|
749
793
|
});
|
|
750
794
|
});
|
|
@@ -765,6 +809,35 @@ function tailText(value: string, maxChars = 1200): string {
|
|
|
765
809
|
return value.slice(value.length - maxChars);
|
|
766
810
|
}
|
|
767
811
|
|
|
812
|
+
function createUtf8Collector() {
|
|
813
|
+
const decoder = new StringDecoder("utf8");
|
|
814
|
+
let text = "";
|
|
815
|
+
let ended = false;
|
|
816
|
+
const finish = () => {
|
|
817
|
+
if (ended) return "";
|
|
818
|
+
ended = true;
|
|
819
|
+
const chunk = decoder.end();
|
|
820
|
+
if (chunk) text += chunk;
|
|
821
|
+
return chunk;
|
|
822
|
+
};
|
|
823
|
+
return {
|
|
824
|
+
write(data: Buffer): string {
|
|
825
|
+
if (ended) return "";
|
|
826
|
+
const chunk = decoder.write(data);
|
|
827
|
+
text += chunk;
|
|
828
|
+
return chunk;
|
|
829
|
+
},
|
|
830
|
+
finish,
|
|
831
|
+
end(): string {
|
|
832
|
+
finish();
|
|
833
|
+
return text;
|
|
834
|
+
},
|
|
835
|
+
get text() {
|
|
836
|
+
return text;
|
|
837
|
+
},
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
|
|
768
841
|
function terminateCliProcessGroup(
|
|
769
842
|
proc: ReturnType<typeof spawn>,
|
|
770
843
|
signal: NodeJS.Signals
|
|
@@ -827,8 +900,8 @@ function executeAgy(
|
|
|
827
900
|
stdio: ["ignore", "pipe", "pipe"],
|
|
828
901
|
detached: process.platform !== "win32",
|
|
829
902
|
});
|
|
830
|
-
|
|
831
|
-
|
|
903
|
+
const stdout = createUtf8Collector();
|
|
904
|
+
const stderr = createUtf8Collector();
|
|
832
905
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
833
906
|
let forceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
834
907
|
let settled = false;
|
|
@@ -846,16 +919,16 @@ function executeAgy(
|
|
|
846
919
|
forceTimer = setTimeout(() => {
|
|
847
920
|
terminateCliProcessGroup(proc, "SIGKILL");
|
|
848
921
|
}, 3_000);
|
|
849
|
-
reject(buildAgyTimeoutError(timeout, stdout, stderr));
|
|
922
|
+
reject(buildAgyTimeoutError(timeout, stdout.end(), stderr.end()));
|
|
850
923
|
}, timeout + 1_000);
|
|
851
924
|
}
|
|
852
925
|
|
|
853
926
|
proc.stdout.on("data", (data: Buffer) => {
|
|
854
|
-
stdout
|
|
927
|
+
stdout.write(data);
|
|
855
928
|
});
|
|
856
929
|
|
|
857
930
|
proc.stderr.on("data", (data: Buffer) => {
|
|
858
|
-
stderr
|
|
931
|
+
stderr.write(data);
|
|
859
932
|
});
|
|
860
933
|
|
|
861
934
|
proc.on("close", (code) => {
|
|
@@ -865,14 +938,17 @@ function executeAgy(
|
|
|
865
938
|
}
|
|
866
939
|
settled = true;
|
|
867
940
|
clearTimers();
|
|
941
|
+
const stdoutText = stdout.end();
|
|
942
|
+
const stderrText = stderr.end();
|
|
868
943
|
if (code !== 0 && code !== null) {
|
|
869
|
-
|
|
944
|
+
console.error("[cliExecutor] agy process exited with non-zero code:", code, "stderr:", stderrText);
|
|
945
|
+
reject(new Error(`Antigravity CLI exited with code ${code}\nstderr: ${stderrText}`));
|
|
870
946
|
return;
|
|
871
947
|
}
|
|
872
948
|
|
|
873
949
|
resolve({
|
|
874
|
-
text:
|
|
875
|
-
raw:
|
|
950
|
+
text: stdoutText.trim(),
|
|
951
|
+
raw: stdoutText,
|
|
876
952
|
elapsed: Date.now() - start,
|
|
877
953
|
});
|
|
878
954
|
});
|
|
@@ -881,6 +957,83 @@ function executeAgy(
|
|
|
881
957
|
if (settled) return;
|
|
882
958
|
settled = true;
|
|
883
959
|
clearTimers();
|
|
960
|
+
console.error("[cliExecutor] agy process error event:", err);
|
|
961
|
+
reject(err);
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
function executeQoder(
|
|
967
|
+
prompt: string,
|
|
968
|
+
options: CliExecuteOptions
|
|
969
|
+
): Promise<CliExecuteResult> {
|
|
970
|
+
const {
|
|
971
|
+
model,
|
|
972
|
+
reasoningEffort,
|
|
973
|
+
timeout = 120_000,
|
|
974
|
+
cwd = process.cwd(),
|
|
975
|
+
yolo = true,
|
|
976
|
+
} = options;
|
|
977
|
+
|
|
978
|
+
return new Promise((resolve, reject) => {
|
|
979
|
+
const args = ["-p", prompt, "--cwd", cwd];
|
|
980
|
+
if (model) {
|
|
981
|
+
args.push("--model");
|
|
982
|
+
args.push(model);
|
|
983
|
+
}
|
|
984
|
+
if (reasoningEffort) {
|
|
985
|
+
args.push("--reasoning-effort");
|
|
986
|
+
args.push(reasoningEffort);
|
|
987
|
+
}
|
|
988
|
+
if (yolo) {
|
|
989
|
+
args.push("--dangerously-skip-permissions");
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
const start = Date.now();
|
|
993
|
+
const proc = spawn("qoder", args, {
|
|
994
|
+
cwd,
|
|
995
|
+
env: buildCliProcessEnv(options.env),
|
|
996
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
997
|
+
});
|
|
998
|
+
const stdout = createUtf8Collector();
|
|
999
|
+
const stderr = createUtf8Collector();
|
|
1000
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
1001
|
+
|
|
1002
|
+
if (timeout > 0) {
|
|
1003
|
+
timer = setTimeout(() => {
|
|
1004
|
+
proc.kill("SIGTERM");
|
|
1005
|
+
reject(new Error(`Qoder CLI timed out after ${timeout}ms`));
|
|
1006
|
+
}, timeout);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
proc.stdout.on("data", (data: Buffer) => {
|
|
1010
|
+
stdout.write(data);
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
proc.stderr.on("data", (data: Buffer) => {
|
|
1014
|
+
stderr.write(data);
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
proc.on("close", (code) => {
|
|
1018
|
+
if (timer) clearTimeout(timer);
|
|
1019
|
+
const stdoutText = stdout.end();
|
|
1020
|
+
const stderrText = stderr.end();
|
|
1021
|
+
if (code !== 0 && code !== null) {
|
|
1022
|
+
console.error("[cliExecutor] qoder process exited with non-zero code:", code, "stderr:", stderrText);
|
|
1023
|
+
reject(new Error(`Qoder CLI exited with code ${code}\nstderr: ${stderrText}`));
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
resolve({
|
|
1028
|
+
text: stdoutText.trim(),
|
|
1029
|
+
raw: stdoutText,
|
|
1030
|
+
elapsed: Date.now() - start,
|
|
1031
|
+
});
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
proc.on("error", (err) => {
|
|
1035
|
+
if (timer) clearTimeout(timer);
|
|
1036
|
+
console.error("[cliExecutor] qoder process error event:", err);
|
|
884
1037
|
reject(err);
|
|
885
1038
|
});
|
|
886
1039
|
});
|
|
@@ -897,6 +1050,7 @@ const EXECUTORS: Record<
|
|
|
897
1050
|
codex: executeCodex,
|
|
898
1051
|
claude: executeClaude,
|
|
899
1052
|
agy: executeAgy,
|
|
1053
|
+
qoder: executeQoder,
|
|
900
1054
|
};
|
|
901
1055
|
|
|
902
1056
|
function formatCliSessionTask(messages: CliSessionMessage[]): string {
|
|
@@ -933,7 +1087,7 @@ function getCliSessionOrThrow(sessionId: string): CliSessionState {
|
|
|
933
1087
|
/**
|
|
934
1088
|
* 执行 CLI 任务
|
|
935
1089
|
*
|
|
936
|
-
* @param provider CLI 工具类型(如 "copilot" | "gemini" | "codex" | "claude" | "agy")
|
|
1090
|
+
* @param provider CLI 工具类型(如 "copilot" | "gemini" | "codex" | "claude" | "agy" | "qoder")
|
|
937
1091
|
* @param prompt 完整 prompt(system prompt 已由调用方拼好)
|
|
938
1092
|
* @param options 执行选项
|
|
939
1093
|
*/
|
|
@@ -1119,7 +1273,7 @@ export function executeCliStreaming(
|
|
|
1119
1273
|
})
|
|
1120
1274
|
);
|
|
1121
1275
|
}
|
|
1122
|
-
if (provider === "agy") {
|
|
1276
|
+
if (provider === "agy" || provider === "qoder") {
|
|
1123
1277
|
return withCleanup(
|
|
1124
1278
|
executor(resolved.prompt, resolved.options).then((result) => {
|
|
1125
1279
|
const merged = { ...result, warnings: [...resolved.warnings, ...(result.warnings ?? [])] };
|
|
@@ -1158,10 +1312,10 @@ export function executeCliStreaming(
|
|
|
1158
1312
|
const start = Date.now();
|
|
1159
1313
|
const proc = spawn("gh", args, {
|
|
1160
1314
|
cwd,
|
|
1161
|
-
env:
|
|
1315
|
+
env: buildCliProcessEnv(resolved.options.env),
|
|
1162
1316
|
});
|
|
1163
1317
|
|
|
1164
|
-
|
|
1318
|
+
const stdout = createUtf8Collector();
|
|
1165
1319
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
1166
1320
|
|
|
1167
1321
|
if (timeout > 0) {
|
|
@@ -1172,8 +1326,7 @@ export function executeCliStreaming(
|
|
|
1172
1326
|
}
|
|
1173
1327
|
|
|
1174
1328
|
proc.stdout.on("data", (data: Buffer) => {
|
|
1175
|
-
const chunk =
|
|
1176
|
-
raw += chunk;
|
|
1329
|
+
const chunk = stdout.write(data);
|
|
1177
1330
|
// 过滤噪音行后再推送
|
|
1178
1331
|
const cleaned = chunk
|
|
1179
1332
|
.split("\n")
|
|
@@ -1184,6 +1337,7 @@ export function executeCliStreaming(
|
|
|
1184
1337
|
|
|
1185
1338
|
proc.on("close", (code) => {
|
|
1186
1339
|
if (timer) clearTimeout(timer);
|
|
1340
|
+
const raw = stdout.end();
|
|
1187
1341
|
if (code !== 0 && code !== null) {
|
|
1188
1342
|
reject(new Error(`Copilot CLI exited with code ${code}`));
|
|
1189
1343
|
return;
|
|
@@ -1228,11 +1382,14 @@ function executeGeminiStreaming(
|
|
|
1228
1382
|
const start = Date.now();
|
|
1229
1383
|
const proc = spawn("gemini", args, {
|
|
1230
1384
|
cwd,
|
|
1231
|
-
env: {
|
|
1385
|
+
env: buildCliProcessEnv({
|
|
1386
|
+
...options.env,
|
|
1387
|
+
NODE_OPTIONS: "--no-deprecation",
|
|
1388
|
+
}),
|
|
1232
1389
|
});
|
|
1233
1390
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1391
|
+
const stdout = createUtf8Collector();
|
|
1392
|
+
const stderr = createUtf8Collector();
|
|
1236
1393
|
let lineBuffer = "";
|
|
1237
1394
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
1238
1395
|
|
|
@@ -1257,8 +1414,7 @@ function executeGeminiStreaming(
|
|
|
1257
1414
|
}
|
|
1258
1415
|
|
|
1259
1416
|
proc.stdout.on("data", (data: Buffer) => {
|
|
1260
|
-
const chunk =
|
|
1261
|
-
raw += chunk;
|
|
1417
|
+
const chunk = stdout.write(data);
|
|
1262
1418
|
lineBuffer += chunk;
|
|
1263
1419
|
|
|
1264
1420
|
let newlineIndex = lineBuffer.indexOf("\n");
|
|
@@ -1279,15 +1435,18 @@ function executeGeminiStreaming(
|
|
|
1279
1435
|
});
|
|
1280
1436
|
|
|
1281
1437
|
proc.stderr.on("data", (data: Buffer) => {
|
|
1282
|
-
stderr
|
|
1438
|
+
stderr.write(data);
|
|
1283
1439
|
});
|
|
1284
1440
|
|
|
1285
1441
|
proc.on("close", (code) => {
|
|
1286
1442
|
if (timer) clearTimeout(timer);
|
|
1443
|
+
lineBuffer += stdout.finish();
|
|
1444
|
+
const raw = stdout.text;
|
|
1445
|
+
const stderrText = stderr.end();
|
|
1287
1446
|
flushLineBuffer();
|
|
1288
1447
|
|
|
1289
1448
|
if (code !== 0 && code !== null) {
|
|
1290
|
-
reject(new Error(`Gemini CLI exited with code ${code}\nstderr: ${
|
|
1449
|
+
reject(new Error(`Gemini CLI exited with code ${code}\nstderr: ${stderrText}`));
|
|
1291
1450
|
return;
|
|
1292
1451
|
}
|
|
1293
1452
|
|
|
@@ -89,10 +89,10 @@ export const getCreateAgentSchema = (t: TFunction) =>
|
|
|
89
89
|
}).optional(),
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* CLI provider(apiSource=cli 时有效):支持 "copilot" | "gemini" | "codex" | "claude" | "agy"
|
|
92
|
+
* CLI provider(apiSource=cli 时有效):支持 "copilot" | "gemini" | "codex" | "claude" | "agy" | "qoder"
|
|
93
93
|
*/
|
|
94
94
|
cliProvider: z
|
|
95
|
-
.enum(["copilot", "gemini", "codex", "claude", "agy"])
|
|
95
|
+
.enum(["copilot", "gemini", "codex", "claude", "agy", "qoder"])
|
|
96
96
|
.optional()
|
|
97
97
|
.or(z.literal("")),
|
|
98
98
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const DEFAULT_GOOGLE_LIVE_AUDIO_MODEL =
|
|
2
|
-
"gemini-
|
|
2
|
+
"gemini-3.1-flash-live-preview";
|