scalemax 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configure/hermes.js +18 -10
- package/dist/configure/models.js +1 -0
- package/dist/configure/paths.d.ts +7 -0
- package/dist/configure/paths.js +16 -2
- package/dist/configure/roo-code.js +28 -16
- package/dist/detect/roo-code.d.ts +19 -0
- package/dist/detect/roo-code.js +31 -8
- package/package.json +1 -1
package/dist/configure/hermes.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { parseDocument } from 'yaml';
|
|
4
4
|
import { atomicWrite, backupFile, captureFileSnapshots, restoreFileSnapshots, snapshotRollback } from '../utils/fs.js';
|
|
5
|
-
import { clientConfigPath } from './paths.js';
|
|
5
|
+
import { clientConfigPath, hermesHome } from './paths.js';
|
|
6
6
|
import { isScaleMaxApiUrl, isScaleMaxManagedText } from './common.js';
|
|
7
7
|
import { anthropicModelIds, anthropicOrigin, deepSeekChatModelIds, modelIdKey, modelSupportsTools, openAiBaseUrl, openAiResponsesModelIds, preferredAnthropicModel, preferredOpenAiResponsesModel } from './models.js';
|
|
8
8
|
const id = 'hermes';
|
|
@@ -27,7 +27,7 @@ function exactChatLabel(ids) {
|
|
|
27
27
|
function asRecord(value) {
|
|
28
28
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
29
29
|
}
|
|
30
|
-
function envPath(
|
|
30
|
+
function envPath(ctx) { return path.join(hermesHome(ctx.home, ctx.platform, ctx.env), '.env'); }
|
|
31
31
|
function managedEnv(text, apiKey) {
|
|
32
32
|
const kept = text.split(/\r?\n/).filter(line => line.trim() !== envMarker && !line.startsWith(`${envKey}=`));
|
|
33
33
|
while (kept.length && !kept[kept.length - 1])
|
|
@@ -73,8 +73,8 @@ async function removeLegacyArtifact(ctx) {
|
|
|
73
73
|
return true;
|
|
74
74
|
}
|
|
75
75
|
export async function configure(ctx) {
|
|
76
|
-
const file = clientConfigPath(id, ctx.home, ctx.platform);
|
|
77
|
-
const secrets = envPath(ctx
|
|
76
|
+
const file = clientConfigPath(id, ctx.home, ctx.platform, ctx.env);
|
|
77
|
+
const secrets = envPath(ctx);
|
|
78
78
|
const legacy = path.join(ctx.home, '.hermes', 'config', 'scalemax.json');
|
|
79
79
|
const snapshots = await captureFileSnapshots([file, secrets, legacy]);
|
|
80
80
|
await fs.ensureDir(path.dirname(file), 0o700);
|
|
@@ -127,7 +127,12 @@ export async function configure(ctx) {
|
|
|
127
127
|
doc.set('custom_providers', providers);
|
|
128
128
|
const currentModel = asRecord(data.model);
|
|
129
129
|
const currentProvider = String(currentModel.provider || '');
|
|
130
|
-
|
|
130
|
+
// "auto" is Hermes's own unchosen default: the native Windows installer
|
|
131
|
+
// writes provider "auto" with an OpenRouter base_url into a fresh
|
|
132
|
+
// config.yaml, which left ScaleMax listed but never active ("No inference
|
|
133
|
+
// provider is configured yet"). A provider the user picked is left alone.
|
|
134
|
+
const unchosen = !currentProvider || currentProvider === 'auto';
|
|
135
|
+
const activatesScaleMax = unchosen || currentProvider.startsWith('custom:scalemax');
|
|
131
136
|
if (activatesScaleMax) {
|
|
132
137
|
const keepClaude = currentProvider === `custom:${claudeProviderName}` || claudeIds.includes(String(currentModel.default || ''));
|
|
133
138
|
const keepDeepSeek = currentProvider === `custom:${deepSeekProviderName}`
|
|
@@ -142,7 +147,10 @@ export async function configure(ctx) {
|
|
|
142
147
|
const activeModel = activeName === codexProviderName
|
|
143
148
|
? preferredOpenAiResponsesModel(ctx)
|
|
144
149
|
: activeName === chatProviderName ? deepSeekIds[0] : preferredAnthropicModel(ctx);
|
|
145
|
-
|
|
150
|
+
// The custom provider carries its own base_url; a leftover model.base_url
|
|
151
|
+
// from the "auto" template would point Hermes back at OpenRouter.
|
|
152
|
+
const { base_url: _unchosenBaseUrl, ...keptModel } = currentModel;
|
|
153
|
+
doc.set('model', { ...(unchosen ? keptModel : currentModel), provider: `custom:${activeName}`, default: activeModel, max_tokens: 4096 });
|
|
146
154
|
}
|
|
147
155
|
const nextConfig = String(doc);
|
|
148
156
|
const nextEnv = managedEnv(originalEnv, ctx.apiKey);
|
|
@@ -184,8 +192,8 @@ export async function configure(ctx) {
|
|
|
184
192
|
};
|
|
185
193
|
}
|
|
186
194
|
export async function verify(ctx) {
|
|
187
|
-
const file = clientConfigPath(id, ctx.home, ctx.platform);
|
|
188
|
-
const secrets = envPath(ctx
|
|
195
|
+
const file = clientConfigPath(id, ctx.home, ctx.platform, ctx.env);
|
|
196
|
+
const secrets = envPath(ctx);
|
|
189
197
|
const doc = parseYaml(await fs.readFile(file, 'utf8'));
|
|
190
198
|
const data = asRecord(doc.toJS());
|
|
191
199
|
const providers = Array.isArray(data.custom_providers) ? data.custom_providers : [];
|
|
@@ -215,8 +223,8 @@ export async function verify(ctx) {
|
|
|
215
223
|
return { id, name, changed: false, path: file, message: `verified ${codexIds.length} Codex/OpenAI, ${deepSeekIds.length} ${chatLabel}, and ${claudeIds.length} Claude models` };
|
|
216
224
|
}
|
|
217
225
|
export async function remove(ctx) {
|
|
218
|
-
const file = clientConfigPath(id, ctx.home, ctx.platform);
|
|
219
|
-
const secrets = envPath(ctx
|
|
226
|
+
const file = clientConfigPath(id, ctx.home, ctx.platform, ctx.env);
|
|
227
|
+
const secrets = envPath(ctx);
|
|
220
228
|
const currentEnv = await fs.readFile(secrets, 'utf8').catch(() => '');
|
|
221
229
|
if (!(await fs.pathExists(file))) {
|
|
222
230
|
const removedLegacy = await removeLegacyArtifact(ctx);
|
package/dist/configure/models.js
CHANGED
|
@@ -2,5 +2,12 @@ import { ClientId } from '../types/client.js';
|
|
|
2
2
|
export declare function codexDir(home: string): string;
|
|
3
3
|
export declare function codexConfigPath(home: string): string;
|
|
4
4
|
export declare function codexAuthPath(home: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Hermes Agent's home: HERMES_HOME when set, else its platform default.
|
|
7
|
+
* Native Windows Hermes keeps config.yaml and .env in %LOCALAPPDATA%\\hermes
|
|
8
|
+
* and never reads ~/.hermes (hermes_constants._get_platform_default_hermes_home),
|
|
9
|
+
* so writing ~/.hermes there left Hermes with no ScaleMax provider.
|
|
10
|
+
*/
|
|
11
|
+
export declare function hermesHome(home: string, platform: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
|
|
5
12
|
export declare function clientConfigPath(id: ClientId, home: string, platform: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
|
|
6
13
|
export declare function installHints(id: ClientId, home: string, platform: NodeJS.Platform, env?: NodeJS.ProcessEnv): string[];
|
package/dist/configure/paths.js
CHANGED
|
@@ -3,6 +3,20 @@ import { configRoot } from '../utils/paths.js';
|
|
|
3
3
|
export function codexDir(home) { return path.join(home, '.codex'); }
|
|
4
4
|
export function codexConfigPath(home) { return path.join(codexDir(home), 'config.toml'); }
|
|
5
5
|
export function codexAuthPath(home) { return path.join(codexDir(home), 'auth.json'); }
|
|
6
|
+
/**
|
|
7
|
+
* Hermes Agent's home: HERMES_HOME when set, else its platform default.
|
|
8
|
+
* Native Windows Hermes keeps config.yaml and .env in %LOCALAPPDATA%\\hermes
|
|
9
|
+
* and never reads ~/.hermes (hermes_constants._get_platform_default_hermes_home),
|
|
10
|
+
* so writing ~/.hermes there left Hermes with no ScaleMax provider.
|
|
11
|
+
*/
|
|
12
|
+
export function hermesHome(home, platform, env = process.env) {
|
|
13
|
+
const explicit = env.HERMES_HOME?.trim();
|
|
14
|
+
if (explicit)
|
|
15
|
+
return explicit;
|
|
16
|
+
if (platform === 'win32')
|
|
17
|
+
return path.join(env.LOCALAPPDATA?.trim() || path.join(home, 'AppData', 'Local'), 'hermes');
|
|
18
|
+
return path.join(home, '.hermes');
|
|
19
|
+
}
|
|
6
20
|
export function clientConfigPath(id, home, platform, env = process.env) {
|
|
7
21
|
const app = configRoot(home, platform, env);
|
|
8
22
|
const linux = env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
@@ -23,7 +37,7 @@ export function clientConfigPath(id, home, platform, env = process.env) {
|
|
|
23
37
|
case 'claude': return path.join(home, '.claude', 'settings.json');
|
|
24
38
|
case 'opencode': return path.join(linux, 'opencode', 'opencode.json');
|
|
25
39
|
case 'openclaw': return path.join(home, '.openclaw', 'openclaw.json');
|
|
26
|
-
case 'hermes': return path.join(home,
|
|
40
|
+
case 'hermes': return path.join(hermesHome(home, platform, env), 'config.yaml');
|
|
27
41
|
case 'cherry-studio': return path.join(home, '.scalemax', 'cherry-studio.setup.json');
|
|
28
42
|
case 'api-code': return path.join(home, '.scalemax', 'api-code.examples.json');
|
|
29
43
|
}
|
|
@@ -41,7 +55,7 @@ export function installHints(id, home, platform, env = process.env) {
|
|
|
41
55
|
case 'claude': return [path.join(home, '.claude')];
|
|
42
56
|
case 'opencode': return [path.join(linux, 'opencode')];
|
|
43
57
|
case 'openclaw': return [path.join(home, '.openclaw'), path.join(linux, 'openclaw')];
|
|
44
|
-
case 'hermes': return [path.join(home, '.hermes')];
|
|
58
|
+
case 'hermes': return [...new Set([hermesHome(home, platform, env), path.join(home, '.hermes')])];
|
|
45
59
|
case 'cherry-studio': return [platform === 'darwin' ? '/Applications/Cherry Studio.app' : path.join(app, 'Cherry Studio'), path.join(linux, 'CherryStudio')];
|
|
46
60
|
case 'api-code': return [path.join(home, '.scalemax')];
|
|
47
61
|
}
|
|
@@ -2,10 +2,10 @@ import path from 'node:path';
|
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import JSON5 from 'json5';
|
|
4
4
|
import { atomicWrite, backupFile, captureFileSnapshots, restoreFileSnapshots, snapshotRollback } from '../utils/fs.js';
|
|
5
|
-
import { clientConfigPath } from './paths.js';
|
|
6
5
|
import { anthropicModelIds, deepSeekChatModelIds, isDeepSeekChatModelId, modelIdKey, modelName, modelSupportsTools, openAiBaseUrl, openAiResponsesModelIds, preferredAnthropicModel, preferredChatCompletionsModel, preferredOpenAiResponsesModel } from './models.js';
|
|
7
6
|
import { isAnthropicModel } from '../utils/api.js';
|
|
8
7
|
import { rooRipgrepRepairNote } from '../repair/roo-ripgrep.js';
|
|
8
|
+
import { rooSettingsTargets } from '../detect/roo-code.js';
|
|
9
9
|
const id = 'roo-code';
|
|
10
10
|
const name = 'Roo Code (VS Code)';
|
|
11
11
|
const importSetting = 'roo-cline.autoImportSettingsPath';
|
|
@@ -167,23 +167,30 @@ export async function configure(ctx) {
|
|
|
167
167
|
return note ? { ...result, note } : result;
|
|
168
168
|
}
|
|
169
169
|
async function configureHandoff(ctx) {
|
|
170
|
-
const
|
|
170
|
+
const targets = await rooSettingsTargets(ctx.home, ctx.platform, ctx.env);
|
|
171
171
|
const profileFile = importPath(ctx.home);
|
|
172
|
-
const snapshots = await captureFileSnapshots([settingsFile, profileFile]);
|
|
173
|
-
const originalSettings = await fs.readFile(settingsFile, 'utf8').catch(() => '');
|
|
172
|
+
const snapshots = await captureFileSnapshots([...targets.map(target => target.settingsFile), profileFile]);
|
|
174
173
|
const originalProfile = await fs.readFile(profileFile, 'utf8').catch(() => '');
|
|
175
|
-
const
|
|
176
|
-
|
|
174
|
+
const writes = [];
|
|
175
|
+
for (const target of targets) {
|
|
176
|
+
const original = await fs.readFile(target.settingsFile, 'utf8').catch(() => '');
|
|
177
|
+
const next = JSON.stringify({ ...parseSettings(original, target.settingsFile), [importSetting]: importSettingValue }, null, 2) + '\n';
|
|
178
|
+
if (original !== next)
|
|
179
|
+
writes.push({ file: target.settingsFile, text: next });
|
|
180
|
+
}
|
|
177
181
|
const nextProfile = handoff(ctx);
|
|
178
|
-
|
|
182
|
+
const editors = targets.map(target => target.editor).join(', ');
|
|
183
|
+
if (!writes.length && originalProfile === nextProfile) {
|
|
179
184
|
await fs.chmod(profileFile, 0o600).catch(() => undefined);
|
|
180
|
-
return { id, name, changed: false, path: profileFile, message:
|
|
185
|
+
return { id, name, changed: false, path: profileFile, message: `supported Roo Code auto-import handoff is already current (${editors}); reload the editor to consume it`, tip: ROO_CODE_MODE_TIP };
|
|
181
186
|
}
|
|
182
|
-
|
|
187
|
+
for (const write of writes)
|
|
188
|
+
await backupFile(write.file, ctx.home);
|
|
183
189
|
await backupFile(profileFile, ctx.home);
|
|
184
190
|
try {
|
|
185
191
|
await atomicWrite(profileFile, nextProfile, 0o600, ctx.platform);
|
|
186
|
-
|
|
192
|
+
for (const write of writes)
|
|
193
|
+
await atomicWrite(write.file, write.text, 0o600, ctx.platform);
|
|
187
194
|
}
|
|
188
195
|
catch (error) {
|
|
189
196
|
try {
|
|
@@ -199,19 +206,23 @@ async function configureHandoff(ctx) {
|
|
|
199
206
|
name,
|
|
200
207
|
changed: true,
|
|
201
208
|
path: profileFile,
|
|
202
|
-
message:
|
|
209
|
+
message: `created a private, supported auto-import handoff for ${editors}; reload the editor so Roo Code imports the profile into SecretStorage`,
|
|
203
210
|
tip: ROO_CODE_MODE_TIP,
|
|
204
211
|
rollback: snapshotRollback(snapshots, ctx.platform),
|
|
205
212
|
};
|
|
206
213
|
}
|
|
207
214
|
export async function verify(ctx) {
|
|
208
|
-
const settingsFile = clientConfigPath(id, ctx.home, ctx.platform, ctx.env);
|
|
209
215
|
const profileFile = importPath(ctx.home);
|
|
210
|
-
|
|
216
|
+
let handoffSet = true;
|
|
217
|
+
for (const target of await rooSettingsTargets(ctx.home, ctx.platform, ctx.env)) {
|
|
218
|
+
const settings = parseSettings(await fs.readFile(target.settingsFile, 'utf8'), target.settingsFile);
|
|
219
|
+
if (settings[importSetting] !== importSettingValue)
|
|
220
|
+
handoffSet = false;
|
|
221
|
+
}
|
|
211
222
|
const profile = await fs.readJson(profileFile).catch(() => undefined);
|
|
212
223
|
const expectedProfiles = profilesFor(ctx);
|
|
213
224
|
const current = currentProfileFor(ctx, expectedProfiles);
|
|
214
|
-
if (
|
|
225
|
+
if (!handoffSet || profile?.providerProfiles?.currentApiConfigName !== current.name) {
|
|
215
226
|
throw new Error('Roo Code auto-import handoff is incomplete.');
|
|
216
227
|
}
|
|
217
228
|
if (profile?.globalSettings?.maxWorkspaceFiles !== 0) {
|
|
@@ -232,10 +243,11 @@ export async function verify(ctx) {
|
|
|
232
243
|
return { id, name, changed: false, path: profileFile, message: 'verified supported auto-import handoff (runtime activation requires a VS Code reload)' };
|
|
233
244
|
}
|
|
234
245
|
export async function remove(ctx) {
|
|
235
|
-
const settingsFile = clientConfigPath(id, ctx.home, ctx.platform, ctx.env);
|
|
236
246
|
const profileFile = importPath(ctx.home);
|
|
237
247
|
let changed = false;
|
|
238
|
-
|
|
248
|
+
for (const { settingsFile } of await rooSettingsTargets(ctx.home, ctx.platform, ctx.env)) {
|
|
249
|
+
if (!(await fs.pathExists(settingsFile)))
|
|
250
|
+
continue;
|
|
239
251
|
const text = await fs.readFile(settingsFile, 'utf8');
|
|
240
252
|
const settings = parseSettings(text, settingsFile);
|
|
241
253
|
if (settings[importSetting] === importSettingValue) {
|
|
@@ -5,6 +5,13 @@ export interface EditorExtensionRoot {
|
|
|
5
5
|
editor: string;
|
|
6
6
|
/** Path segments below the home folder. */
|
|
7
7
|
segments: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* The editor's user-data folder name under the platform config root
|
|
10
|
+
* (%APPDATA% / ~/Library/Application Support / ~/.config), whose
|
|
11
|
+
* `User/settings.json` Roo Code reads. Absent for remote servers, which have
|
|
12
|
+
* no local settings file of their own.
|
|
13
|
+
*/
|
|
14
|
+
userDir?: string;
|
|
8
15
|
}
|
|
9
16
|
/**
|
|
10
17
|
* VS Code's own per-user extension folders. Detection stays limited to these
|
|
@@ -17,6 +24,18 @@ export declare const VSCODE_EXTENSION_ROOTS: readonly EditorExtensionRoot[];
|
|
|
17
24
|
* installed extension itself rather than writing editor settings.
|
|
18
25
|
*/
|
|
19
26
|
export declare const ALL_EDITOR_EXTENSION_ROOTS: readonly EditorExtensionRoot[];
|
|
27
|
+
export interface RooSettingsTarget {
|
|
28
|
+
editor: string;
|
|
29
|
+
/** That editor's user settings.json, which holds roo-cline.autoImportSettingsPath. */
|
|
30
|
+
settingsFile: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Every settings.json Roo Code reads its auto-import path from: VS Code's
|
|
34
|
+
* (always, as before) plus each VS Code-family editor that has Roo Code
|
|
35
|
+
* installed. A fork reads only its own settings.json, so writing VS Code's
|
|
36
|
+
* alone left Roo in Antigravity, Cursor or Windsurf unconfigured.
|
|
37
|
+
*/
|
|
38
|
+
export declare function rooSettingsTargets(home: string, platform: NodeJS.Platform, env?: NodeJS.ProcessEnv): Promise<RooSettingsTarget[]>;
|
|
20
39
|
export interface RooExtensionDir {
|
|
21
40
|
editor: string;
|
|
22
41
|
/** Absolute extension folder, e.g. ~/.vscode/extensions/rooveterinaryinc.roo-cline-3.54.0 */
|
package/dist/detect/roo-code.js
CHANGED
|
@@ -3,14 +3,15 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import JSON5 from 'json5';
|
|
4
4
|
import { commandExists } from '../utils/exec.js';
|
|
5
5
|
import { clientConfigPath } from '../configure/paths.js';
|
|
6
|
+
import { configRoot } from '../utils/paths.js';
|
|
6
7
|
export const ROO_EXTENSION_PATTERN = /^rooveterinaryinc\.roo-cline-/i;
|
|
7
8
|
/**
|
|
8
9
|
* VS Code's own per-user extension folders. Detection stays limited to these
|
|
9
10
|
* because configure writes VS Code's settings.json, which forks do not read.
|
|
10
11
|
*/
|
|
11
12
|
export const VSCODE_EXTENSION_ROOTS = [
|
|
12
|
-
{ editor: 'VS Code', segments: ['.vscode', 'extensions'] },
|
|
13
|
-
{ editor: 'VS Code Insiders', segments: ['.vscode-insiders', 'extensions'] },
|
|
13
|
+
{ editor: 'VS Code', segments: ['.vscode', 'extensions'], userDir: 'Code' },
|
|
14
|
+
{ editor: 'VS Code Insiders', segments: ['.vscode-insiders', 'extensions'], userDir: 'Code - Insiders' },
|
|
14
15
|
];
|
|
15
16
|
/**
|
|
16
17
|
* Every per-user extension folder Roo Code can be installed into: VS Code,
|
|
@@ -19,14 +20,36 @@ export const VSCODE_EXTENSION_ROOTS = [
|
|
|
19
20
|
*/
|
|
20
21
|
export const ALL_EDITOR_EXTENSION_ROOTS = [
|
|
21
22
|
...VSCODE_EXTENSION_ROOTS,
|
|
22
|
-
{ editor: 'Cursor', segments: ['.cursor', 'extensions'] },
|
|
23
|
-
{ editor: 'Windsurf', segments: ['.windsurf', 'extensions'] },
|
|
24
|
-
{ editor: 'VSCodium', segments: ['.vscode-oss', 'extensions'] },
|
|
23
|
+
{ editor: 'Cursor', segments: ['.cursor', 'extensions'], userDir: 'Cursor' },
|
|
24
|
+
{ editor: 'Windsurf', segments: ['.windsurf', 'extensions'], userDir: 'Windsurf' },
|
|
25
|
+
{ editor: 'VSCodium', segments: ['.vscode-oss', 'extensions'], userDir: 'VSCodium' },
|
|
25
26
|
{ editor: 'VS Code Server', segments: ['.vscode-server', 'extensions'] },
|
|
26
27
|
{ editor: 'Cursor Server', segments: ['.cursor-server', 'extensions'] },
|
|
27
|
-
{ editor: 'Trae', segments: ['.trae', 'extensions'] },
|
|
28
|
-
{ editor: 'Kiro', segments: ['.kiro', 'extensions'] },
|
|
28
|
+
{ editor: 'Trae', segments: ['.trae', 'extensions'], userDir: 'Trae' },
|
|
29
|
+
{ editor: 'Kiro', segments: ['.kiro', 'extensions'], userDir: 'Kiro' },
|
|
30
|
+
// Google Antigravity (VS Code fork, Open VSX). Product.json on Windows,
|
|
31
|
+
// 2.5.5: dataFolderName ".antigravity-ide", user data "%APPDATA%\Antigravity IDE".
|
|
32
|
+
{ editor: 'Antigravity IDE', segments: ['.antigravity-ide', 'extensions'], userDir: 'Antigravity IDE' },
|
|
33
|
+
{ editor: 'Antigravity', segments: ['.antigravity', 'extensions'], userDir: 'Antigravity' },
|
|
29
34
|
];
|
|
35
|
+
/**
|
|
36
|
+
* Every settings.json Roo Code reads its auto-import path from: VS Code's
|
|
37
|
+
* (always, as before) plus each VS Code-family editor that has Roo Code
|
|
38
|
+
* installed. A fork reads only its own settings.json, so writing VS Code's
|
|
39
|
+
* alone left Roo in Antigravity, Cursor or Windsurf unconfigured.
|
|
40
|
+
*/
|
|
41
|
+
export async function rooSettingsTargets(home, platform, env = process.env) {
|
|
42
|
+
const targets = [{ editor: 'VS Code', settingsFile: clientConfigPath('roo-code', home, platform, env) }];
|
|
43
|
+
const root = configRoot(home, platform, env);
|
|
44
|
+
const forks = ALL_EDITOR_EXTENSION_ROOTS.filter(entry => entry.userDir && entry.editor !== 'VS Code');
|
|
45
|
+
for (const hit of await findRooExtensionDirs(home, forks)) {
|
|
46
|
+
const fork = forks.find(entry => entry.editor === hit.editor);
|
|
47
|
+
const settingsFile = path.join(root, fork.userDir, 'User', 'settings.json');
|
|
48
|
+
if (!targets.some(target => target.settingsFile === settingsFile))
|
|
49
|
+
targets.push({ editor: hit.editor, settingsFile });
|
|
50
|
+
}
|
|
51
|
+
return targets;
|
|
52
|
+
}
|
|
30
53
|
/** Roo Code extension folders under the given roots; missing roots are skipped. */
|
|
31
54
|
export async function findRooExtensionDirs(home, roots = VSCODE_EXTENSION_ROOTS) {
|
|
32
55
|
const hits = [];
|
|
@@ -42,7 +65,7 @@ export async function findRooExtensionDirs(home, roots = VSCODE_EXTENSION_ROOTS)
|
|
|
42
65
|
return hits;
|
|
43
66
|
}
|
|
44
67
|
export async function detect(ctx) {
|
|
45
|
-
const paths = (await findRooExtensionDirs(ctx.home)).map(hit => hit.dir);
|
|
68
|
+
const paths = (await findRooExtensionDirs(ctx.home, ALL_EDITOR_EXTENSION_ROOTS.filter(entry => entry.userDir))).map(hit => hit.dir);
|
|
46
69
|
const executable = await commandExists('code', ctx.env);
|
|
47
70
|
if (executable)
|
|
48
71
|
paths.unshift('command:code');
|