@robota-sdk/agent-cli 3.0.0-beta.59 → 3.0.0-beta.60
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 +29 -22
- package/dist/node/bin.js +3 -4
- package/dist/node/{chunk-4ZX5RLIX.js → chunk-6XQKLNRF.js} +20 -29
- package/dist/node/{chunk-B522YHTK.js → chunk-GHQHUBHC.js} +703 -919
- package/dist/node/index.cjs +747 -997
- package/dist/node/index.d.cts +5 -0
- package/dist/node/index.d.ts +5 -0
- package/dist/node/index.js +2 -2
- package/dist/node/subagents/child-process-subagent-worker.js +1 -1
- package/package.json +27 -11
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
createProviderFromSettings,
|
|
4
4
|
findProviderDefinition,
|
|
5
5
|
formatSupportedProviderTypes,
|
|
6
|
+
getProviderSettingsPaths,
|
|
6
7
|
hasUsableSecretReference,
|
|
7
8
|
isSubagentWorkerChildMessage,
|
|
8
9
|
readMergedProviderSettings,
|
|
10
|
+
readMergedProviderSettingsFromPaths,
|
|
9
11
|
readProviderSettings
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6XQKLNRF.js";
|
|
11
13
|
|
|
12
14
|
// src/background/managed-shell-process-runner.ts
|
|
13
15
|
import { spawn } from "child_process";
|
|
@@ -191,28 +193,55 @@ var WORKTREE_DIR = ".robota/worktrees";
|
|
|
191
193
|
var BRANCH_PREFIX = "robota";
|
|
192
194
|
var GIT_ENCODING = "utf8";
|
|
193
195
|
var SHORT_ID_LENGTH = 8;
|
|
196
|
+
var DEFAULT_MAX_CREATE_ATTEMPTS = 5;
|
|
197
|
+
var COLLISION_ERROR_PATTERN = /already exists|is already checked out|missing but already registered/i;
|
|
194
198
|
function createGitWorktreeIsolationAdapter(options) {
|
|
195
199
|
return new GitWorktreeIsolationAdapter(options);
|
|
196
200
|
}
|
|
197
201
|
var GitWorktreeIsolationAdapter = class {
|
|
198
202
|
worktreeDir;
|
|
199
203
|
branchPrefix;
|
|
204
|
+
idFactory;
|
|
205
|
+
maxCreateAttempts;
|
|
200
206
|
constructor(options = {}) {
|
|
201
207
|
this.worktreeDir = options.worktreeDir ?? WORKTREE_DIR;
|
|
202
208
|
this.branchPrefix = options.branchPrefix ?? BRANCH_PREFIX;
|
|
209
|
+
this.idFactory = options.idFactory ?? createShortId;
|
|
210
|
+
this.maxCreateAttempts = options.maxCreateAttempts ?? DEFAULT_MAX_CREATE_ATTEMPTS;
|
|
203
211
|
}
|
|
204
212
|
prepare(request) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
213
|
+
if (this.maxCreateAttempts < 1) {
|
|
214
|
+
throw new BackgroundTaskError2("runner", "Git worktree creation attempts must be at least 1");
|
|
215
|
+
}
|
|
216
|
+
const repoRoot = resolveRepoRoot(request.cwd);
|
|
217
|
+
const baseRevision = runGit(repoRoot, ["rev-parse", "HEAD"]).trim();
|
|
218
|
+
const parentStatus = runGit(repoRoot, ["status", "--porcelain"]).trimEnd();
|
|
208
219
|
const worktreeRoot = join(repoRoot, this.worktreeDir);
|
|
209
|
-
const worktreePath = join(worktreeRoot, `${request.jobId}-${shortId}`);
|
|
210
220
|
mkdirSync(worktreeRoot, { recursive: true });
|
|
211
|
-
|
|
212
|
-
|
|
221
|
+
let lastError;
|
|
222
|
+
for (let attempt = 0; attempt < this.maxCreateAttempts; attempt += 1) {
|
|
223
|
+
const shortId = normalizeShortId(this.idFactory());
|
|
224
|
+
const jobId = sanitizePathSegment(request.jobId);
|
|
225
|
+
const branchName = `${this.branchPrefix}/${jobId}-${shortId}`;
|
|
226
|
+
const worktreePath = join(worktreeRoot, `${jobId}-${shortId}`);
|
|
227
|
+
try {
|
|
228
|
+
runGit(repoRoot, ["worktree", "add", "-b", branchName, worktreePath, "HEAD"]);
|
|
229
|
+
return { repoRoot, worktreePath, branchName, baseRevision, parentStatus };
|
|
230
|
+
} catch (error) {
|
|
231
|
+
lastError = toError(error);
|
|
232
|
+
if (!isCollisionError(lastError)) throw lastError;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
throw new BackgroundTaskError2(
|
|
236
|
+
"runner",
|
|
237
|
+
`Unable to create Git worktree after ${this.maxCreateAttempts} attempts due to branch or path collisions. Last error: ${lastError?.message ?? "unknown error"}`
|
|
238
|
+
);
|
|
213
239
|
}
|
|
214
240
|
isClean(worktree) {
|
|
215
|
-
return
|
|
241
|
+
return this.getStatus(worktree).trim().length === 0;
|
|
242
|
+
}
|
|
243
|
+
getStatus(worktree) {
|
|
244
|
+
return runGit(worktree.worktreePath, ["status", "--porcelain"]);
|
|
216
245
|
}
|
|
217
246
|
remove(worktree) {
|
|
218
247
|
runGit(worktree.repoRoot, ["worktree", "remove", "--force", worktree.worktreePath]);
|
|
@@ -224,13 +253,51 @@ function runGit(cwd, args) {
|
|
|
224
253
|
return execFileSync("git", args, {
|
|
225
254
|
cwd,
|
|
226
255
|
encoding: GIT_ENCODING,
|
|
227
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
256
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
257
|
+
env: createGitEnvironment()
|
|
228
258
|
});
|
|
229
259
|
} catch (error) {
|
|
230
260
|
const message = error instanceof Error ? error.message : String(error);
|
|
231
261
|
throw new BackgroundTaskError2("runner", `git ${args.join(" ")} failed: ${message}`);
|
|
232
262
|
}
|
|
233
263
|
}
|
|
264
|
+
function createGitEnvironment() {
|
|
265
|
+
const env = { ...process.env };
|
|
266
|
+
for (const key of Object.keys(env)) {
|
|
267
|
+
if (key.startsWith("GIT_")) {
|
|
268
|
+
delete env[key];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return env;
|
|
272
|
+
}
|
|
273
|
+
function resolveRepoRoot(cwd) {
|
|
274
|
+
try {
|
|
275
|
+
return runGit(cwd, ["rev-parse", "--show-toplevel"]).trim();
|
|
276
|
+
} catch (error) {
|
|
277
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
278
|
+
throw new BackgroundTaskError2(
|
|
279
|
+
"runner",
|
|
280
|
+
`Worktree isolation requires a Git repository. Run from a Git worktree or request isolation "none". Details: ${message}`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function createShortId() {
|
|
285
|
+
return randomUUID().slice(0, SHORT_ID_LENGTH);
|
|
286
|
+
}
|
|
287
|
+
function normalizeShortId(value) {
|
|
288
|
+
const sanitized = sanitizePathSegment(value).slice(0, SHORT_ID_LENGTH);
|
|
289
|
+
return sanitized.length > 0 ? sanitized : createShortId();
|
|
290
|
+
}
|
|
291
|
+
function sanitizePathSegment(value) {
|
|
292
|
+
const sanitized = value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
293
|
+
return sanitized.length > 0 ? sanitized : "agent";
|
|
294
|
+
}
|
|
295
|
+
function toError(error) {
|
|
296
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
297
|
+
}
|
|
298
|
+
function isCollisionError(error) {
|
|
299
|
+
return COLLISION_ERROR_PATTERN.test(error.message);
|
|
300
|
+
}
|
|
234
301
|
|
|
235
302
|
// src/subagents/child-process-subagent-runner.ts
|
|
236
303
|
import { fork } from "child_process";
|
|
@@ -594,6 +661,23 @@ function readTranscriptLog(jobId, transcriptPath, cursor) {
|
|
|
594
661
|
import { readFileSync as readFileSync6 } from "fs";
|
|
595
662
|
import { join as join9, dirname as dirname5 } from "path";
|
|
596
663
|
import { fileURLToPath } from "url";
|
|
664
|
+
import { createAgentCommandModule } from "@robota-sdk/agent-command-agent";
|
|
665
|
+
import { createBackgroundCommandModule } from "@robota-sdk/agent-command-background";
|
|
666
|
+
import { createProviderCommandModule } from "@robota-sdk/agent-command-provider";
|
|
667
|
+
import { createCompactCommandModule } from "@robota-sdk/agent-command-compact";
|
|
668
|
+
import { createContextCommandModule } from "@robota-sdk/agent-command-context";
|
|
669
|
+
import { createExitCommandModule } from "@robota-sdk/agent-command-exit";
|
|
670
|
+
import { createHelpCommandModule } from "@robota-sdk/agent-command-help";
|
|
671
|
+
import { createLanguageCommandModule } from "@robota-sdk/agent-command-language";
|
|
672
|
+
import { createMemoryCommandModule } from "@robota-sdk/agent-command-memory";
|
|
673
|
+
import { createModeCommandModule } from "@robota-sdk/agent-command-mode";
|
|
674
|
+
import { createModelCommandModule } from "@robota-sdk/agent-command-model";
|
|
675
|
+
import { createPermissionsCommandModule } from "@robota-sdk/agent-command-permissions";
|
|
676
|
+
import { createPluginCommandModule } from "@robota-sdk/agent-command-plugin";
|
|
677
|
+
import { createResetCommandModule } from "@robota-sdk/agent-command-reset";
|
|
678
|
+
import { createRewindCommandModule } from "@robota-sdk/agent-command-rewind";
|
|
679
|
+
import { createStatusLineCommandModule } from "@robota-sdk/agent-command-statusline";
|
|
680
|
+
import { createSessionCommandModule } from "@robota-sdk/agent-command-session";
|
|
597
681
|
import { InteractiveSession as InteractiveSession2, projectPaths } from "@robota-sdk/agent-sdk";
|
|
598
682
|
import { SessionStore } from "@robota-sdk/agent-sessions";
|
|
599
683
|
|
|
@@ -710,28 +794,6 @@ function writeSettings(path, settings) {
|
|
|
710
794
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
711
795
|
writeFileSync(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
712
796
|
}
|
|
713
|
-
function updateModelInSettings(settingsPath, modelId) {
|
|
714
|
-
const settings = readSettings(settingsPath);
|
|
715
|
-
const currentProvider = settings.currentProvider;
|
|
716
|
-
const providers = settings.providers;
|
|
717
|
-
if (typeof currentProvider === "string" && isSettingsData(providers)) {
|
|
718
|
-
const providerMap = providers;
|
|
719
|
-
providerMap[currentProvider] = {
|
|
720
|
-
...isSettingsData(providerMap[currentProvider]) ? providerMap[currentProvider] : {},
|
|
721
|
-
model: modelId
|
|
722
|
-
};
|
|
723
|
-
settings.providers = providerMap;
|
|
724
|
-
} else {
|
|
725
|
-
settings.provider = {
|
|
726
|
-
...isSettingsData(settings.provider) ? settings.provider : {},
|
|
727
|
-
model: modelId
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
writeSettings(settingsPath, settings);
|
|
731
|
-
}
|
|
732
|
-
function isSettingsData(value) {
|
|
733
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
734
|
-
}
|
|
735
797
|
function deleteSettings(path) {
|
|
736
798
|
if (existsSync2(path)) {
|
|
737
799
|
unlinkSync(path);
|
|
@@ -742,31 +804,21 @@ function deleteSettings(path) {
|
|
|
742
804
|
|
|
743
805
|
// src/utils/provider-setup.ts
|
|
744
806
|
import { join as join4 } from "path";
|
|
745
|
-
import { homedir } from "os";
|
|
746
807
|
|
|
747
808
|
// src/utils/settings-check.ts
|
|
748
809
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
749
|
-
function
|
|
750
|
-
|
|
751
|
-
try {
|
|
752
|
-
const raw = readFileSync3(filePath, "utf8").trim();
|
|
753
|
-
if (raw.length === 0) return "incomplete";
|
|
754
|
-
const parsed = JSON.parse(raw);
|
|
755
|
-
if (!hasUsableProviderConfig(parsed, providerDefinitions)) return "incomplete";
|
|
756
|
-
return "valid";
|
|
757
|
-
} catch {
|
|
758
|
-
return "corrupt";
|
|
759
|
-
}
|
|
810
|
+
function checkSettingsDocument(settings, providerDefinitions = []) {
|
|
811
|
+
return hasUsableProviderConfig(settings, providerDefinitions) ? "valid" : "incomplete";
|
|
760
812
|
}
|
|
761
813
|
function hasUsableProviderConfig(settings, providerDefinitions) {
|
|
814
|
+
if (typeof settings.currentProvider === "string") {
|
|
815
|
+
const profile = settings.providers?.[settings.currentProvider];
|
|
816
|
+
return isUsableProviderProfile(profile?.type, profile, providerDefinitions);
|
|
817
|
+
}
|
|
762
818
|
if (settings.provider && isUsableProviderProfile(settings.provider.name, settings.provider, providerDefinitions)) {
|
|
763
819
|
return true;
|
|
764
820
|
}
|
|
765
|
-
|
|
766
|
-
return false;
|
|
767
|
-
}
|
|
768
|
-
const profile = settings.providers?.[settings.currentProvider];
|
|
769
|
-
return isUsableProviderProfile(profile?.type, profile, providerDefinitions);
|
|
821
|
+
return false;
|
|
770
822
|
}
|
|
771
823
|
function isUsableProviderProfile(type, profile, providerDefinitions) {
|
|
772
824
|
if (!profile) {
|
|
@@ -786,69 +838,14 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
|
|
|
786
838
|
}
|
|
787
839
|
|
|
788
840
|
// src/utils/provider-settings.ts
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
function setCurrentProvider(settings, profileName) {
|
|
799
|
-
if (!settings.providers?.[profileName]) {
|
|
800
|
-
throw new Error(`Provider profile "${profileName}" was not found`);
|
|
801
|
-
}
|
|
802
|
-
return {
|
|
803
|
-
...settings,
|
|
804
|
-
currentProvider: profileName
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
function validateProviderProfile(profileName, profile, options = {}) {
|
|
808
|
-
if (!profile.type) {
|
|
809
|
-
throw new Error(`Provider profile "${profileName}" is missing type`);
|
|
810
|
-
}
|
|
811
|
-
if (!profile.model) {
|
|
812
|
-
throw new Error(`Provider profile "${profileName}" is missing model`);
|
|
813
|
-
}
|
|
814
|
-
const definition = findProviderDefinition(options.providerDefinitions ?? [], profile.type);
|
|
815
|
-
if (definition?.requiresApiKey === true && !hasUsableSecretReference(profile.apiKey ?? definition.defaults?.apiKey)) {
|
|
816
|
-
throw new Error(`Provider profile "${profileName}" is missing apiKey`);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
function buildProviderSetupPatch(input, options = {}) {
|
|
820
|
-
const profile = buildProviderProfile(input, options);
|
|
821
|
-
validateProviderProfile(input.profile, profile, options);
|
|
822
|
-
return {
|
|
823
|
-
...input.setCurrent && { currentProvider: input.profile },
|
|
824
|
-
providers: {
|
|
825
|
-
[input.profile]: profile
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
function buildProviderProfile(input, options = {}) {
|
|
830
|
-
const defaults = getProviderDefaults(input.type, options.providerDefinitions ?? []);
|
|
831
|
-
const apiKey = input.apiKeyEnv !== void 0 ? `$ENV:${input.apiKeyEnv}` : input.apiKey ?? defaults.apiKey;
|
|
832
|
-
const baseURL = input.baseURL ?? defaults.baseURL;
|
|
833
|
-
return {
|
|
834
|
-
type: input.type,
|
|
835
|
-
model: input.model ?? defaults.model,
|
|
836
|
-
...apiKey !== void 0 && { apiKey },
|
|
837
|
-
...baseURL !== void 0 && { baseURL },
|
|
838
|
-
...input.timeout !== void 0 && { timeout: input.timeout }
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
function getProviderDefaults(type, providerDefinitions) {
|
|
842
|
-
return findProviderDefinition(providerDefinitions, type)?.defaults ?? {};
|
|
843
|
-
}
|
|
844
|
-
function mergeProviderPatch(settings, patch) {
|
|
845
|
-
const [profileName, profile] = Object.entries(patch.providers)[0] ?? [];
|
|
846
|
-
if (!profileName || !profile) {
|
|
847
|
-
return settings;
|
|
848
|
-
}
|
|
849
|
-
const withProfile = upsertProviderProfile(settings, profileName, profile);
|
|
850
|
-
return patch.currentProvider ? setCurrentProvider(withProfile, patch.currentProvider) : withProfile;
|
|
851
|
-
}
|
|
841
|
+
import {
|
|
842
|
+
buildProviderProfile,
|
|
843
|
+
buildProviderSetupPatch,
|
|
844
|
+
mergeProviderPatch,
|
|
845
|
+
setCurrentProvider,
|
|
846
|
+
upsertProviderProfile,
|
|
847
|
+
validateProviderProfile
|
|
848
|
+
} from "@robota-sdk/agent-sdk";
|
|
852
849
|
|
|
853
850
|
// src/utils/provider-configuration.ts
|
|
854
851
|
function readProviderDocument(settingsPath) {
|
|
@@ -869,158 +866,84 @@ function applyProviderSwitch(settingsPath, profileName, options = {}) {
|
|
|
869
866
|
writeSettings(settingsPath, next);
|
|
870
867
|
return next;
|
|
871
868
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
stepIndex: 0,
|
|
879
|
-
values: {}
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
function formatProviderSetupSelectionPrompt(providerDefinitions) {
|
|
883
|
-
if (providerDefinitions.length === 0) {
|
|
884
|
-
return " No providers are available.";
|
|
885
|
-
}
|
|
886
|
-
const lines = [
|
|
887
|
-
" Select provider:",
|
|
888
|
-
...providerDefinitions.map(
|
|
889
|
-
(definition, index) => ` ${index + 1}. ${formatProviderSetupChoiceLabel(definition)}`
|
|
890
|
-
),
|
|
891
|
-
` Provider [1-${providerDefinitions.length}] (default: 1): `
|
|
892
|
-
];
|
|
893
|
-
return lines.join("\n");
|
|
894
|
-
}
|
|
895
|
-
function resolveProviderSetupSelection(rawValue, providerDefinitions) {
|
|
896
|
-
const value = rawValue.trim();
|
|
897
|
-
const selectedValue = value.length > 0 ? value : "1";
|
|
898
|
-
const index = parseProviderSelectionIndex(selectedValue);
|
|
899
|
-
if (index !== void 0) {
|
|
900
|
-
const definition2 = providerDefinitions[index];
|
|
901
|
-
if (definition2 !== void 0) {
|
|
902
|
-
return definition2.type;
|
|
903
|
-
}
|
|
904
|
-
throw new Error(
|
|
905
|
-
`Provider selection ${selectedValue} is out of range. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
|
-
const definition = findProviderDefinition(providerDefinitions, selectedValue);
|
|
909
|
-
if (definition === void 0) {
|
|
910
|
-
throw new Error(
|
|
911
|
-
`Unknown provider: ${selectedValue}. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
912
|
-
);
|
|
869
|
+
function applyActiveModelChange(cwd, modelId, options = {}) {
|
|
870
|
+
const settingsPaths = options.settingsPaths ?? getProviderSettingsPaths(cwd);
|
|
871
|
+
const merged = readMergedProviderSettingsFromPaths(settingsPaths);
|
|
872
|
+
const activeProfileName = options.providerOverride ?? merged.currentProvider;
|
|
873
|
+
if (typeof activeProfileName === "string") {
|
|
874
|
+
return updateActiveProviderProfileModel(settingsPaths, activeProfileName, modelId);
|
|
913
875
|
}
|
|
914
|
-
return
|
|
876
|
+
return updateLegacyProviderModel(settingsPaths, modelId);
|
|
915
877
|
}
|
|
916
|
-
function
|
|
917
|
-
const
|
|
918
|
-
if (
|
|
919
|
-
throw new Error(
|
|
878
|
+
function updateActiveProviderProfileModel(settingsPaths, profileName, modelId) {
|
|
879
|
+
const settingsPath = findLastPathWithProviderProfile(settingsPaths, profileName) ?? settingsPaths[0];
|
|
880
|
+
if (settingsPath === void 0) {
|
|
881
|
+
throw new Error("No settings path available for model update");
|
|
920
882
|
}
|
|
921
|
-
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
stepIndex: state.stepIndex + 1,
|
|
933
|
-
values: { ...state.values, [step.key]: value }
|
|
934
|
-
};
|
|
935
|
-
if (nextState.stepIndex < state.steps.length) {
|
|
936
|
-
return { status: "next", state: nextState };
|
|
937
|
-
}
|
|
938
|
-
return { status: "complete", input: buildProviderSetupInput(nextState) };
|
|
939
|
-
}
|
|
940
|
-
async function runProviderSetupPromptFlow(type, promptInput2, providerDefinitions) {
|
|
941
|
-
let state = createProviderSetupFlow(type, providerDefinitions);
|
|
942
|
-
const stepCount = state.steps.length;
|
|
943
|
-
while (state.stepIndex < stepCount) {
|
|
944
|
-
const step = getProviderSetupStep(state);
|
|
945
|
-
const value = await promptInput2(formatProviderSetupPromptLabel(step), step.masked === true);
|
|
946
|
-
const result = submitProviderSetupValue(state, value);
|
|
947
|
-
if (result.status === "complete") {
|
|
948
|
-
return result.input;
|
|
949
|
-
}
|
|
950
|
-
if (result.status === "error") {
|
|
951
|
-
throw new Error(result.message);
|
|
883
|
+
const settings = readProviderDocument(settingsPath);
|
|
884
|
+
const providers = settings.providers ?? {};
|
|
885
|
+
const existing = providers[profileName] ?? {};
|
|
886
|
+
const next = {
|
|
887
|
+
...settings,
|
|
888
|
+
providers: {
|
|
889
|
+
...providers,
|
|
890
|
+
[profileName]: {
|
|
891
|
+
...existing,
|
|
892
|
+
model: modelId
|
|
893
|
+
}
|
|
952
894
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
}
|
|
957
|
-
function formatProviderSetupPromptLabel(step) {
|
|
958
|
-
const suffix = step.defaultValue !== void 0 ? ` (default: ${step.defaultValue})` : "";
|
|
959
|
-
return ` ${step.title}${suffix}: `;
|
|
960
|
-
}
|
|
961
|
-
function formatProviderSetupChoiceLabel(definition) {
|
|
962
|
-
const label = definition.displayName !== void 0 ? `${definition.displayName} (${definition.type})` : definition.type;
|
|
963
|
-
return definition.description !== void 0 ? `${label} - ${definition.description}` : label;
|
|
895
|
+
};
|
|
896
|
+
writeSettings(settingsPath, next);
|
|
897
|
+
return { settingsPath, settings: next, profileName };
|
|
964
898
|
}
|
|
965
|
-
function
|
|
966
|
-
|
|
967
|
-
|
|
899
|
+
function updateLegacyProviderModel(settingsPaths, modelId) {
|
|
900
|
+
const settingsPath = findLastPathWithLegacyProvider(settingsPaths) ?? settingsPaths[0];
|
|
901
|
+
if (settingsPath === void 0) {
|
|
902
|
+
throw new Error("No settings path available for model update");
|
|
968
903
|
}
|
|
969
|
-
|
|
904
|
+
const settings = readProviderDocument(settingsPath);
|
|
905
|
+
const next = {
|
|
906
|
+
...settings,
|
|
907
|
+
provider: {
|
|
908
|
+
...settings.provider ?? {},
|
|
909
|
+
model: modelId
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
writeSettings(settingsPath, next);
|
|
913
|
+
return { settingsPath, settings: next, legacyProvider: true };
|
|
970
914
|
}
|
|
971
|
-
function
|
|
972
|
-
|
|
973
|
-
|
|
915
|
+
function findLastPathWithProviderProfile(settingsPaths, profileName) {
|
|
916
|
+
for (let index = settingsPaths.length - 1; index >= 0; index -= 1) {
|
|
917
|
+
const settingsPath = settingsPaths[index];
|
|
918
|
+
if (settingsPath === void 0) continue;
|
|
919
|
+
const settings = readProviderDocument(settingsPath);
|
|
920
|
+
if (settings.providers?.[profileName] !== void 0) return settingsPath;
|
|
974
921
|
}
|
|
975
922
|
return void 0;
|
|
976
923
|
}
|
|
977
|
-
function
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
);
|
|
983
|
-
}
|
|
984
|
-
if (definition.setupSteps !== void 0) {
|
|
985
|
-
return [...definition.setupSteps];
|
|
986
|
-
}
|
|
987
|
-
const steps = [
|
|
988
|
-
{
|
|
989
|
-
key: "model",
|
|
990
|
-
title: `${definition.type} model`,
|
|
991
|
-
defaultValue: definition.defaults?.model,
|
|
992
|
-
required: definition.defaults?.model === void 0
|
|
993
|
-
}
|
|
994
|
-
];
|
|
995
|
-
if (definition.defaults?.baseURL !== void 0) {
|
|
996
|
-
steps.unshift({
|
|
997
|
-
key: "baseURL",
|
|
998
|
-
title: `${definition.type} base URL`,
|
|
999
|
-
defaultValue: definition.defaults.baseURL
|
|
1000
|
-
});
|
|
924
|
+
function findLastPathWithLegacyProvider(settingsPaths) {
|
|
925
|
+
for (let index = settingsPaths.length - 1; index >= 0; index -= 1) {
|
|
926
|
+
const settingsPath = settingsPaths[index];
|
|
927
|
+
if (settingsPath === void 0) continue;
|
|
928
|
+
const settings = readProviderDocument(settingsPath);
|
|
929
|
+
if (settings.provider !== void 0) return settingsPath;
|
|
1001
930
|
}
|
|
1002
|
-
|
|
1003
|
-
steps.push({
|
|
1004
|
-
key: "apiKey",
|
|
1005
|
-
title: `${definition.type} API key`,
|
|
1006
|
-
defaultValue: definition.defaults?.apiKey,
|
|
1007
|
-
required: definition.defaults?.apiKey === void 0,
|
|
1008
|
-
masked: true
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
return steps;
|
|
1012
|
-
}
|
|
1013
|
-
function buildProviderSetupInput(state) {
|
|
1014
|
-
return {
|
|
1015
|
-
profile: state.type,
|
|
1016
|
-
type: state.type,
|
|
1017
|
-
model: state.values.model,
|
|
1018
|
-
apiKey: state.values.apiKey,
|
|
1019
|
-
...state.values.baseURL !== void 0 && { baseURL: state.values.baseURL },
|
|
1020
|
-
setCurrent: true
|
|
1021
|
-
};
|
|
931
|
+
return void 0;
|
|
1022
932
|
}
|
|
1023
933
|
|
|
934
|
+
// src/utils/provider-setup-flow.ts
|
|
935
|
+
import {
|
|
936
|
+
createProviderSetupFlow,
|
|
937
|
+
formatProviderSetupChoiceLabel,
|
|
938
|
+
formatProviderSetupPromptLabel,
|
|
939
|
+
formatProviderSetupSelectionPrompt,
|
|
940
|
+
getProviderSetupStep,
|
|
941
|
+
resolveProviderSetupSelection,
|
|
942
|
+
runProviderSetupPromptFlow,
|
|
943
|
+
submitProviderSetupValue,
|
|
944
|
+
validateProviderSetupValue
|
|
945
|
+
} from "@robota-sdk/agent-sdk";
|
|
946
|
+
|
|
1024
947
|
// src/utils/provider-setup.ts
|
|
1025
948
|
function getSettingsPathForScope(cwd, scope) {
|
|
1026
949
|
if (scope === void 0 || scope === "user") {
|
|
@@ -1052,17 +975,25 @@ function handleProviderConfigurationArgs(cwd, args, providerDefinitions = DEFAUL
|
|
|
1052
975
|
return false;
|
|
1053
976
|
}
|
|
1054
977
|
async function ensureConfig(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}));
|
|
1059
|
-
if (checks.some((check) => check.status === "valid")) {
|
|
978
|
+
const merged = readMergedProviderSettings(cwd);
|
|
979
|
+
const selectedSettings = args.provider !== void 0 ? { ...merged, currentProvider: args.provider } : merged;
|
|
980
|
+
if (checkSettingsDocument(selectedSettings, providerDefinitions) === "valid") {
|
|
1060
981
|
return;
|
|
1061
982
|
}
|
|
1062
983
|
if (!isInteractiveTerminal()) {
|
|
1063
984
|
throw new Error(formatMissingProviderConfigMessage(providerDefinitions));
|
|
1064
985
|
}
|
|
1065
|
-
await runInteractiveProviderSetup(
|
|
986
|
+
await runInteractiveProviderSetup(
|
|
987
|
+
cwd,
|
|
988
|
+
selectStartupSetupArgs(cwd, args),
|
|
989
|
+
promptInput2,
|
|
990
|
+
providerDefinitions
|
|
991
|
+
);
|
|
992
|
+
const updated = readMergedProviderSettings(cwd);
|
|
993
|
+
const updatedSettings = args.provider !== void 0 ? { ...updated, currentProvider: args.provider } : updated;
|
|
994
|
+
if (checkSettingsDocument(updatedSettings, providerDefinitions) !== "valid") {
|
|
995
|
+
throw new Error(formatMissingProviderConfigMessage(providerDefinitions));
|
|
996
|
+
}
|
|
1066
997
|
}
|
|
1067
998
|
async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
1068
999
|
const providerChoice = await promptInput2(formatProviderSetupSelectionPrompt(providerDefinitions));
|
|
@@ -1098,15 +1029,31 @@ function buildSetupInputFromArgs(args) {
|
|
|
1098
1029
|
setCurrent: args.setCurrent
|
|
1099
1030
|
};
|
|
1100
1031
|
}
|
|
1101
|
-
function
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1032
|
+
function selectStartupSetupArgs(cwd, args) {
|
|
1033
|
+
if (args.settingsScope !== void 0 || args.provider !== void 0) {
|
|
1034
|
+
return args;
|
|
1035
|
+
}
|
|
1036
|
+
const currentProviderPath = findHighestPriorityCurrentProviderPath(getProviderSettingsPaths(cwd));
|
|
1037
|
+
if (currentProviderPath === void 0) {
|
|
1038
|
+
return args;
|
|
1039
|
+
}
|
|
1040
|
+
const projectSettingsPath = join4(cwd, ".robota", "settings.json");
|
|
1041
|
+
const projectLocalSettingsPath = join4(cwd, ".robota", "settings.local.json");
|
|
1042
|
+
if (currentProviderPath === projectSettingsPath || currentProviderPath === projectLocalSettingsPath) {
|
|
1043
|
+
return { ...args, settingsScope: "project-local" };
|
|
1044
|
+
}
|
|
1045
|
+
return args;
|
|
1046
|
+
}
|
|
1047
|
+
function findHighestPriorityCurrentProviderPath(settingsPaths) {
|
|
1048
|
+
for (let index = settingsPaths.length - 1; index >= 0; index -= 1) {
|
|
1049
|
+
const settingsPath = settingsPaths[index];
|
|
1050
|
+
if (settingsPath === void 0) continue;
|
|
1051
|
+
const settings = readSettings(settingsPath);
|
|
1052
|
+
if (typeof settings.currentProvider === "string") {
|
|
1053
|
+
return settingsPath;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return void 0;
|
|
1110
1057
|
}
|
|
1111
1058
|
function isInteractiveTerminal() {
|
|
1112
1059
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
@@ -1140,19 +1087,15 @@ import { render } from "ink";
|
|
|
1140
1087
|
// src/ui/App.tsx
|
|
1141
1088
|
import { useState as useState14, useEffect as useEffect4 } from "react";
|
|
1142
1089
|
import { Box as Box18, Text as Text20, useApp as useApp2, useInput as useInput8 } from "ink";
|
|
1143
|
-
import {
|
|
1090
|
+
import { createSystemMessage as createSystemMessage6, messageToHistoryEntry as messageToHistoryEntry6 } from "@robota-sdk/agent-core";
|
|
1144
1091
|
|
|
1145
1092
|
// src/ui/hooks/useInteractiveSession.ts
|
|
1146
1093
|
import { useState, useRef, useCallback as useCallback2, useEffect } from "react";
|
|
1147
|
-
import { homedir as homedir2 } from "os";
|
|
1148
|
-
import { join as join5 } from "path";
|
|
1149
1094
|
import {
|
|
1150
1095
|
InteractiveSession,
|
|
1151
|
-
CommandRegistry,
|
|
1152
|
-
|
|
1153
|
-
SkillCommandSource
|
|
1154
|
-
PluginCommandSource,
|
|
1155
|
-
BundlePluginLoader
|
|
1096
|
+
CommandRegistry as CommandRegistry2,
|
|
1097
|
+
createBuiltinCommandModule,
|
|
1098
|
+
SkillCommandSource
|
|
1156
1099
|
} from "@robota-sdk/agent-sdk";
|
|
1157
1100
|
import { createSystemMessage as createSystemMessage2, messageToHistoryEntry as messageToHistoryEntry2 } from "@robota-sdk/agent-core";
|
|
1158
1101
|
|
|
@@ -1178,7 +1121,11 @@ function toBackgroundTaskViewModel(state, partialText) {
|
|
|
1178
1121
|
lastActivityAt: state.lastActivityAt,
|
|
1179
1122
|
timeoutReason: state.timeoutReason,
|
|
1180
1123
|
exitCode: state.result?.exitCode,
|
|
1181
|
-
signalCode: state.result?.signalCode
|
|
1124
|
+
signalCode: state.result?.signalCode,
|
|
1125
|
+
worktreePath: state.worktreePath,
|
|
1126
|
+
branchName: state.branchName,
|
|
1127
|
+
worktreeStatus: state.worktreeStatus,
|
|
1128
|
+
worktreeNextAction: state.worktreeNextAction
|
|
1182
1129
|
};
|
|
1183
1130
|
}
|
|
1184
1131
|
function getBackgroundTaskStatusLabel(state) {
|
|
@@ -1343,6 +1290,14 @@ var TuiStateManager = class {
|
|
|
1343
1290
|
this.history = updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
|
|
1344
1291
|
this.notify();
|
|
1345
1292
|
}
|
|
1293
|
+
clearHistory() {
|
|
1294
|
+
this.history = [];
|
|
1295
|
+
this.debouncedStreamNotify.flush();
|
|
1296
|
+
this.streamBuf = "";
|
|
1297
|
+
this.streamingText = "";
|
|
1298
|
+
this.activeTools = [];
|
|
1299
|
+
this.notify();
|
|
1300
|
+
}
|
|
1346
1301
|
/** Update pending prompt state */
|
|
1347
1302
|
setPendingPrompt(prompt) {
|
|
1348
1303
|
this.pendingPrompt = prompt;
|
|
@@ -1408,133 +1363,35 @@ var TuiStateManager = class {
|
|
|
1408
1363
|
// src/ui/hooks/useSlashRouting.ts
|
|
1409
1364
|
import { useCallback } from "react";
|
|
1410
1365
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1411
|
-
import {
|
|
1412
|
-
createSystemMessage,
|
|
1413
|
-
messageToHistoryEntry
|
|
1414
|
-
} from "@robota-sdk/agent-core";
|
|
1366
|
+
import { createSystemMessage, messageToHistoryEntry } from "@robota-sdk/agent-core";
|
|
1415
1367
|
|
|
1416
|
-
// src/
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
message: formatProviderList(settings.currentProvider, settings.providers),
|
|
1424
|
-
success: true
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
if (subcommand === "current" || subcommand === "") {
|
|
1428
|
-
return {
|
|
1429
|
-
message: formatCurrentProvider(settings.currentProvider, settings.providers),
|
|
1430
|
-
success: true
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
if (subcommand === "use") {
|
|
1434
|
-
return buildProviderSwitch(settings.providers, profileArg);
|
|
1435
|
-
}
|
|
1436
|
-
if (subcommand === "test") {
|
|
1437
|
-
return await testProvider(settings.currentProvider, settings.providers, profileArg, deps);
|
|
1438
|
-
}
|
|
1439
|
-
if (subcommand === "add") {
|
|
1440
|
-
return buildProviderSetup(profileArg, providerDefinitions);
|
|
1441
|
-
}
|
|
1442
|
-
return {
|
|
1443
|
-
message: "Usage: provider [current|list|use <profile>|add <type>|test [profile]]",
|
|
1444
|
-
success: false
|
|
1445
|
-
};
|
|
1446
|
-
}
|
|
1447
|
-
function formatProviderList(currentProvider, providers) {
|
|
1448
|
-
const entries = Object.entries(providers ?? {});
|
|
1449
|
-
if (entries.length === 0) {
|
|
1450
|
-
return "No provider profiles configured.";
|
|
1451
|
-
}
|
|
1452
|
-
return entries.map(([name, profile]) => {
|
|
1453
|
-
const marker = name === currentProvider ? "*" : "-";
|
|
1454
|
-
return `${marker} ${name}: ${profile.type ?? "unknown"} ${profile.model ?? "(no model)"}`;
|
|
1455
|
-
}).join("\n");
|
|
1456
|
-
}
|
|
1457
|
-
function formatCurrentProvider(currentProvider, providers) {
|
|
1458
|
-
if (!currentProvider) {
|
|
1459
|
-
return "No current provider configured.";
|
|
1460
|
-
}
|
|
1461
|
-
const profile = providers?.[currentProvider];
|
|
1462
|
-
if (!profile) {
|
|
1463
|
-
return `Current provider "${currentProvider}" was not found in providers.`;
|
|
1464
|
-
}
|
|
1465
|
-
return [
|
|
1466
|
-
`Current provider: ${currentProvider}`,
|
|
1467
|
-
`Type: ${profile.type ?? "unknown"}`,
|
|
1468
|
-
`Model: ${profile.model ?? "(no model)"}`,
|
|
1469
|
-
...profile.baseURL ? [`Base URL: ${profile.baseURL}`] : []
|
|
1470
|
-
].join("\n");
|
|
1471
|
-
}
|
|
1472
|
-
function buildProviderSwitch(providers, profileName) {
|
|
1473
|
-
if (!profileName) {
|
|
1474
|
-
return { message: "Usage: provider use <profile>", success: false };
|
|
1475
|
-
}
|
|
1476
|
-
if (!providers?.[profileName]) {
|
|
1477
|
-
return { message: `Provider profile "${profileName}" was not found.`, success: false };
|
|
1478
|
-
}
|
|
1479
|
-
return {
|
|
1480
|
-
message: `Provider change requested: ${profileName}`,
|
|
1481
|
-
success: true,
|
|
1482
|
-
data: { providerSwitch: { profile: profileName } }
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
function buildProviderSetup(type, providerDefinitions) {
|
|
1486
|
-
if (!type) {
|
|
1487
|
-
return {
|
|
1488
|
-
message: "Provider setup requested. Select a provider to continue.",
|
|
1489
|
-
success: true,
|
|
1490
|
-
data: { providerSetup: {} }
|
|
1491
|
-
};
|
|
1492
|
-
}
|
|
1493
|
-
if (findProviderDefinition(providerDefinitions, type) === void 0) {
|
|
1494
|
-
return {
|
|
1495
|
-
message: `Usage: provider add <type>. Supported: ${formatSupportedProviderTypes(providerDefinitions)}`,
|
|
1496
|
-
success: false
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
|
-
return {
|
|
1500
|
-
message: `Provider setup requested: ${type}`,
|
|
1501
|
-
success: true,
|
|
1502
|
-
data: { providerSetup: { type } }
|
|
1503
|
-
};
|
|
1368
|
+
// src/plugins/plugin-command-source-loader.ts
|
|
1369
|
+
import { homedir } from "os";
|
|
1370
|
+
import { join as join5 } from "path";
|
|
1371
|
+
import { BundlePluginLoader, PluginCommandSource } from "@robota-sdk/agent-sdk";
|
|
1372
|
+
var PLUGIN_SOURCE_NAME = "plugin";
|
|
1373
|
+
function getHomeDir() {
|
|
1374
|
+
return process.env.HOME ?? homedir();
|
|
1504
1375
|
}
|
|
1505
|
-
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
return { message: "No provider profile selected.", success: false };
|
|
1509
|
-
}
|
|
1510
|
-
const profile = providers?.[profileName];
|
|
1511
|
-
if (!profile) {
|
|
1512
|
-
return { message: `Provider profile "${profileName}" was not found.`, success: false };
|
|
1513
|
-
}
|
|
1376
|
+
function reloadPluginCommandSource(registry) {
|
|
1377
|
+
const pluginsDir = join5(getHomeDir(), ".robota", "plugins");
|
|
1378
|
+
const loader = new BundlePluginLoader(pluginsDir);
|
|
1514
1379
|
try {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1380
|
+
const plugins = loader.loadPluginsSync();
|
|
1381
|
+
if (plugins.length === 0) {
|
|
1382
|
+
registry.replaceSource(PLUGIN_SOURCE_NAME);
|
|
1383
|
+
return 0;
|
|
1384
|
+
}
|
|
1385
|
+
registry.replaceSource(PLUGIN_SOURCE_NAME, new PluginCommandSource(plugins));
|
|
1386
|
+
return plugins.length;
|
|
1387
|
+
} catch {
|
|
1388
|
+
registry.replaceSource(PLUGIN_SOURCE_NAME);
|
|
1389
|
+
return 0;
|
|
1520
1390
|
}
|
|
1521
|
-
const providerDefinitions = deps.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
1522
|
-
const definition = profile.type ? findProviderDefinition(providerDefinitions, profile.type) : void 0;
|
|
1523
|
-
const probe = deps.probe ?? definition?.probeProfile ?? probeProviderProfile;
|
|
1524
|
-
const result = await probe(profile);
|
|
1525
|
-
return {
|
|
1526
|
-
message: result.ok ? `Provider "${profileName}" test passed: ${result.message}` : `Provider "${profileName}" test failed: ${result.message}; manual configuration can continue.`,
|
|
1527
|
-
success: true,
|
|
1528
|
-
data: { providerTest: { profile: profileName } }
|
|
1529
|
-
};
|
|
1530
|
-
}
|
|
1531
|
-
async function probeProviderProfile(profile) {
|
|
1532
|
-
void profile;
|
|
1533
|
-
return { ok: true, message: "Profile fields are valid; no endpoint probe configured." };
|
|
1534
1391
|
}
|
|
1535
1392
|
|
|
1536
1393
|
// src/ui/hooks/useSlashRouting.ts
|
|
1537
|
-
function useSlashRouting(
|
|
1394
|
+
function useSlashRouting(interactiveSession, registry, manager) {
|
|
1538
1395
|
return useCallback(
|
|
1539
1396
|
async (input) => {
|
|
1540
1397
|
manager.onUserTurnAccepted();
|
|
@@ -1546,70 +1403,32 @@ function useSlashRouting(cwd, interactiveSession, registry, manager, providerDef
|
|
|
1546
1403
|
const parts = input.slice(1).split(/\s+/);
|
|
1547
1404
|
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
1548
1405
|
const args = parts.slice(1).join(" ");
|
|
1549
|
-
if (cmd === "provider") {
|
|
1550
|
-
await routeProviderCommand(cwd, args, interactiveSession, manager, providerDefinitions);
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
1406
|
const result = await interactiveSession.executeCommand(cmd, args);
|
|
1554
1407
|
if (result) {
|
|
1555
|
-
applySystemCommandResult(result, interactiveSession, manager);
|
|
1408
|
+
applySystemCommandResult(result, interactiveSession, registry, manager);
|
|
1556
1409
|
return;
|
|
1557
1410
|
}
|
|
1558
1411
|
if (await routeSkillCommand(input, cmd, registry, interactiveSession, manager)) {
|
|
1559
1412
|
return;
|
|
1560
1413
|
}
|
|
1561
|
-
if (routeTuiCommand(cmd, interactiveSession)) {
|
|
1562
|
-
return;
|
|
1563
|
-
}
|
|
1564
1414
|
manager.addEntry(
|
|
1565
1415
|
messageToHistoryEntry(
|
|
1566
1416
|
createSystemMessage(`Unknown command "/${cmd}". Type /help for help.`)
|
|
1567
1417
|
)
|
|
1568
1418
|
);
|
|
1569
1419
|
},
|
|
1570
|
-
[
|
|
1420
|
+
[interactiveSession, registry, manager]
|
|
1571
1421
|
);
|
|
1572
1422
|
}
|
|
1573
|
-
|
|
1574
|
-
const
|
|
1423
|
+
function applySystemCommandResult(result, interactiveSession, registry, manager) {
|
|
1424
|
+
const pendingEffects = applyImmediateCommandEffects(result.effects, registry, manager);
|
|
1575
1425
|
manager.addEntry(messageToHistoryEntry(createSystemMessage(result.message)));
|
|
1576
|
-
const providerSwitch = result.data?.providerSwitch;
|
|
1577
|
-
if (providerSwitch?.profile) {
|
|
1578
|
-
getEffects(interactiveSession)._pendingProviderProfile = providerSwitch.profile;
|
|
1579
|
-
}
|
|
1580
|
-
const providerSetup = result.data?.providerSetup;
|
|
1581
|
-
if (providerSetup !== void 0) {
|
|
1582
|
-
getEffects(interactiveSession)._pendingProviderSetup = providerSetup;
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
function applySystemCommandResult(result, interactiveSession, manager) {
|
|
1586
|
-
manager.addEntry(messageToHistoryEntry(createSystemMessage(result.message)));
|
|
1587
|
-
const data = result.data;
|
|
1588
1426
|
const effects = getEffects(interactiveSession);
|
|
1589
|
-
if (
|
|
1590
|
-
effects.
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
if (typeof data?.language === "string") {
|
|
1594
|
-
effects._pendingLanguage = data.language;
|
|
1595
|
-
return;
|
|
1427
|
+
if (result.interaction !== void 0) {
|
|
1428
|
+
effects._pendingCommandInteraction = result.interaction;
|
|
1596
1429
|
}
|
|
1597
|
-
if (
|
|
1598
|
-
effects.
|
|
1599
|
-
return;
|
|
1600
|
-
}
|
|
1601
|
-
if (data?.triggerResumePicker === true) {
|
|
1602
|
-
effects._triggerResumePicker = true;
|
|
1603
|
-
return;
|
|
1604
|
-
}
|
|
1605
|
-
if (typeof data?.name === "string") {
|
|
1606
|
-
effects._sessionName = data.name;
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
const statusLinePatch = data?.statuslinePatch;
|
|
1610
|
-
if (isStatusLineSettingsPatch(statusLinePatch)) {
|
|
1611
|
-
effects._statusLinePatch = statusLinePatch;
|
|
1612
|
-
return;
|
|
1430
|
+
if (pendingEffects.length > 0) {
|
|
1431
|
+
effects._pendingCommandEffects = pendingEffects;
|
|
1613
1432
|
}
|
|
1614
1433
|
const ctx = interactiveSession.getContextState();
|
|
1615
1434
|
manager.setContextState({
|
|
@@ -1618,6 +1437,22 @@ function applySystemCommandResult(result, interactiveSession, manager) {
|
|
|
1618
1437
|
maxTokens: ctx.maxTokens
|
|
1619
1438
|
});
|
|
1620
1439
|
}
|
|
1440
|
+
function applyImmediateCommandEffects(effects, registry, manager) {
|
|
1441
|
+
if (effects === void 0 || effects.length === 0) return [];
|
|
1442
|
+
const pendingEffects = [];
|
|
1443
|
+
for (const effect of effects) {
|
|
1444
|
+
if (effect.type === "conversation-history-cleared") {
|
|
1445
|
+
manager.clearHistory();
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
if (effect.type === "plugin-registry-reload-requested") {
|
|
1449
|
+
reloadPluginCommandSource(registry);
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
pendingEffects.push(effect);
|
|
1453
|
+
}
|
|
1454
|
+
return pendingEffects;
|
|
1455
|
+
}
|
|
1621
1456
|
async function routeSkillCommand(input, cmd, registry, interactiveSession, manager) {
|
|
1622
1457
|
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
1623
1458
|
if (!skillCmd) {
|
|
@@ -1641,29 +1476,14 @@ async function routeSkillCommand(input, cmd, registry, interactiveSession, manag
|
|
|
1641
1476
|
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
1642
1477
|
return true;
|
|
1643
1478
|
}
|
|
1644
|
-
function routeTuiCommand(cmd, interactiveSession) {
|
|
1645
|
-
if (cmd === "exit") {
|
|
1646
|
-
getEffects(interactiveSession)._exitRequested = true;
|
|
1647
|
-
return true;
|
|
1648
|
-
}
|
|
1649
|
-
if (cmd === "plugin") {
|
|
1650
|
-
getEffects(interactiveSession)._triggerPluginTUI = true;
|
|
1651
|
-
return true;
|
|
1652
|
-
}
|
|
1653
|
-
return false;
|
|
1654
|
-
}
|
|
1655
1479
|
function getEffects(interactiveSession) {
|
|
1656
1480
|
return interactiveSession;
|
|
1657
1481
|
}
|
|
1658
|
-
function isStatusLineSettingsPatch(value) {
|
|
1659
|
-
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) {
|
|
1660
|
-
return false;
|
|
1661
|
-
}
|
|
1662
|
-
const candidate = value;
|
|
1663
|
-
return (candidate.enabled === void 0 || typeof candidate.enabled === "boolean") && (candidate.gitBranch === void 0 || typeof candidate.gitBranch === "boolean");
|
|
1664
|
-
}
|
|
1665
1482
|
|
|
1666
1483
|
// src/ui/hooks/useInteractiveSession.ts
|
|
1484
|
+
function applyCompactEventToManager(interactiveSession, manager) {
|
|
1485
|
+
manager.syncHistory(interactiveSession.getFullHistory());
|
|
1486
|
+
}
|
|
1667
1487
|
function initializeSession(props, permissionHandler) {
|
|
1668
1488
|
const interactiveSession = new InteractiveSession({
|
|
1669
1489
|
cwd: props.cwd,
|
|
@@ -1677,23 +1497,16 @@ function initializeSession(props, permissionHandler) {
|
|
|
1677
1497
|
sessionName: props.sessionName,
|
|
1678
1498
|
backgroundTaskRunners: props.backgroundTaskRunners,
|
|
1679
1499
|
subagentRunnerFactory: props.subagentRunnerFactory,
|
|
1680
|
-
commandModules: props.commandModules
|
|
1500
|
+
commandModules: props.commandModules,
|
|
1501
|
+
commandHostAdapters: props.commandHostAdapters
|
|
1681
1502
|
});
|
|
1682
|
-
const registry = new
|
|
1683
|
-
registry.
|
|
1503
|
+
const registry = new CommandRegistry2();
|
|
1504
|
+
registry.addModule(createBuiltinCommandModule());
|
|
1684
1505
|
for (const module of props.commandModules ?? []) {
|
|
1685
1506
|
registry.addModule(module);
|
|
1686
1507
|
}
|
|
1687
1508
|
registry.addSource(new SkillCommandSource(props.cwd));
|
|
1688
|
-
|
|
1689
|
-
const loader = new BundlePluginLoader(pluginsDir);
|
|
1690
|
-
try {
|
|
1691
|
-
const plugins = loader.loadPluginsSync();
|
|
1692
|
-
if (plugins.length > 0) {
|
|
1693
|
-
registry.addSource(new PluginCommandSource(plugins));
|
|
1694
|
-
}
|
|
1695
|
-
} catch {
|
|
1696
|
-
}
|
|
1509
|
+
reloadPluginCommandSource(registry);
|
|
1697
1510
|
const manager = new TuiStateManager();
|
|
1698
1511
|
return { interactiveSession, registry, manager };
|
|
1699
1512
|
}
|
|
@@ -1743,6 +1556,7 @@ function useInteractiveSession(props) {
|
|
|
1743
1556
|
}
|
|
1744
1557
|
}
|
|
1745
1558
|
useEffect(() => {
|
|
1559
|
+
const onCompact = () => applyCompactEventToManager(interactiveSession, manager);
|
|
1746
1560
|
interactiveSession.on("text_delta", manager.onTextDelta);
|
|
1747
1561
|
interactiveSession.on("tool_start", manager.onToolStart);
|
|
1748
1562
|
interactiveSession.on("tool_end", manager.onToolEnd);
|
|
@@ -1751,6 +1565,7 @@ function useInteractiveSession(props) {
|
|
|
1751
1565
|
interactiveSession.on("interrupted", manager.onInterrupted);
|
|
1752
1566
|
interactiveSession.on("error", manager.onError);
|
|
1753
1567
|
interactiveSession.on("context_update", manager.onContextUpdate);
|
|
1568
|
+
interactiveSession.on("compact", onCompact);
|
|
1754
1569
|
interactiveSession.on("background_task_event", manager.onBackgroundTaskEvent);
|
|
1755
1570
|
const initCheck = setInterval(() => {
|
|
1756
1571
|
try {
|
|
@@ -1778,6 +1593,7 @@ function useInteractiveSession(props) {
|
|
|
1778
1593
|
interactiveSession.off("interrupted", manager.onInterrupted);
|
|
1779
1594
|
interactiveSession.off("error", manager.onError);
|
|
1780
1595
|
interactiveSession.off("context_update", manager.onContextUpdate);
|
|
1596
|
+
interactiveSession.off("compact", onCompact);
|
|
1781
1597
|
interactiveSession.off("background_task_event", manager.onBackgroundTaskEvent);
|
|
1782
1598
|
};
|
|
1783
1599
|
}, [interactiveSession, manager]);
|
|
@@ -1787,13 +1603,7 @@ function useInteractiveSession(props) {
|
|
|
1787
1603
|
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
1788
1604
|
}
|
|
1789
1605
|
}, [manager.isThinking, interactiveSession, manager]);
|
|
1790
|
-
const handleSubmit = useSlashRouting(
|
|
1791
|
-
props.cwd,
|
|
1792
|
-
interactiveSession,
|
|
1793
|
-
registry,
|
|
1794
|
-
manager,
|
|
1795
|
-
props.providerDefinitions ?? []
|
|
1796
|
-
);
|
|
1606
|
+
const handleSubmit = useSlashRouting(interactiveSession, registry, manager);
|
|
1797
1607
|
const handleAbort = useCallback2(() => {
|
|
1798
1608
|
manager.setAborting(true);
|
|
1799
1609
|
interactiveSession.abort();
|
|
@@ -1834,184 +1644,134 @@ function useInteractiveSession(props) {
|
|
|
1834
1644
|
|
|
1835
1645
|
// src/ui/hooks/usePluginCallbacks.ts
|
|
1836
1646
|
import { useMemo } from "react";
|
|
1837
|
-
|
|
1647
|
+
|
|
1648
|
+
// src/plugins/plugin-command-adapter.ts
|
|
1649
|
+
import { homedir as homedir2 } from "os";
|
|
1838
1650
|
import { join as join6 } from "path";
|
|
1839
1651
|
import {
|
|
1840
|
-
PluginSettingsStore,
|
|
1841
|
-
BundlePluginLoader as BundlePluginLoader2,
|
|
1842
1652
|
BundlePluginInstaller,
|
|
1843
|
-
|
|
1653
|
+
BundlePluginLoader as BundlePluginLoader2,
|
|
1654
|
+
MarketplaceClient,
|
|
1655
|
+
PluginSettingsStore
|
|
1844
1656
|
} from "@robota-sdk/agent-sdk";
|
|
1845
|
-
function
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
const parts = p.pluginDir.split("/");
|
|
1864
|
-
const cacheIdx = parts.indexOf("cache");
|
|
1865
|
-
const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
|
|
1866
|
-
const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
|
|
1867
|
-
return {
|
|
1868
|
-
name: fullId,
|
|
1869
|
-
description: p.manifest.description,
|
|
1870
|
-
enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
|
|
1871
|
-
};
|
|
1872
|
-
});
|
|
1873
|
-
},
|
|
1874
|
-
listAvailablePlugins: async (marketplaceName) => {
|
|
1875
|
-
let manifest;
|
|
1876
|
-
try {
|
|
1877
|
-
manifest = marketplace.fetchManifest(marketplaceName);
|
|
1878
|
-
} catch {
|
|
1879
|
-
return [];
|
|
1880
|
-
}
|
|
1881
|
-
const installed = installer.getInstalledPlugins();
|
|
1882
|
-
const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
|
|
1883
|
-
return manifest.plugins.map((p) => ({
|
|
1884
|
-
name: p.name,
|
|
1885
|
-
description: p.description,
|
|
1886
|
-
installed: installedNames.has(p.name)
|
|
1887
|
-
}));
|
|
1888
|
-
},
|
|
1889
|
-
install: async (pluginId, scope) => {
|
|
1890
|
-
const [name, marketplaceName] = pluginId.split("@");
|
|
1891
|
-
if (!name || !marketplaceName) {
|
|
1892
|
-
throw new Error("Plugin ID must be in format: name@marketplace");
|
|
1893
|
-
}
|
|
1894
|
-
if (scope === "project") {
|
|
1895
|
-
const projectPluginsDir = join6(cwd, ".robota", "plugins");
|
|
1896
|
-
const projectInstaller = new BundlePluginInstaller({
|
|
1897
|
-
pluginsDir: projectPluginsDir,
|
|
1898
|
-
settingsStore,
|
|
1899
|
-
marketplaceClient: marketplace
|
|
1900
|
-
});
|
|
1901
|
-
await projectInstaller.install(name, marketplaceName);
|
|
1902
|
-
} else {
|
|
1903
|
-
await installer.install(name, marketplaceName);
|
|
1904
|
-
}
|
|
1905
|
-
},
|
|
1906
|
-
uninstall: async (pluginId) => {
|
|
1907
|
-
await installer.uninstall(pluginId);
|
|
1908
|
-
},
|
|
1909
|
-
enable: async (pluginId) => {
|
|
1910
|
-
await installer.enable(pluginId);
|
|
1911
|
-
},
|
|
1912
|
-
disable: async (pluginId) => {
|
|
1913
|
-
await installer.disable(pluginId);
|
|
1914
|
-
},
|
|
1915
|
-
marketplaceAdd: async (source) => {
|
|
1916
|
-
if (source.includes("/") && !source.includes(":")) {
|
|
1917
|
-
return marketplace.addMarketplace({ type: "github", repo: source });
|
|
1918
|
-
} else {
|
|
1919
|
-
return marketplace.addMarketplace({ type: "git", url: source });
|
|
1920
|
-
}
|
|
1921
|
-
},
|
|
1922
|
-
marketplaceRemove: async (name) => {
|
|
1923
|
-
const installedFromMarketplace = installer.getPluginsByMarketplace(name);
|
|
1924
|
-
for (const record of installedFromMarketplace) {
|
|
1925
|
-
await installer.uninstall(`${record.pluginName}@${record.marketplace}`);
|
|
1926
|
-
}
|
|
1927
|
-
marketplace.removeMarketplace(name);
|
|
1928
|
-
},
|
|
1929
|
-
marketplaceUpdate: async (name) => {
|
|
1930
|
-
marketplace.updateMarketplace(name);
|
|
1931
|
-
},
|
|
1932
|
-
marketplaceList: async () => {
|
|
1933
|
-
return marketplace.listMarketplaces().map((m) => ({
|
|
1934
|
-
name: m.name,
|
|
1935
|
-
type: m.source.type
|
|
1936
|
-
}));
|
|
1937
|
-
},
|
|
1938
|
-
reloadPlugins: async () => {
|
|
1939
|
-
}
|
|
1940
|
-
};
|
|
1941
|
-
}, [cwd]);
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
// src/ui/hooks/useSideEffects.ts
|
|
1945
|
-
import { useState as useState2, useRef as useRef2, useCallback as useCallback3 } from "react";
|
|
1946
|
-
import { useApp } from "ink";
|
|
1947
|
-
import { createSystemMessage as createSystemMessage3, messageToHistoryEntry as messageToHistoryEntry3, getModelName } from "@robota-sdk/agent-core";
|
|
1948
|
-
|
|
1949
|
-
// src/utils/provider-setup-interaction.ts
|
|
1950
|
-
function startProviderSetupInteraction(providerDefinitions, type) {
|
|
1951
|
-
if (type === void 0) {
|
|
1952
|
-
const state2 = {
|
|
1953
|
-
mode: "select-provider",
|
|
1954
|
-
providerDefinitions
|
|
1955
|
-
};
|
|
1956
|
-
return { status: "prompt", state: state2, prompt: toProviderSelectionPrompt(providerDefinitions) };
|
|
1957
|
-
}
|
|
1958
|
-
const state = {
|
|
1959
|
-
mode: "setup-fields",
|
|
1960
|
-
providerDefinitions,
|
|
1961
|
-
flow: createProviderSetupFlow(type, providerDefinitions)
|
|
1657
|
+
function createCliPluginServices(cwd) {
|
|
1658
|
+
const home = homedir2();
|
|
1659
|
+
const pluginsDir = join6(home, ".robota", "plugins");
|
|
1660
|
+
const userSettingsPath = join6(home, ".robota", "settings.json");
|
|
1661
|
+
const settingsStore = new PluginSettingsStore(userSettingsPath);
|
|
1662
|
+
const marketplace = new MarketplaceClient({ pluginsDir });
|
|
1663
|
+
const installer = new BundlePluginInstaller({
|
|
1664
|
+
pluginsDir,
|
|
1665
|
+
settingsStore,
|
|
1666
|
+
marketplaceClient: marketplace
|
|
1667
|
+
});
|
|
1668
|
+
const loader = new BundlePluginLoader2(pluginsDir);
|
|
1669
|
+
return {
|
|
1670
|
+
cwd,
|
|
1671
|
+
marketplace,
|
|
1672
|
+
installer,
|
|
1673
|
+
loader,
|
|
1674
|
+
settingsStore
|
|
1962
1675
|
};
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
};
|
|
1676
|
+
}
|
|
1677
|
+
async function listInstalledPlugins(services) {
|
|
1678
|
+
const plugins = await services.loader.loadAll();
|
|
1679
|
+
const enabledMap = services.settingsStore.getEnabledPlugins();
|
|
1680
|
+
return plugins.map((plugin) => {
|
|
1681
|
+
const parts = plugin.pluginDir.split("/");
|
|
1682
|
+
const cacheIdx = parts.indexOf("cache");
|
|
1683
|
+
const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] ?? "" : "";
|
|
1684
|
+
const fullId = marketplaceName ? `${plugin.manifest.name}@${marketplaceName}` : plugin.manifest.name;
|
|
1972
1685
|
return {
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1686
|
+
name: fullId,
|
|
1687
|
+
description: plugin.manifest.description,
|
|
1688
|
+
enabled: enabledMap[fullId] !== false && enabledMap[plugin.manifest.name] !== false
|
|
1976
1689
|
};
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
async function listAvailablePlugins(services, marketplaceName) {
|
|
1693
|
+
let manifest;
|
|
1694
|
+
try {
|
|
1695
|
+
manifest = services.marketplace.fetchManifest(marketplaceName);
|
|
1696
|
+
} catch {
|
|
1697
|
+
return [];
|
|
1698
|
+
}
|
|
1699
|
+
const installed = services.installer.getInstalledPlugins();
|
|
1700
|
+
const installedNames = new Set(Object.values(installed).map((record) => record.pluginName));
|
|
1701
|
+
return manifest.plugins.map((plugin) => ({
|
|
1702
|
+
name: plugin.name,
|
|
1703
|
+
description: plugin.description,
|
|
1704
|
+
installed: installedNames.has(plugin.name)
|
|
1705
|
+
}));
|
|
1706
|
+
}
|
|
1707
|
+
async function installPlugin(services, pluginId, scope) {
|
|
1708
|
+
const [name, marketplaceName] = pluginId.split("@");
|
|
1709
|
+
if (!name || !marketplaceName) {
|
|
1710
|
+
throw new Error("Plugin ID must be in format: name@marketplace");
|
|
1711
|
+
}
|
|
1712
|
+
if (scope === "project") {
|
|
1713
|
+
const projectPluginsDir = join6(services.cwd, ".robota", "plugins");
|
|
1714
|
+
const projectInstaller = new BundlePluginInstaller({
|
|
1715
|
+
pluginsDir: projectPluginsDir,
|
|
1716
|
+
settingsStore: services.settingsStore,
|
|
1717
|
+
marketplaceClient: services.marketplace
|
|
1718
|
+
});
|
|
1719
|
+
await projectInstaller.install(name, marketplaceName);
|
|
1720
|
+
return;
|
|
1977
1721
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1722
|
+
await services.installer.install(name, marketplaceName);
|
|
1723
|
+
}
|
|
1724
|
+
async function removeMarketplace(services, name) {
|
|
1725
|
+
const installedFromMarketplace = services.installer.getPluginsByMarketplace(name);
|
|
1726
|
+
for (const record of installedFromMarketplace) {
|
|
1727
|
+
await services.installer.uninstall(`${record.pluginName}@${record.marketplace}`);
|
|
1981
1728
|
}
|
|
1982
|
-
|
|
1983
|
-
...state,
|
|
1984
|
-
flow: result.state
|
|
1985
|
-
};
|
|
1986
|
-
return { status: "prompt", state: nextState, prompt: toProviderSetupStepPrompt(result.state) };
|
|
1729
|
+
services.marketplace.removeMarketplace(name);
|
|
1987
1730
|
}
|
|
1988
|
-
function
|
|
1989
|
-
return {
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
value: definition.type,
|
|
1994
|
-
label: formatProviderSetupChoiceLabel(definition)
|
|
1995
|
-
})),
|
|
1996
|
-
maxVisible: 6
|
|
1997
|
-
};
|
|
1731
|
+
function listMarketplaces(services) {
|
|
1732
|
+
return services.marketplace.listMarketplaces().map((marketplaceEntry) => ({
|
|
1733
|
+
name: marketplaceEntry.name,
|
|
1734
|
+
type: marketplaceEntry.source.type
|
|
1735
|
+
}));
|
|
1998
1736
|
}
|
|
1999
|
-
function
|
|
2000
|
-
const
|
|
1737
|
+
function createCliPluginCommandAdapter(cwd) {
|
|
1738
|
+
const services = createCliPluginServices(cwd);
|
|
2001
1739
|
return {
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
1740
|
+
listInstalled: () => listInstalledPlugins(services),
|
|
1741
|
+
listAvailablePlugins: (marketplaceName) => listAvailablePlugins(services, marketplaceName),
|
|
1742
|
+
install: (pluginId, scope) => installPlugin(services, pluginId, scope),
|
|
1743
|
+
uninstall: async (pluginId) => services.installer.uninstall(pluginId),
|
|
1744
|
+
enable: async (pluginId) => services.installer.enable(pluginId),
|
|
1745
|
+
disable: async (pluginId) => services.installer.disable(pluginId),
|
|
1746
|
+
marketplaceAdd: async (source) => {
|
|
1747
|
+
if (source.includes("/") && !source.includes(":")) {
|
|
1748
|
+
return services.marketplace.addMarketplace({ type: "github", repo: source });
|
|
1749
|
+
}
|
|
1750
|
+
return services.marketplace.addMarketplace({ type: "git", url: source });
|
|
1751
|
+
},
|
|
1752
|
+
marketplaceRemove: (name) => removeMarketplace(services, name),
|
|
1753
|
+
marketplaceUpdate: async (name) => services.marketplace.updateMarketplace(name),
|
|
1754
|
+
marketplaceList: async () => listMarketplaces(services),
|
|
1755
|
+
reloadPlugins: async () => ({
|
|
1756
|
+
loadedPluginCount: (await services.loader.loadAll()).length
|
|
1757
|
+
})
|
|
2008
1758
|
};
|
|
2009
1759
|
}
|
|
2010
1760
|
|
|
1761
|
+
// src/ui/hooks/usePluginCallbacks.ts
|
|
1762
|
+
function usePluginCallbacks(cwd) {
|
|
1763
|
+
return useMemo(() => createCliPluginCommandAdapter(cwd), [cwd]);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// src/ui/hooks/useSideEffects.ts
|
|
1767
|
+
import { useState as useState2, useRef as useRef2, useCallback as useCallback3 } from "react";
|
|
1768
|
+
import { useApp } from "ink";
|
|
1769
|
+
import { createSystemMessage as createSystemMessage5, messageToHistoryEntry as messageToHistoryEntry5 } from "@robota-sdk/agent-core";
|
|
1770
|
+
|
|
2011
1771
|
// src/utils/statusline-settings.ts
|
|
1772
|
+
import { DEFAULT_STATUS_LINE_COMMAND_SETTINGS } from "@robota-sdk/agent-sdk";
|
|
2012
1773
|
var DEFAULT_STATUS_LINE_SETTINGS = {
|
|
2013
|
-
|
|
2014
|
-
gitBranch: true
|
|
1774
|
+
...DEFAULT_STATUS_LINE_COMMAND_SETTINGS
|
|
2015
1775
|
};
|
|
2016
1776
|
function readStatusLineSettings(settings) {
|
|
2017
1777
|
const raw = settings.statusline;
|
|
@@ -2046,39 +1806,196 @@ function applyPendingStatusLinePatch(sideEffects, setStatusLineSettings) {
|
|
|
2046
1806
|
return true;
|
|
2047
1807
|
}
|
|
2048
1808
|
|
|
1809
|
+
// src/ui/hooks/command-effect-handler.ts
|
|
1810
|
+
import { isStatusLineCommandSettingsPatch } from "@robota-sdk/agent-sdk";
|
|
1811
|
+
import { createSystemMessage as createSystemMessage3, messageToHistoryEntry as messageToHistoryEntry3 } from "@robota-sdk/agent-core";
|
|
1812
|
+
function applyCommandEffects(effects, sideEffects, deps) {
|
|
1813
|
+
for (const effect of effects) {
|
|
1814
|
+
if (effect.type === "model-change-requested") {
|
|
1815
|
+
deps.requestModelChange(effect.modelId);
|
|
1816
|
+
return true;
|
|
1817
|
+
}
|
|
1818
|
+
if (effect.type === "language-change-requested") {
|
|
1819
|
+
applyLanguageEffect(effect.language, deps);
|
|
1820
|
+
return true;
|
|
1821
|
+
}
|
|
1822
|
+
if (effect.type === "settings-reset-requested") {
|
|
1823
|
+
applySettingsResetEffect(deps);
|
|
1824
|
+
return true;
|
|
1825
|
+
}
|
|
1826
|
+
if (effect.type === "session-exit-requested") {
|
|
1827
|
+
deps.requestShutdown(
|
|
1828
|
+
effect.reason ?? "prompt_input_exit",
|
|
1829
|
+
effect.message ?? "User requested exit"
|
|
1830
|
+
);
|
|
1831
|
+
return true;
|
|
1832
|
+
}
|
|
1833
|
+
if (effect.type === "session-restart-requested") {
|
|
1834
|
+
deps.requestShutdown(effect.reason, effect.message);
|
|
1835
|
+
return true;
|
|
1836
|
+
}
|
|
1837
|
+
if (effect.type === "plugin-tui-requested") {
|
|
1838
|
+
deps.openPluginTUI();
|
|
1839
|
+
return true;
|
|
1840
|
+
}
|
|
1841
|
+
if (effect.type === "session-picker-requested") {
|
|
1842
|
+
deps.openSessionPicker();
|
|
1843
|
+
return true;
|
|
1844
|
+
}
|
|
1845
|
+
if (effect.type === "session-renamed") {
|
|
1846
|
+
deps.renameSession(effect.name);
|
|
1847
|
+
return true;
|
|
1848
|
+
}
|
|
1849
|
+
if (effect.type === "statusline-settings-patch") {
|
|
1850
|
+
if (isStatusLineCommandSettingsPatch(effect.patch)) {
|
|
1851
|
+
sideEffects._statusLinePatch = effect.patch;
|
|
1852
|
+
if (deps.applyStatusLinePatch(effect.patch)) return true;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
return false;
|
|
1857
|
+
}
|
|
1858
|
+
function applyLanguageEffect(language, deps) {
|
|
1859
|
+
const settingsPath = getUserSettingsPath();
|
|
1860
|
+
const settings = readSettings(settingsPath);
|
|
1861
|
+
settings.language = language;
|
|
1862
|
+
writeSettings(settingsPath, settings);
|
|
1863
|
+
deps.addEntry(
|
|
1864
|
+
messageToHistoryEntry3(createSystemMessage3(`Language set to "${language}". Restarting...`))
|
|
1865
|
+
);
|
|
1866
|
+
deps.requestShutdown("other", "Language change restart");
|
|
1867
|
+
}
|
|
1868
|
+
function applySettingsResetEffect(deps) {
|
|
1869
|
+
const settingsPath = getUserSettingsPath();
|
|
1870
|
+
if (deleteSettings(settingsPath)) {
|
|
1871
|
+
deps.addEntry(
|
|
1872
|
+
messageToHistoryEntry3(createSystemMessage3(`Deleted ${settingsPath}. Exiting...`))
|
|
1873
|
+
);
|
|
1874
|
+
} else {
|
|
1875
|
+
deps.addEntry(messageToHistoryEntry3(createSystemMessage3("No user settings found.")));
|
|
1876
|
+
}
|
|
1877
|
+
deps.requestShutdown("other", "Reset settings restart");
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// src/ui/hooks/model-change-side-effect.ts
|
|
1881
|
+
import {
|
|
1882
|
+
createSystemMessage as createSystemMessage4,
|
|
1883
|
+
getModelName,
|
|
1884
|
+
messageToHistoryEntry as messageToHistoryEntry4
|
|
1885
|
+
} from "@robota-sdk/agent-core";
|
|
1886
|
+
function formatModelChangeConfirmationMessage(modelId) {
|
|
1887
|
+
return `Change model to ${getModelName(modelId)}? This will exit the current session so the next session uses it.`;
|
|
1888
|
+
}
|
|
1889
|
+
function formatModelChangeExitMessage(modelId) {
|
|
1890
|
+
return `Model changed to ${getModelName(modelId)}. Exiting so the next session uses it.`;
|
|
1891
|
+
}
|
|
1892
|
+
function applyConfirmedModelChange(deps) {
|
|
1893
|
+
const applyModelChange = deps.applyModelChange ?? applyActiveModelChange;
|
|
1894
|
+
const options = deps.providerOverride !== void 0 ? { providerOverride: deps.providerOverride } : void 0;
|
|
1895
|
+
try {
|
|
1896
|
+
applyModelChange(deps.cwd, deps.modelId, options);
|
|
1897
|
+
deps.addEntry(
|
|
1898
|
+
messageToHistoryEntry4(createSystemMessage4(formatModelChangeExitMessage(deps.modelId)))
|
|
1899
|
+
);
|
|
1900
|
+
deps.requestShutdown("other", "Model change applied");
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
deps.addEntry(
|
|
1903
|
+
messageToHistoryEntry4(
|
|
1904
|
+
createSystemMessage4(`Failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
1905
|
+
)
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function addModelChangeCancelledMessage(addEntry) {
|
|
1910
|
+
addEntry(messageToHistoryEntry4(createSystemMessage4("Model change cancelled.")));
|
|
1911
|
+
}
|
|
1912
|
+
|
|
2049
1913
|
// src/ui/hooks/useSideEffects.ts
|
|
2050
1914
|
var EXIT_DELAY_MS = 500;
|
|
2051
1915
|
function useSideEffects({
|
|
2052
1916
|
cwd,
|
|
1917
|
+
providerOverride,
|
|
2053
1918
|
interactiveSession,
|
|
2054
1919
|
addEntry,
|
|
2055
1920
|
baseHandleSubmit,
|
|
2056
1921
|
setSessionName,
|
|
2057
|
-
setStatusLineSettings
|
|
2058
|
-
providerDefinitions
|
|
1922
|
+
setStatusLineSettings
|
|
2059
1923
|
}) {
|
|
2060
1924
|
const { exit } = useApp();
|
|
2061
1925
|
const [pendingModelId, setPendingModelId] = useState2(null);
|
|
2062
1926
|
const pendingModelChangeRef = useRef2(null);
|
|
2063
|
-
const [pendingProviderProfile, setPendingProviderProfile] = useState2(null);
|
|
2064
|
-
const pendingProviderProfileRef = useRef2(null);
|
|
2065
1927
|
const [pendingInteractionPrompt, setPendingInteractionPrompt] = useState2(null);
|
|
2066
|
-
const
|
|
1928
|
+
const commandInteractionRef = useRef2(null);
|
|
2067
1929
|
const [showPluginTUI, setShowPluginTUI] = useState2(false);
|
|
2068
1930
|
const [showSessionPicker, setShowSessionPicker] = useState2(false);
|
|
2069
1931
|
const requestShutdown = useCallback3(
|
|
2070
1932
|
(reason, message) => {
|
|
2071
|
-
addEntry(
|
|
1933
|
+
addEntry(messageToHistoryEntry5(createSystemMessage5("Shutting down...")));
|
|
2072
1934
|
setTimeout(() => {
|
|
2073
1935
|
void interactiveSession.shutdown({ reason, message }).finally(() => exit());
|
|
2074
1936
|
}, EXIT_DELAY_MS);
|
|
2075
1937
|
},
|
|
2076
1938
|
[interactiveSession, addEntry, exit]
|
|
2077
1939
|
);
|
|
1940
|
+
const applyEffects = useCallback3(
|
|
1941
|
+
(effects, sideEffects) => applyCommandEffects(effects, sideEffects, {
|
|
1942
|
+
addEntry,
|
|
1943
|
+
requestShutdown,
|
|
1944
|
+
requestModelChange: (modelId) => {
|
|
1945
|
+
pendingModelChangeRef.current = modelId;
|
|
1946
|
+
setPendingModelId(modelId);
|
|
1947
|
+
},
|
|
1948
|
+
openPluginTUI: () => setShowPluginTUI(true),
|
|
1949
|
+
openSessionPicker: () => setShowSessionPicker(true),
|
|
1950
|
+
renameSession: (name) => {
|
|
1951
|
+
interactiveSession.setName(name);
|
|
1952
|
+
setSessionName(name);
|
|
1953
|
+
},
|
|
1954
|
+
applyStatusLinePatch: () => applyPendingStatusLinePatch(sideEffects, setStatusLineSettings)
|
|
1955
|
+
}),
|
|
1956
|
+
[addEntry, interactiveSession, requestShutdown, setSessionName, setStatusLineSettings]
|
|
1957
|
+
);
|
|
1958
|
+
const applyCommandResult = useCallback3(
|
|
1959
|
+
(result) => {
|
|
1960
|
+
if (result.message.length > 0) {
|
|
1961
|
+
addEntry(messageToHistoryEntry5(createSystemMessage5(result.message)));
|
|
1962
|
+
}
|
|
1963
|
+
if (result.interaction !== void 0) {
|
|
1964
|
+
commandInteractionRef.current = result.interaction;
|
|
1965
|
+
setPendingInteractionPrompt(result.interaction.prompt);
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
commandInteractionRef.current = null;
|
|
1969
|
+
setPendingInteractionPrompt(null);
|
|
1970
|
+
if (result.effects !== void 0 && result.effects.length > 0) {
|
|
1971
|
+
applyEffects(result.effects, interactiveSession);
|
|
1972
|
+
}
|
|
1973
|
+
},
|
|
1974
|
+
[addEntry, applyEffects, interactiveSession]
|
|
1975
|
+
);
|
|
1976
|
+
const applyQueuedCommandState = useCallback3(
|
|
1977
|
+
(sideEffects) => {
|
|
1978
|
+
if (sideEffects._pendingCommandInteraction !== void 0) {
|
|
1979
|
+
const interaction = sideEffects._pendingCommandInteraction;
|
|
1980
|
+
delete sideEffects._pendingCommandInteraction;
|
|
1981
|
+
commandInteractionRef.current = interaction;
|
|
1982
|
+
setPendingInteractionPrompt(interaction.prompt);
|
|
1983
|
+
return true;
|
|
1984
|
+
}
|
|
1985
|
+
if (sideEffects._pendingCommandEffects !== void 0) {
|
|
1986
|
+
const effects = sideEffects._pendingCommandEffects;
|
|
1987
|
+
delete sideEffects._pendingCommandEffects;
|
|
1988
|
+
return applyEffects(effects, sideEffects);
|
|
1989
|
+
}
|
|
1990
|
+
return false;
|
|
1991
|
+
},
|
|
1992
|
+
[applyEffects]
|
|
1993
|
+
);
|
|
2078
1994
|
const handleSubmit = useCallback3(
|
|
2079
1995
|
async (input) => {
|
|
2080
1996
|
await baseHandleSubmit(input);
|
|
2081
1997
|
const sideEffects = interactiveSession;
|
|
1998
|
+
if (applyQueuedCommandState(sideEffects)) return;
|
|
2082
1999
|
if (sideEffects._pendingModelId) {
|
|
2083
2000
|
const modelId = sideEffects._pendingModelId;
|
|
2084
2001
|
delete sideEffects._pendingModelId;
|
|
@@ -2086,69 +2003,25 @@ function useSideEffects({
|
|
|
2086
2003
|
setPendingModelId(modelId);
|
|
2087
2004
|
return;
|
|
2088
2005
|
}
|
|
2089
|
-
if (sideEffects._pendingLanguage) {
|
|
2090
|
-
const lang = sideEffects._pendingLanguage;
|
|
2091
|
-
delete sideEffects._pendingLanguage;
|
|
2092
|
-
const settingsPath = getUserSettingsPath();
|
|
2093
|
-
const settings = readSettings(settingsPath);
|
|
2094
|
-
settings.language = lang;
|
|
2095
|
-
writeSettings(settingsPath, settings);
|
|
2096
|
-
addEntry(
|
|
2097
|
-
messageToHistoryEntry3(createSystemMessage3(`Language set to "${lang}". Restarting...`))
|
|
2098
|
-
);
|
|
2099
|
-
requestShutdown("other", "Language change restart");
|
|
2100
|
-
return;
|
|
2101
|
-
}
|
|
2102
|
-
if (sideEffects._pendingProviderProfile) {
|
|
2103
|
-
const profile = sideEffects._pendingProviderProfile;
|
|
2104
|
-
delete sideEffects._pendingProviderProfile;
|
|
2105
|
-
pendingProviderProfileRef.current = profile;
|
|
2106
|
-
setPendingProviderProfile(profile);
|
|
2107
|
-
return;
|
|
2108
|
-
}
|
|
2109
|
-
if (sideEffects._pendingProviderSetup !== void 0) {
|
|
2110
|
-
const setup = sideEffects._pendingProviderSetup;
|
|
2111
|
-
delete sideEffects._pendingProviderSetup;
|
|
2112
|
-
const result = startProviderSetupInteraction(providerDefinitions, setup.type);
|
|
2113
|
-
if (result.status === "prompt") {
|
|
2114
|
-
providerSetupInteractionRef.current = result.state;
|
|
2115
|
-
setPendingInteractionPrompt(result.prompt);
|
|
2116
|
-
}
|
|
2117
|
-
return;
|
|
2118
|
-
}
|
|
2119
2006
|
if (sideEffects._resetRequested) {
|
|
2120
2007
|
delete sideEffects._resetRequested;
|
|
2121
|
-
|
|
2122
|
-
if (deleteSettings(settingsPath)) {
|
|
2123
|
-
addEntry(
|
|
2124
|
-
messageToHistoryEntry3(createSystemMessage3(`Deleted ${settingsPath}. Exiting...`))
|
|
2125
|
-
);
|
|
2126
|
-
} else {
|
|
2127
|
-
addEntry(messageToHistoryEntry3(createSystemMessage3("No user settings found.")));
|
|
2128
|
-
}
|
|
2129
|
-
requestShutdown("other", "Reset settings restart");
|
|
2008
|
+
applyEffects([{ type: "settings-reset-requested" }], sideEffects);
|
|
2130
2009
|
return;
|
|
2131
2010
|
}
|
|
2132
2011
|
if (sideEffects._exitRequested) {
|
|
2133
2012
|
delete sideEffects._exitRequested;
|
|
2134
|
-
|
|
2135
|
-
return;
|
|
2136
|
-
}
|
|
2137
|
-
if (sideEffects._triggerPluginTUI) {
|
|
2138
|
-
delete sideEffects._triggerPluginTUI;
|
|
2139
|
-
setShowPluginTUI(true);
|
|
2013
|
+
applyEffects([{ type: "session-exit-requested" }], sideEffects);
|
|
2140
2014
|
return;
|
|
2141
2015
|
}
|
|
2142
2016
|
if (sideEffects._triggerResumePicker) {
|
|
2143
2017
|
delete sideEffects._triggerResumePicker;
|
|
2144
|
-
|
|
2018
|
+
applyEffects([{ type: "session-picker-requested" }], sideEffects);
|
|
2145
2019
|
return;
|
|
2146
2020
|
}
|
|
2147
2021
|
if (sideEffects._sessionName) {
|
|
2148
2022
|
const name = sideEffects._sessionName;
|
|
2149
2023
|
delete sideEffects._sessionName;
|
|
2150
|
-
|
|
2151
|
-
setSessionName(name);
|
|
2024
|
+
applyEffects([{ type: "session-renamed", name }], sideEffects);
|
|
2152
2025
|
return;
|
|
2153
2026
|
}
|
|
2154
2027
|
if (applyPendingStatusLinePatch(sideEffects, setStatusLineSettings)) return;
|
|
@@ -2156,11 +2029,9 @@ function useSideEffects({
|
|
|
2156
2029
|
[
|
|
2157
2030
|
interactiveSession,
|
|
2158
2031
|
baseHandleSubmit,
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
setStatusLineSettings,
|
|
2163
|
-
providerDefinitions
|
|
2032
|
+
applyQueuedCommandState,
|
|
2033
|
+
applyEffects,
|
|
2034
|
+
setStatusLineSettings
|
|
2164
2035
|
]
|
|
2165
2036
|
);
|
|
2166
2037
|
const handleModelConfirm = useCallback3(
|
|
@@ -2169,117 +2040,60 @@ function useSideEffects({
|
|
|
2169
2040
|
setPendingModelId(null);
|
|
2170
2041
|
pendingModelChangeRef.current = null;
|
|
2171
2042
|
if (index === 0 && modelId) {
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
);
|
|
2180
|
-
requestShutdown("other", "Model change restart");
|
|
2181
|
-
} catch (err) {
|
|
2182
|
-
addEntry(
|
|
2183
|
-
messageToHistoryEntry3(
|
|
2184
|
-
createSystemMessage3(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2185
|
-
)
|
|
2186
|
-
);
|
|
2187
|
-
}
|
|
2188
|
-
} else {
|
|
2189
|
-
addEntry(messageToHistoryEntry3(createSystemMessage3("Model change cancelled.")));
|
|
2190
|
-
}
|
|
2191
|
-
},
|
|
2192
|
-
[addEntry, requestShutdown]
|
|
2193
|
-
);
|
|
2194
|
-
const handleProviderConfirm = useCallback3(
|
|
2195
|
-
(index) => {
|
|
2196
|
-
const profile = pendingProviderProfileRef.current;
|
|
2197
|
-
setPendingProviderProfile(null);
|
|
2198
|
-
pendingProviderProfileRef.current = null;
|
|
2199
|
-
if (index === 0 && profile) {
|
|
2200
|
-
try {
|
|
2201
|
-
const settingsPath = getUserSettingsPath();
|
|
2202
|
-
applyProviderSwitch(settingsPath, profile, {
|
|
2203
|
-
knownProviders: readMergedProviderSettings(cwd).providers
|
|
2204
|
-
});
|
|
2205
|
-
addEntry(
|
|
2206
|
-
messageToHistoryEntry3(
|
|
2207
|
-
createSystemMessage3(`Provider changed to ${profile}. Restarting...`)
|
|
2208
|
-
)
|
|
2209
|
-
);
|
|
2210
|
-
requestShutdown("other", "Provider change restart");
|
|
2211
|
-
} catch (err) {
|
|
2212
|
-
addEntry(
|
|
2213
|
-
messageToHistoryEntry3(
|
|
2214
|
-
createSystemMessage3(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2215
|
-
)
|
|
2216
|
-
);
|
|
2217
|
-
}
|
|
2043
|
+
applyConfirmedModelChange({
|
|
2044
|
+
cwd,
|
|
2045
|
+
modelId,
|
|
2046
|
+
providerOverride,
|
|
2047
|
+
addEntry,
|
|
2048
|
+
requestShutdown
|
|
2049
|
+
});
|
|
2218
2050
|
} else {
|
|
2219
|
-
addEntry
|
|
2051
|
+
addModelChangeCancelledMessage(addEntry);
|
|
2220
2052
|
}
|
|
2221
2053
|
},
|
|
2222
|
-
[cwd, addEntry, requestShutdown]
|
|
2223
|
-
);
|
|
2224
|
-
const completeProviderSetup = useCallback3(
|
|
2225
|
-
(input) => {
|
|
2226
|
-
providerSetupInteractionRef.current = null;
|
|
2227
|
-
setPendingInteractionPrompt(null);
|
|
2228
|
-
try {
|
|
2229
|
-
const settingsPath = getUserSettingsPath();
|
|
2230
|
-
applyProviderConfiguration(settingsPath, input, { providerDefinitions });
|
|
2231
|
-
addEntry(
|
|
2232
|
-
messageToHistoryEntry3(
|
|
2233
|
-
createSystemMessage3(`Provider ${input.profile} configured. Restarting...`)
|
|
2234
|
-
)
|
|
2235
|
-
);
|
|
2236
|
-
requestShutdown("other", "Provider setup restart");
|
|
2237
|
-
} catch (err) {
|
|
2238
|
-
addEntry(
|
|
2239
|
-
messageToHistoryEntry3(
|
|
2240
|
-
createSystemMessage3(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2241
|
-
)
|
|
2242
|
-
);
|
|
2243
|
-
}
|
|
2244
|
-
},
|
|
2245
|
-
[addEntry, requestShutdown, providerDefinitions]
|
|
2054
|
+
[cwd, providerOverride, addEntry, requestShutdown]
|
|
2246
2055
|
);
|
|
2247
2056
|
const handleInteractionSubmit = useCallback3(
|
|
2248
|
-
(value) => {
|
|
2249
|
-
const
|
|
2250
|
-
if (
|
|
2057
|
+
async (value) => {
|
|
2058
|
+
const interaction = commandInteractionRef.current;
|
|
2059
|
+
if (interaction === null) {
|
|
2251
2060
|
setPendingInteractionPrompt(null);
|
|
2252
2061
|
return;
|
|
2253
2062
|
}
|
|
2254
2063
|
try {
|
|
2255
|
-
const result =
|
|
2256
|
-
|
|
2257
|
-
completeProviderSetup(result.input);
|
|
2258
|
-
return;
|
|
2259
|
-
}
|
|
2260
|
-
providerSetupInteractionRef.current = result.state;
|
|
2261
|
-
setPendingInteractionPrompt(result.prompt);
|
|
2064
|
+
const result = await interaction.submit(value);
|
|
2065
|
+
applyCommandResult(result);
|
|
2262
2066
|
} catch (err) {
|
|
2263
|
-
|
|
2067
|
+
commandInteractionRef.current = null;
|
|
2264
2068
|
setPendingInteractionPrompt(null);
|
|
2265
2069
|
addEntry(
|
|
2266
|
-
|
|
2267
|
-
|
|
2070
|
+
messageToHistoryEntry5(
|
|
2071
|
+
createSystemMessage5(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2268
2072
|
)
|
|
2269
2073
|
);
|
|
2270
2074
|
}
|
|
2271
2075
|
},
|
|
2272
|
-
[addEntry,
|
|
2076
|
+
[addEntry, applyCommandResult]
|
|
2273
2077
|
);
|
|
2274
2078
|
const handleInteractionCancel = useCallback3(() => {
|
|
2275
|
-
|
|
2079
|
+
const interaction = commandInteractionRef.current;
|
|
2080
|
+
commandInteractionRef.current = null;
|
|
2276
2081
|
setPendingInteractionPrompt(null);
|
|
2277
|
-
|
|
2278
|
-
|
|
2082
|
+
if (interaction?.cancel === void 0) {
|
|
2083
|
+
addEntry(messageToHistoryEntry5(createSystemMessage5("Command interaction cancelled.")));
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
Promise.resolve(interaction.cancel()).then((result) => applyCommandResult(result)).catch((err) => {
|
|
2087
|
+
addEntry(
|
|
2088
|
+
messageToHistoryEntry5(
|
|
2089
|
+
createSystemMessage5(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2090
|
+
)
|
|
2091
|
+
);
|
|
2092
|
+
});
|
|
2093
|
+
}, [addEntry, applyCommandResult]);
|
|
2279
2094
|
return {
|
|
2280
2095
|
handleSubmit,
|
|
2281
2096
|
pendingModelId,
|
|
2282
|
-
pendingProviderProfile,
|
|
2283
2097
|
pendingInteractionPrompt,
|
|
2284
2098
|
showPluginTUI,
|
|
2285
2099
|
showSessionPicker,
|
|
@@ -2287,7 +2101,6 @@ function useSideEffects({
|
|
|
2287
2101
|
setShowPluginTUI,
|
|
2288
2102
|
setShowSessionPicker,
|
|
2289
2103
|
handleModelConfirm,
|
|
2290
|
-
handleProviderConfirm,
|
|
2291
2104
|
handleInteractionSubmit,
|
|
2292
2105
|
handleInteractionCancel
|
|
2293
2106
|
};
|
|
@@ -2960,6 +2773,21 @@ function StatusLeft({
|
|
|
2960
2773
|
)
|
|
2961
2774
|
] });
|
|
2962
2775
|
}
|
|
2776
|
+
function StatusRight({
|
|
2777
|
+
isThinking,
|
|
2778
|
+
messageCount
|
|
2779
|
+
}) {
|
|
2780
|
+
return /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
2781
|
+
isThinking && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2782
|
+
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "thinking..." }),
|
|
2783
|
+
" "
|
|
2784
|
+
] }),
|
|
2785
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
2786
|
+
"msgs: ",
|
|
2787
|
+
messageCount
|
|
2788
|
+
] })
|
|
2789
|
+
] });
|
|
2790
|
+
}
|
|
2963
2791
|
function StatusBar({
|
|
2964
2792
|
permissionMode,
|
|
2965
2793
|
modelName,
|
|
@@ -3002,10 +2830,7 @@ function StatusBar({
|
|
|
3002
2830
|
showGitBranch
|
|
3003
2831
|
}
|
|
3004
2832
|
),
|
|
3005
|
-
/* @__PURE__ */ jsx5(
|
|
3006
|
-
"msgs: ",
|
|
3007
|
-
messageCount
|
|
3008
|
-
] }) })
|
|
2833
|
+
/* @__PURE__ */ jsx5(StatusRight, { isThinking, messageCount })
|
|
3009
2834
|
]
|
|
3010
2835
|
}
|
|
3011
2836
|
);
|
|
@@ -4264,32 +4089,43 @@ function getToolStyle(t) {
|
|
|
4264
4089
|
if (t.result === "denied") return { color: "yellowBright", icon: "\u2298", strikethrough: true };
|
|
4265
4090
|
return { color: "green", icon: "\u2713", strikethrough: false };
|
|
4266
4091
|
}
|
|
4267
|
-
function
|
|
4092
|
+
function renderThinkingFallback(isThinking) {
|
|
4093
|
+
if (!isThinking) return /* @__PURE__ */ jsx16(Fragment4, {});
|
|
4094
|
+
return /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { color: "yellow", children: "Thinking..." }) });
|
|
4095
|
+
}
|
|
4096
|
+
function renderTools(activeTools) {
|
|
4097
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
4098
|
+
/* @__PURE__ */ jsx16(Text15, { color: "white", bold: true, children: "Tools:" }),
|
|
4099
|
+
/* @__PURE__ */ jsx16(Text15, { children: " " }),
|
|
4100
|
+
activeTools.map((t, i) => {
|
|
4101
|
+
const { color, icon, strikethrough } = getToolStyle(t);
|
|
4102
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
4103
|
+
/* @__PURE__ */ jsxs13(Text15, { color, strikethrough, children: [
|
|
4104
|
+
" ",
|
|
4105
|
+
icon,
|
|
4106
|
+
" ",
|
|
4107
|
+
t.toolName,
|
|
4108
|
+
"(",
|
|
4109
|
+
t.firstArg,
|
|
4110
|
+
")"
|
|
4111
|
+
] }),
|
|
4112
|
+
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ jsx16(ToolDiffBlock, { file: t.diffFile, lines: t.diffLines })
|
|
4113
|
+
] }, `${t.toolName}-${i}`);
|
|
4114
|
+
})
|
|
4115
|
+
] });
|
|
4116
|
+
}
|
|
4117
|
+
function StreamingIndicator({
|
|
4118
|
+
text,
|
|
4119
|
+
activeTools,
|
|
4120
|
+
isThinking = false
|
|
4121
|
+
}) {
|
|
4268
4122
|
const hasTools = activeTools.length > 0;
|
|
4269
4123
|
const hasText = text.length > 0;
|
|
4270
4124
|
if (!hasTools && !hasText) {
|
|
4271
|
-
return
|
|
4125
|
+
return renderThinkingFallback(isThinking);
|
|
4272
4126
|
}
|
|
4273
4127
|
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
4274
|
-
hasTools &&
|
|
4275
|
-
/* @__PURE__ */ jsx16(Text15, { color: "white", bold: true, children: "Tools:" }),
|
|
4276
|
-
/* @__PURE__ */ jsx16(Text15, { children: " " }),
|
|
4277
|
-
activeTools.map((t, i) => {
|
|
4278
|
-
const { color, icon, strikethrough } = getToolStyle(t);
|
|
4279
|
-
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
4280
|
-
/* @__PURE__ */ jsxs13(Text15, { color, strikethrough, children: [
|
|
4281
|
-
" ",
|
|
4282
|
-
icon,
|
|
4283
|
-
" ",
|
|
4284
|
-
t.toolName,
|
|
4285
|
-
"(",
|
|
4286
|
-
t.firstArg,
|
|
4287
|
-
")"
|
|
4288
|
-
] }),
|
|
4289
|
-
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ jsx16(ToolDiffBlock, { file: t.diffFile, lines: t.diffLines })
|
|
4290
|
-
] }, `${t.toolName}-${i}`);
|
|
4291
|
-
})
|
|
4292
|
-
] }),
|
|
4128
|
+
hasTools && renderTools(activeTools),
|
|
4293
4129
|
hasText && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
4294
4130
|
/* @__PURE__ */ jsx16(Text15, { color: "cyan", bold: true, children: "Robota:" }),
|
|
4295
4131
|
/* @__PURE__ */ jsx16(Text15, { children: " " }),
|
|
@@ -4812,9 +4648,14 @@ function getTaskSegments(task, now) {
|
|
|
4812
4648
|
if (task.signalCode) {
|
|
4813
4649
|
segments.push(`signal ${task.signalCode}`);
|
|
4814
4650
|
}
|
|
4651
|
+
if (task.worktreePath || task.branchName) {
|
|
4652
|
+
segments.push("worktree");
|
|
4653
|
+
}
|
|
4815
4654
|
return segments;
|
|
4816
4655
|
}
|
|
4817
4656
|
function getTaskPreview(task) {
|
|
4657
|
+
if (task.worktreeNextAction) return task.worktreeNextAction;
|
|
4658
|
+
if (task.worktreePath) return task.worktreePath;
|
|
4818
4659
|
const preview = task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
|
|
4819
4660
|
return preview || void 0;
|
|
4820
4661
|
}
|
|
@@ -5142,7 +4983,6 @@ function App(props) {
|
|
|
5142
4983
|
}
|
|
5143
4984
|
function AppInner(props) {
|
|
5144
4985
|
const cwd = props.cwd;
|
|
5145
|
-
const providerDefinitions = props.providerDefinitions ?? [];
|
|
5146
4986
|
const {
|
|
5147
4987
|
interactiveSession,
|
|
5148
4988
|
registry,
|
|
@@ -5173,9 +5013,10 @@ function AppInner(props) {
|
|
|
5173
5013
|
backgroundTaskRunners: props.backgroundTaskRunners,
|
|
5174
5014
|
subagentRunnerFactory: props.subagentRunnerFactory,
|
|
5175
5015
|
commandModules: props.commandModules,
|
|
5176
|
-
|
|
5016
|
+
commandHostAdapters: props.commandHostAdapters
|
|
5177
5017
|
});
|
|
5178
|
-
const
|
|
5018
|
+
const fallbackPluginCallbacks = usePluginCallbacks(cwd);
|
|
5019
|
+
const pluginCallbacks = props.commandHostAdapters?.plugin ?? fallbackPluginCallbacks;
|
|
5179
5020
|
const { exit } = useApp2();
|
|
5180
5021
|
const [sessionName, setSessionName] = useState14(props.sessionName);
|
|
5181
5022
|
const [updateNotice, setUpdateNotice] = useState14();
|
|
@@ -5186,24 +5027,22 @@ function AppInner(props) {
|
|
|
5186
5027
|
const {
|
|
5187
5028
|
handleSubmit,
|
|
5188
5029
|
pendingModelId,
|
|
5189
|
-
pendingProviderProfile,
|
|
5190
5030
|
pendingInteractionPrompt,
|
|
5191
5031
|
showPluginTUI,
|
|
5192
5032
|
showSessionPicker,
|
|
5193
5033
|
setShowPluginTUI,
|
|
5194
5034
|
setShowSessionPicker,
|
|
5195
5035
|
handleModelConfirm,
|
|
5196
|
-
handleProviderConfirm,
|
|
5197
5036
|
handleInteractionSubmit,
|
|
5198
5037
|
handleInteractionCancel
|
|
5199
5038
|
} = useSideEffects({
|
|
5200
5039
|
cwd,
|
|
5040
|
+
providerOverride: props.providerOverride,
|
|
5201
5041
|
interactiveSession,
|
|
5202
5042
|
addEntry,
|
|
5203
5043
|
baseHandleSubmit,
|
|
5204
5044
|
setSessionName,
|
|
5205
|
-
setStatusLineSettings
|
|
5206
|
-
providerDefinitions
|
|
5045
|
+
setStatusLineSettings
|
|
5207
5046
|
});
|
|
5208
5047
|
useEffect4(() => {
|
|
5209
5048
|
const name = interactiveSession?.getName?.();
|
|
@@ -5272,24 +5111,24 @@ function AppInner(props) {
|
|
|
5272
5111
|
/* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
5273
5112
|
/* @__PURE__ */ jsx22(MessageList, { history }),
|
|
5274
5113
|
isShuttingDown && /* @__PURE__ */ jsx22(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text20, { color: "yellow", children: "Shutting down..." }) }),
|
|
5275
|
-
(isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx22(Box18, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx22(
|
|
5114
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx22(Box18, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx22(
|
|
5115
|
+
StreamingIndicator,
|
|
5116
|
+
{
|
|
5117
|
+
text: streamingText,
|
|
5118
|
+
activeTools,
|
|
5119
|
+
isThinking
|
|
5120
|
+
}
|
|
5121
|
+
) }),
|
|
5276
5122
|
/* @__PURE__ */ jsx22(BackgroundTaskPanel, { tasks: backgroundTasks })
|
|
5277
5123
|
] }),
|
|
5278
5124
|
permissionRequest && /* @__PURE__ */ jsx22(PermissionPrompt, { request: permissionRequest }),
|
|
5279
5125
|
pendingModelId && /* @__PURE__ */ jsx22(
|
|
5280
5126
|
ConfirmPrompt,
|
|
5281
5127
|
{
|
|
5282
|
-
message:
|
|
5128
|
+
message: formatModelChangeConfirmationMessage(pendingModelId),
|
|
5283
5129
|
onSelect: handleModelConfirm
|
|
5284
5130
|
}
|
|
5285
5131
|
),
|
|
5286
|
-
pendingProviderProfile && /* @__PURE__ */ jsx22(
|
|
5287
|
-
ConfirmPrompt,
|
|
5288
|
-
{
|
|
5289
|
-
message: `Change provider to ${pendingProviderProfile}? This will restart the session.`,
|
|
5290
|
-
onSelect: handleProviderConfirm
|
|
5291
|
-
}
|
|
5292
|
-
),
|
|
5293
5132
|
pendingInteractionPrompt && /* @__PURE__ */ jsx22(
|
|
5294
5133
|
InteractivePrompt,
|
|
5295
5134
|
{
|
|
@@ -5303,7 +5142,7 @@ function AppInner(props) {
|
|
|
5303
5142
|
{
|
|
5304
5143
|
callbacks: pluginCallbacks,
|
|
5305
5144
|
onClose: () => setShowPluginTUI(false),
|
|
5306
|
-
addMessage: (msg) => addEntry(
|
|
5145
|
+
addMessage: (msg) => addEntry(messageToHistoryEntry6(createSystemMessage6(msg.content)))
|
|
5307
5146
|
}
|
|
5308
5147
|
),
|
|
5309
5148
|
showSessionPicker && /* @__PURE__ */ jsx22(
|
|
@@ -5317,7 +5156,7 @@ function AppInner(props) {
|
|
|
5317
5156
|
},
|
|
5318
5157
|
onCancel: () => {
|
|
5319
5158
|
setShowSessionPicker(false);
|
|
5320
|
-
addEntry(
|
|
5159
|
+
addEntry(messageToHistoryEntry6(createSystemMessage6("Session resume cancelled.")));
|
|
5321
5160
|
}
|
|
5322
5161
|
}
|
|
5323
5162
|
),
|
|
@@ -5382,93 +5221,6 @@ function renderApp(options) {
|
|
|
5382
5221
|
});
|
|
5383
5222
|
}
|
|
5384
5223
|
|
|
5385
|
-
// src/commands/statusline-command-module.ts
|
|
5386
|
-
var USAGE = [
|
|
5387
|
-
"Usage: /statusline on | off | reset | git on | git off",
|
|
5388
|
-
"Fields: model, context, permission mode, message count, session name, thinking state, git branch."
|
|
5389
|
-
].join("\n");
|
|
5390
|
-
function createStatusLineEntry() {
|
|
5391
|
-
return {
|
|
5392
|
-
name: "statusline",
|
|
5393
|
-
description: "Configure TUI status-line visibility and fields such as model, context, tokens, session, and git branch.",
|
|
5394
|
-
source: "cli",
|
|
5395
|
-
modelInvocable: false,
|
|
5396
|
-
argumentHint: "on | off | reset | git on | git off",
|
|
5397
|
-
subcommands: [
|
|
5398
|
-
{ name: "on", description: "Show the status line", source: "cli" },
|
|
5399
|
-
{ name: "off", description: "Hide the status line", source: "cli" },
|
|
5400
|
-
{ name: "reset", description: "Restore default status-line fields", source: "cli" },
|
|
5401
|
-
{ name: "git", description: "Show or hide git branch field", source: "cli" }
|
|
5402
|
-
]
|
|
5403
|
-
};
|
|
5404
|
-
}
|
|
5405
|
-
function parseStatusLineArgs(args) {
|
|
5406
|
-
const parts = args.trim().toLowerCase().split(/\s+/).filter((part) => part.length > 0);
|
|
5407
|
-
const [first, second] = parts;
|
|
5408
|
-
if (first === "on" && second === void 0) {
|
|
5409
|
-
return { success: true, message: "Status line enabled.", patch: { enabled: true } };
|
|
5410
|
-
}
|
|
5411
|
-
if (first === "off" && second === void 0) {
|
|
5412
|
-
return { success: true, message: "Status line disabled.", patch: { enabled: false } };
|
|
5413
|
-
}
|
|
5414
|
-
if (first === "reset" && second === void 0) {
|
|
5415
|
-
return {
|
|
5416
|
-
success: true,
|
|
5417
|
-
message: "Status line settings reset.",
|
|
5418
|
-
patch: { enabled: true, gitBranch: true }
|
|
5419
|
-
};
|
|
5420
|
-
}
|
|
5421
|
-
if (first === "git" && second === "on" && parts.length === 2) {
|
|
5422
|
-
return {
|
|
5423
|
-
success: true,
|
|
5424
|
-
message: "Status line git branch shown.",
|
|
5425
|
-
patch: { gitBranch: true }
|
|
5426
|
-
};
|
|
5427
|
-
}
|
|
5428
|
-
if (first === "git" && second === "off" && parts.length === 2) {
|
|
5429
|
-
return {
|
|
5430
|
-
success: true,
|
|
5431
|
-
message: "Status line git branch hidden.",
|
|
5432
|
-
patch: { gitBranch: false }
|
|
5433
|
-
};
|
|
5434
|
-
}
|
|
5435
|
-
return { success: false, message: USAGE };
|
|
5436
|
-
}
|
|
5437
|
-
function createStatusLineSystemCommand() {
|
|
5438
|
-
const entry = createStatusLineEntry();
|
|
5439
|
-
return {
|
|
5440
|
-
name: entry.name,
|
|
5441
|
-
description: entry.description,
|
|
5442
|
-
modelInvocable: false,
|
|
5443
|
-
userInvocable: true,
|
|
5444
|
-
argumentHint: entry.argumentHint,
|
|
5445
|
-
execute: (_session, args) => {
|
|
5446
|
-
const action = parseStatusLineArgs(args);
|
|
5447
|
-
if (!action.success) {
|
|
5448
|
-
return { success: false, message: action.message };
|
|
5449
|
-
}
|
|
5450
|
-
return {
|
|
5451
|
-
success: true,
|
|
5452
|
-
message: action.message,
|
|
5453
|
-
data: { statuslinePatch: action.patch }
|
|
5454
|
-
};
|
|
5455
|
-
}
|
|
5456
|
-
};
|
|
5457
|
-
}
|
|
5458
|
-
var StatusLineCommandSource = class {
|
|
5459
|
-
name = "cli-statusline";
|
|
5460
|
-
getCommands() {
|
|
5461
|
-
return [createStatusLineEntry()];
|
|
5462
|
-
}
|
|
5463
|
-
};
|
|
5464
|
-
function createStatusLineCommandModule() {
|
|
5465
|
-
return {
|
|
5466
|
-
name: "cli-statusline",
|
|
5467
|
-
commandSources: [new StatusLineCommandSource()],
|
|
5468
|
-
systemCommands: [createStatusLineSystemCommand()]
|
|
5469
|
-
};
|
|
5470
|
-
}
|
|
5471
|
-
|
|
5472
5224
|
// src/cli.ts
|
|
5473
5225
|
function readVersion() {
|
|
5474
5226
|
try {
|
|
@@ -5559,9 +5311,39 @@ async function startCli(options = {}) {
|
|
|
5559
5311
|
return;
|
|
5560
5312
|
}
|
|
5561
5313
|
const cwd = process.cwd();
|
|
5314
|
+
const commandHostAdapters = {
|
|
5315
|
+
settings: {
|
|
5316
|
+
read: () => readSettings(getUserSettingsPath()),
|
|
5317
|
+
write: (settings) => writeSettings(getUserSettingsPath(), settings)
|
|
5318
|
+
},
|
|
5319
|
+
plugin: createCliPluginCommandAdapter(cwd)
|
|
5320
|
+
};
|
|
5562
5321
|
const providerDefinitions = options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
5563
5322
|
const commandModules = [
|
|
5323
|
+
createHelpCommandModule(),
|
|
5324
|
+
createAgentCommandModule(),
|
|
5325
|
+
createModelCommandModule(),
|
|
5326
|
+
createModeCommandModule(),
|
|
5327
|
+
createPermissionsCommandModule(),
|
|
5328
|
+
createLanguageCommandModule(),
|
|
5329
|
+
createBackgroundCommandModule(),
|
|
5330
|
+
createMemoryCommandModule(),
|
|
5331
|
+
createCompactCommandModule(),
|
|
5332
|
+
createContextCommandModule(),
|
|
5333
|
+
createExitCommandModule(),
|
|
5334
|
+
createSessionCommandModule(),
|
|
5335
|
+
createResetCommandModule(),
|
|
5336
|
+
createRewindCommandModule(),
|
|
5564
5337
|
createStatusLineCommandModule(),
|
|
5338
|
+
createPluginCommandModule(),
|
|
5339
|
+
createProviderCommandModule({
|
|
5340
|
+
providerDefinitions,
|
|
5341
|
+
settings: {
|
|
5342
|
+
readMergedSettings: () => readMergedProviderSettings(cwd),
|
|
5343
|
+
readTargetSettings: () => readSettings(getUserSettingsPath()),
|
|
5344
|
+
writeTargetSettings: (settings) => writeSettings(getUserSettingsPath(), settings)
|
|
5345
|
+
}
|
|
5346
|
+
}),
|
|
5565
5347
|
...options.commandModules ?? []
|
|
5566
5348
|
];
|
|
5567
5349
|
const startupUpdateNoticePromise = shouldRunStartupCliUpdateCheck(args) ? getStartupCliUpdateNotice({ currentVersion: version }) : void 0;
|
|
@@ -5644,7 +5426,8 @@ ${args.jsonSchema}`
|
|
|
5644
5426
|
appendSystemPrompt,
|
|
5645
5427
|
backgroundTaskRunners,
|
|
5646
5428
|
subagentRunnerFactory,
|
|
5647
|
-
commandModules
|
|
5429
|
+
commandModules,
|
|
5430
|
+
commandHostAdapters
|
|
5648
5431
|
});
|
|
5649
5432
|
const transport = createHeadlessTransport({
|
|
5650
5433
|
outputFormat: args.outputFormat ?? "text",
|
|
@@ -5658,6 +5441,7 @@ ${args.jsonSchema}`
|
|
|
5658
5441
|
renderApp({
|
|
5659
5442
|
cwd,
|
|
5660
5443
|
provider,
|
|
5444
|
+
providerOverride: args.provider,
|
|
5661
5445
|
modelId,
|
|
5662
5446
|
language: args.language,
|
|
5663
5447
|
permissionMode: args.permissionMode,
|
|
@@ -5670,7 +5454,7 @@ ${args.jsonSchema}`
|
|
|
5670
5454
|
backgroundTaskRunners,
|
|
5671
5455
|
subagentRunnerFactory,
|
|
5672
5456
|
commandModules,
|
|
5673
|
-
|
|
5457
|
+
commandHostAdapters,
|
|
5674
5458
|
startupUpdateNoticePromise
|
|
5675
5459
|
});
|
|
5676
5460
|
}
|