@ottocode/sdk 0.1.265 → 0.1.267
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/package.json +2 -2
- package/src/config/src/index.ts +4 -0
- package/src/config/src/manager.ts +8 -14
- package/src/config/src/paths.ts +4 -0
- package/src/core/src/providers/resolver.ts +29 -70
- package/src/core/src/tools/bin-manager/cache.ts +13 -0
- package/src/core/src/tools/bin-manager/filesystem.ts +32 -0
- package/src/core/src/tools/bin-manager/paths.ts +36 -0
- package/src/core/src/tools/bin-manager/vendor.ts +80 -0
- package/src/core/src/tools/bin-manager.ts +14 -140
- package/src/core/src/tools/builtin/patch/apply-hunk.ts +308 -0
- package/src/core/src/tools/builtin/patch/apply-report.ts +99 -0
- package/src/core/src/tools/builtin/patch/apply.ts +6 -663
- package/src/core/src/tools/builtin/patch/hunk-header.ts +17 -0
- package/src/core/src/tools/builtin/patch/indentation.ts +160 -0
- package/src/core/src/tools/builtin/patch/matching.ts +58 -0
- package/src/core/src/tools/builtin/patch/parse-enveloped.ts +10 -72
- package/src/core/src/tools/builtin/patch/parse-unified.ts +15 -105
- package/src/core/src/tools/builtin/patch/replace-builder.ts +64 -0
- package/src/core/src/tools/builtin/patch/unified-state.ts +86 -0
- package/src/core/src/tools/builtin/websearch-strategies.ts +197 -0
- package/src/core/src/tools/builtin/websearch.ts +9 -187
- package/src/core/src/tools/loader.ts +6 -49
- package/src/core/src/tools/plugin-discovery.ts +86 -0
- package/src/core/src/utils/logger/format.ts +50 -0
- package/src/core/src/utils/logger/sinks.ts +61 -0
- package/src/core/src/utils/logger.ts +2 -119
- package/src/index.ts +3 -0
- package/src/providers/src/index.ts +4 -0
- package/src/providers/src/model-resolution.ts +21 -0
- package/src/providers/src/zai-client.ts +5 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.267",
|
|
4
4
|
"description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
|
|
5
5
|
"author": "nitishxyz",
|
|
6
6
|
"license": "MIT",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@modelcontextprotocol/sdk": "^1.12",
|
|
103
103
|
"@openauthjs/openauth": "^0.4.3",
|
|
104
104
|
"@openrouter/ai-sdk-provider": "^1.2.0",
|
|
105
|
-
"@ottorouter/ai-sdk": "0.2.
|
|
105
|
+
"@ottorouter/ai-sdk": "0.2.2",
|
|
106
106
|
"@solana/web3.js": "^1.98.0",
|
|
107
107
|
"ai": "^6.0.170",
|
|
108
108
|
"ai-sdk-ollama": "^3.8.3",
|
package/src/config/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getGlobalConfigPath,
|
|
3
|
+
getGlobalSkillsConfigPath,
|
|
3
4
|
getLocalDataDir,
|
|
4
5
|
ensureDir,
|
|
5
6
|
fileExists,
|
|
@@ -54,13 +55,16 @@ export async function loadConfig(
|
|
|
54
55
|
const dbPath = joinPath(dataDir, 'otto.sqlite');
|
|
55
56
|
const projectConfigPath = joinPath(dataDir, 'config.json');
|
|
56
57
|
const globalConfigPath = getGlobalConfigPath();
|
|
58
|
+
const globalSkillsConfigPath = getGlobalSkillsConfigPath();
|
|
57
59
|
|
|
58
60
|
const projectCfg = await readJsonOptional(projectConfigPath);
|
|
59
61
|
const globalCfg = await readJsonOptional(globalConfigPath);
|
|
62
|
+
const globalSkillsCfg = await readJsonOptional(globalSkillsConfigPath);
|
|
60
63
|
|
|
61
64
|
const merged = deepMerge(
|
|
62
65
|
DEFAULTS,
|
|
63
66
|
globalCfg,
|
|
67
|
+
globalSkillsCfg ? { skills: globalSkillsCfg } : undefined,
|
|
64
68
|
omitGlobalOnlySettings(projectCfg),
|
|
65
69
|
);
|
|
66
70
|
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
import {
|
|
14
14
|
getGlobalConfigDir,
|
|
15
15
|
getGlobalConfigPath,
|
|
16
|
+
getGlobalSkillsConfigPath,
|
|
16
17
|
getGlobalDebugDir,
|
|
17
18
|
getGlobalDebugLogPath,
|
|
18
19
|
getGlobalDebugSessionsDir,
|
|
@@ -151,25 +152,18 @@ export async function writeSkillSettings(
|
|
|
151
152
|
updates: SkillSettings,
|
|
152
153
|
_projectRoot?: string,
|
|
153
154
|
) {
|
|
154
|
-
const filePath =
|
|
155
|
+
const filePath = getGlobalSkillsConfigPath();
|
|
155
156
|
const existing = await readJsonFile(filePath);
|
|
156
|
-
const prevSkills =
|
|
157
|
-
existing && typeof existing.skills === 'object'
|
|
158
|
-
? (existing.skills as Record<string, unknown>)
|
|
159
|
-
: {};
|
|
160
157
|
const prevItems =
|
|
161
|
-
|
|
162
|
-
? (
|
|
158
|
+
existing?.items && typeof existing.items === 'object'
|
|
159
|
+
? (existing.items as Record<string, unknown>)
|
|
163
160
|
: {};
|
|
164
161
|
const next = {
|
|
165
162
|
...existing,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
...
|
|
169
|
-
items
|
|
170
|
-
...prevItems,
|
|
171
|
-
...(updates.items ?? {}),
|
|
172
|
-
},
|
|
163
|
+
...updates,
|
|
164
|
+
items: {
|
|
165
|
+
...prevItems,
|
|
166
|
+
...(updates.items ?? {}),
|
|
173
167
|
},
|
|
174
168
|
};
|
|
175
169
|
await writeConfigFile(filePath, next);
|
package/src/config/src/paths.ts
CHANGED
|
@@ -31,6 +31,10 @@ export function getGlobalConfigPath(): string {
|
|
|
31
31
|
return joinPath(getGlobalConfigDir(), 'config.json');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export function getGlobalSkillsConfigPath(): string {
|
|
35
|
+
return joinPath(getGlobalConfigDir(), 'skills.json');
|
|
36
|
+
}
|
|
37
|
+
|
|
34
38
|
export function getGlobalAuthPath(): string {
|
|
35
39
|
return joinPath(getGlobalConfigDir(), 'auth.json');
|
|
36
40
|
}
|
|
@@ -4,33 +4,22 @@ import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
|
4
4
|
import { createOllama } from 'ai-sdk-ollama';
|
|
5
5
|
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
6
6
|
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
7
|
-
import { createXai } from '@ai-sdk/xai';
|
|
8
7
|
import {
|
|
9
8
|
catalog,
|
|
9
|
+
createMinimaxModel,
|
|
10
|
+
createMoonshotModel,
|
|
10
11
|
createOttoRouterModel,
|
|
11
12
|
createOpenAIOAuthModel,
|
|
13
|
+
createXaiModel,
|
|
14
|
+
createZaiCodingModel,
|
|
15
|
+
createZaiModel,
|
|
12
16
|
normalizeOllamaBaseURL,
|
|
17
|
+
resolveOpenAIResponsesModel,
|
|
18
|
+
shouldUseOpenAIResponsesApi,
|
|
13
19
|
} from '../../../providers/src/index.ts';
|
|
14
20
|
import { createCopilotModel } from '../../../providers/src/copilot-client.ts';
|
|
15
21
|
import type { OAuth } from '../../../types/src/index.ts';
|
|
16
22
|
|
|
17
|
-
function needsResponsesApi(model: string): boolean {
|
|
18
|
-
const m = model.toLowerCase();
|
|
19
|
-
if (m.includes('gpt-5')) return true;
|
|
20
|
-
if (m.startsWith('o1')) return true;
|
|
21
|
-
if (m.startsWith('o3')) return true;
|
|
22
|
-
if (m.startsWith('o4')) return true;
|
|
23
|
-
if (m.includes('codex-mini')) return true;
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function resolveOpenAIModel(
|
|
28
|
-
instance: ReturnType<typeof createOpenAI>,
|
|
29
|
-
model: string,
|
|
30
|
-
) {
|
|
31
|
-
return needsResponsesApi(model) ? instance.responses(model) : instance(model);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
23
|
export type ProviderName =
|
|
35
24
|
| 'openai'
|
|
36
25
|
| 'anthropic'
|
|
@@ -71,13 +60,15 @@ export async function resolveModel(
|
|
|
71
60
|
apiKey: config.apiKey || 'oauth-token',
|
|
72
61
|
fetch: config.customFetch,
|
|
73
62
|
});
|
|
74
|
-
return
|
|
63
|
+
return resolveOpenAIResponsesModel(instance, model);
|
|
75
64
|
}
|
|
76
65
|
if (config.apiKey) {
|
|
77
66
|
const instance = createOpenAI({ apiKey: config.apiKey });
|
|
78
|
-
return
|
|
67
|
+
return resolveOpenAIResponsesModel(instance, model);
|
|
79
68
|
}
|
|
80
|
-
return
|
|
69
|
+
return shouldUseOpenAIResponsesApi(model)
|
|
70
|
+
? openai.responses(model)
|
|
71
|
+
: openai(model);
|
|
81
72
|
}
|
|
82
73
|
|
|
83
74
|
if (provider === 'anthropic') {
|
|
@@ -204,70 +195,38 @@ export async function resolveModel(
|
|
|
204
195
|
}
|
|
205
196
|
|
|
206
197
|
if (provider === 'xai') {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return instance(model);
|
|
198
|
+
return createXaiModel(model, {
|
|
199
|
+
apiKey: config.apiKey,
|
|
200
|
+
baseURL: config.baseURL,
|
|
201
|
+
});
|
|
212
202
|
}
|
|
213
203
|
|
|
214
204
|
if (provider === 'zai') {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
config.
|
|
218
|
-
process.env.ZAI_API_KEY ||
|
|
219
|
-
process.env.ZHIPU_API_KEY ||
|
|
220
|
-
'';
|
|
221
|
-
const baseURL =
|
|
222
|
-
config.baseURL || entry?.api || 'https://api.z.ai/api/paas/v4';
|
|
223
|
-
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
224
|
-
const instance = createOpenAICompatible({
|
|
225
|
-
name: entry?.label ?? 'Z.AI',
|
|
226
|
-
baseURL,
|
|
227
|
-
headers,
|
|
205
|
+
return createZaiModel(model, {
|
|
206
|
+
apiKey: config.apiKey,
|
|
207
|
+
baseURL: config.baseURL,
|
|
228
208
|
});
|
|
229
|
-
return instance(model);
|
|
230
209
|
}
|
|
231
210
|
|
|
232
211
|
if (provider === 'zai-coding') {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
config.
|
|
236
|
-
process.env.ZAI_CODING_API_KEY ||
|
|
237
|
-
process.env.ZHIPU_API_KEY ||
|
|
238
|
-
'';
|
|
239
|
-
const baseURL =
|
|
240
|
-
config.baseURL || entry?.api || 'https://api.z.ai/api/coding/paas/v4';
|
|
241
|
-
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
242
|
-
const instance = createOpenAICompatible({
|
|
243
|
-
name: entry?.label ?? 'Z.AI Coding',
|
|
244
|
-
baseURL,
|
|
245
|
-
headers,
|
|
212
|
+
return createZaiCodingModel(model, {
|
|
213
|
+
apiKey: config.apiKey,
|
|
214
|
+
baseURL: config.baseURL,
|
|
246
215
|
});
|
|
247
|
-
return instance(model);
|
|
248
216
|
}
|
|
249
217
|
|
|
250
218
|
if (provider === 'moonshot') {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
config.baseURL || entry?.api || 'https://api.moonshot.ai/v1';
|
|
255
|
-
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
256
|
-
const instance = createOpenAICompatible({
|
|
257
|
-
name: entry?.label ?? 'Moonshot AI',
|
|
258
|
-
baseURL,
|
|
259
|
-
headers,
|
|
219
|
+
return createMoonshotModel(model, {
|
|
220
|
+
apiKey: config.apiKey,
|
|
221
|
+
baseURL: config.baseURL,
|
|
260
222
|
});
|
|
261
|
-
return instance(model);
|
|
262
223
|
}
|
|
263
224
|
|
|
264
225
|
if (provider === 'minimax') {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const instance = createAnthropic({ apiKey, baseURL });
|
|
270
|
-
return instance(model);
|
|
226
|
+
return createMinimaxModel(model, {
|
|
227
|
+
apiKey: config.apiKey,
|
|
228
|
+
baseURL: config.baseURL,
|
|
229
|
+
});
|
|
271
230
|
}
|
|
272
231
|
|
|
273
232
|
throw new Error(`Unsupported provider: ${provider}`);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const resolvedPaths = new Map<string, string>();
|
|
2
|
+
|
|
3
|
+
export function getCachedBinary(name: string): string | undefined {
|
|
4
|
+
return resolvedPaths.get(name);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function setCachedBinary(name: string, path: string): void {
|
|
8
|
+
resolvedPaths.set(name, path);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function clearCachedBinaries(): void {
|
|
12
|
+
resolvedPaths.clear();
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
export async function ensureDir(dir: string): Promise<void> {
|
|
4
|
+
await fs.mkdir(dir, { recursive: true });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function fileExists(p: string): Promise<boolean> {
|
|
8
|
+
try {
|
|
9
|
+
await fs.access(p);
|
|
10
|
+
return true;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function isExecutable(p: string): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
await fs.access(p, 0o1);
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function makeExecutable(p: string): Promise<void> {
|
|
26
|
+
if (process.platform === 'win32') return;
|
|
27
|
+
try {
|
|
28
|
+
await fs.chmod(p, 0o755);
|
|
29
|
+
} catch {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { fs };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
|
|
3
|
+
const OTTO_BIN_DIR_NAME = 'bin';
|
|
4
|
+
|
|
5
|
+
let cachedBinDir: string | null = null;
|
|
6
|
+
|
|
7
|
+
function getConfigHome(): string {
|
|
8
|
+
const cfgHome = process.env.XDG_CONFIG_HOME;
|
|
9
|
+
if (cfgHome?.trim()) return cfgHome.replace(/\\/g, '/');
|
|
10
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
11
|
+
return join(home, '.config');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getAgiBinDir(): string {
|
|
15
|
+
if (cachedBinDir) return cachedBinDir;
|
|
16
|
+
cachedBinDir = join(getConfigHome(), 'otto', OTTO_BIN_DIR_NAME);
|
|
17
|
+
return cachedBinDir;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getPlatformKey(): string {
|
|
21
|
+
const platform = process.platform;
|
|
22
|
+
const arch = process.arch;
|
|
23
|
+
const os =
|
|
24
|
+
platform === 'darwin'
|
|
25
|
+
? 'darwin'
|
|
26
|
+
: platform === 'win32'
|
|
27
|
+
? 'windows'
|
|
28
|
+
: 'linux';
|
|
29
|
+
const cpu = arch === 'arm64' ? 'arm64' : 'x64';
|
|
30
|
+
return `${os}-${cpu}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getBinaryFileName(name: string): string {
|
|
34
|
+
if (process.platform === 'win32') return `${name}.exe`;
|
|
35
|
+
return name;
|
|
36
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import {
|
|
3
|
+
ensureDir,
|
|
4
|
+
fileExists,
|
|
5
|
+
fs,
|
|
6
|
+
isExecutable,
|
|
7
|
+
makeExecutable,
|
|
8
|
+
} from './filesystem.ts';
|
|
9
|
+
import { getAgiBinDir, getBinaryFileName, getPlatformKey } from './paths.ts';
|
|
10
|
+
|
|
11
|
+
function getVendorSearchPaths(binaryName: string): string[] {
|
|
12
|
+
const platformKey = getPlatformKey();
|
|
13
|
+
const paths: string[] = [];
|
|
14
|
+
|
|
15
|
+
const tauriResource = process.env.TAURI_RESOURCE_DIR;
|
|
16
|
+
if (tauriResource) {
|
|
17
|
+
paths.push(join(tauriResource, 'vendor', 'bin', platformKey, binaryName));
|
|
18
|
+
paths.push(join(tauriResource, 'vendor', 'bin', binaryName));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const exePath = process.execPath;
|
|
23
|
+
if (exePath) {
|
|
24
|
+
const exeDir = join(exePath, '..');
|
|
25
|
+
paths.push(join(exeDir, 'vendor', 'bin', platformKey, binaryName));
|
|
26
|
+
paths.push(
|
|
27
|
+
join(
|
|
28
|
+
exeDir,
|
|
29
|
+
'..',
|
|
30
|
+
'Resources',
|
|
31
|
+
'vendor',
|
|
32
|
+
'bin',
|
|
33
|
+
platformKey,
|
|
34
|
+
binaryName,
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
} catch {}
|
|
39
|
+
|
|
40
|
+
if (process.env.CARGO_MANIFEST_DIR) {
|
|
41
|
+
paths.push(
|
|
42
|
+
join(
|
|
43
|
+
process.env.CARGO_MANIFEST_DIR,
|
|
44
|
+
'resources',
|
|
45
|
+
'vendor',
|
|
46
|
+
'bin',
|
|
47
|
+
platformKey,
|
|
48
|
+
binaryName,
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const cwd = process.cwd();
|
|
54
|
+
paths.push(join(cwd, 'vendor', 'bin', platformKey, binaryName));
|
|
55
|
+
|
|
56
|
+
return paths;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function extractFromVendor(name: string): Promise<string | null> {
|
|
60
|
+
const binaryName = getBinaryFileName(name);
|
|
61
|
+
const binDir = getAgiBinDir();
|
|
62
|
+
const targetPath = join(binDir, binaryName);
|
|
63
|
+
|
|
64
|
+
if ((await fileExists(targetPath)) && (await isExecutable(targetPath))) {
|
|
65
|
+
return targetPath;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const searchPaths = getVendorSearchPaths(binaryName);
|
|
69
|
+
|
|
70
|
+
for (const src of searchPaths) {
|
|
71
|
+
if (await fileExists(src)) {
|
|
72
|
+
await ensureDir(binDir);
|
|
73
|
+
await fs.copyFile(src, targetPath);
|
|
74
|
+
await makeExecutable(targetPath);
|
|
75
|
+
return targetPath;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
@@ -1,73 +1,18 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { promises as fs } from 'node:fs';
|
|
3
2
|
import { spawn, execSync } from 'node:child_process';
|
|
4
3
|
import { homedir } from 'node:os';
|
|
4
|
+
import {
|
|
5
|
+
clearCachedBinaries,
|
|
6
|
+
getCachedBinary,
|
|
7
|
+
setCachedBinary,
|
|
8
|
+
} from './bin-manager/cache.ts';
|
|
9
|
+
import { fileExists, isExecutable } from './bin-manager/filesystem.ts';
|
|
10
|
+
import { getAgiBinDir, getBinaryFileName } from './bin-manager/paths.ts';
|
|
11
|
+
import { extractFromVendor } from './bin-manager/vendor.ts';
|
|
5
12
|
|
|
6
|
-
const OTTO_BIN_DIR_NAME = 'bin';
|
|
7
|
-
|
|
8
|
-
let cachedBinDir: string | null = null;
|
|
9
|
-
const resolvedPaths = new Map<string, string>();
|
|
10
13
|
let cachedLoginPath: string | null = null;
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
const cfgHome = process.env.XDG_CONFIG_HOME;
|
|
14
|
-
if (cfgHome?.trim()) return cfgHome.replace(/\\/g, '/');
|
|
15
|
-
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
16
|
-
return join(home, '.config');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getAgiBinDir(): string {
|
|
20
|
-
if (cachedBinDir) return cachedBinDir;
|
|
21
|
-
cachedBinDir = join(getConfigHome(), 'otto', OTTO_BIN_DIR_NAME);
|
|
22
|
-
return cachedBinDir;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getPlatformKey(): string {
|
|
26
|
-
const platform = process.platform;
|
|
27
|
-
const arch = process.arch;
|
|
28
|
-
const os =
|
|
29
|
-
platform === 'darwin'
|
|
30
|
-
? 'darwin'
|
|
31
|
-
: platform === 'win32'
|
|
32
|
-
? 'windows'
|
|
33
|
-
: 'linux';
|
|
34
|
-
const cpu = arch === 'arm64' ? 'arm64' : 'x64';
|
|
35
|
-
return `${os}-${cpu}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getBinaryFileName(name: string): string {
|
|
39
|
-
if (process.platform === 'win32') return `${name}.exe`;
|
|
40
|
-
return name;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function ensureDir(dir: string): Promise<void> {
|
|
44
|
-
await fs.mkdir(dir, { recursive: true });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function fileExists(p: string): Promise<boolean> {
|
|
48
|
-
try {
|
|
49
|
-
await fs.access(p);
|
|
50
|
-
return true;
|
|
51
|
-
} catch {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function isExecutable(p: string): Promise<boolean> {
|
|
57
|
-
try {
|
|
58
|
-
await fs.access(p, 0o1);
|
|
59
|
-
return true;
|
|
60
|
-
} catch {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function makeExecutable(p: string): Promise<void> {
|
|
66
|
-
if (process.platform === 'win32') return;
|
|
67
|
-
try {
|
|
68
|
-
await fs.chmod(p, 0o755);
|
|
69
|
-
} catch {}
|
|
70
|
-
}
|
|
15
|
+
export { getAgiBinDir } from './bin-manager/paths.ts';
|
|
71
16
|
|
|
72
17
|
async function whichBinary(name: string): Promise<string | null> {
|
|
73
18
|
const cmd = process.platform === 'win32' ? 'where' : 'which';
|
|
@@ -85,79 +30,8 @@ async function whichBinary(name: string): Promise<string | null> {
|
|
|
85
30
|
});
|
|
86
31
|
}
|
|
87
32
|
|
|
88
|
-
function getVendorSearchPaths(binaryName: string): string[] {
|
|
89
|
-
const platformKey = getPlatformKey();
|
|
90
|
-
const paths: string[] = [];
|
|
91
|
-
|
|
92
|
-
const tauriResource = process.env.TAURI_RESOURCE_DIR;
|
|
93
|
-
if (tauriResource) {
|
|
94
|
-
paths.push(join(tauriResource, 'vendor', 'bin', platformKey, binaryName));
|
|
95
|
-
paths.push(join(tauriResource, 'vendor', 'bin', binaryName));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const exePath = process.execPath;
|
|
100
|
-
if (exePath) {
|
|
101
|
-
const exeDir = join(exePath, '..');
|
|
102
|
-
paths.push(join(exeDir, 'vendor', 'bin', platformKey, binaryName));
|
|
103
|
-
paths.push(
|
|
104
|
-
join(
|
|
105
|
-
exeDir,
|
|
106
|
-
'..',
|
|
107
|
-
'Resources',
|
|
108
|
-
'vendor',
|
|
109
|
-
'bin',
|
|
110
|
-
platformKey,
|
|
111
|
-
binaryName,
|
|
112
|
-
),
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
} catch {}
|
|
116
|
-
|
|
117
|
-
if (process.env.CARGO_MANIFEST_DIR) {
|
|
118
|
-
paths.push(
|
|
119
|
-
join(
|
|
120
|
-
process.env.CARGO_MANIFEST_DIR,
|
|
121
|
-
'resources',
|
|
122
|
-
'vendor',
|
|
123
|
-
'bin',
|
|
124
|
-
platformKey,
|
|
125
|
-
binaryName,
|
|
126
|
-
),
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const cwd = process.cwd();
|
|
131
|
-
paths.push(join(cwd, 'vendor', 'bin', platformKey, binaryName));
|
|
132
|
-
|
|
133
|
-
return paths;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function extractFromVendor(name: string): Promise<string | null> {
|
|
137
|
-
const binaryName = getBinaryFileName(name);
|
|
138
|
-
const binDir = getAgiBinDir();
|
|
139
|
-
const targetPath = join(binDir, binaryName);
|
|
140
|
-
|
|
141
|
-
if ((await fileExists(targetPath)) && (await isExecutable(targetPath))) {
|
|
142
|
-
return targetPath;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const searchPaths = getVendorSearchPaths(binaryName);
|
|
146
|
-
|
|
147
|
-
for (const src of searchPaths) {
|
|
148
|
-
if (await fileExists(src)) {
|
|
149
|
-
await ensureDir(binDir);
|
|
150
|
-
await fs.copyFile(src, targetPath);
|
|
151
|
-
await makeExecutable(targetPath);
|
|
152
|
-
return targetPath;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
33
|
export async function resolveBinary(name: string): Promise<string> {
|
|
160
|
-
const cached =
|
|
34
|
+
const cached = getCachedBinary(name);
|
|
161
35
|
if (cached) return cached;
|
|
162
36
|
|
|
163
37
|
const binaryName = getBinaryFileName(name);
|
|
@@ -167,19 +41,19 @@ export async function resolveBinary(name: string): Promise<string> {
|
|
|
167
41
|
(await fileExists(installedPath)) &&
|
|
168
42
|
(await isExecutable(installedPath))
|
|
169
43
|
) {
|
|
170
|
-
|
|
44
|
+
setCachedBinary(name, installedPath);
|
|
171
45
|
return installedPath;
|
|
172
46
|
}
|
|
173
47
|
|
|
174
48
|
const vendorPath = await extractFromVendor(name);
|
|
175
49
|
if (vendorPath) {
|
|
176
|
-
|
|
50
|
+
setCachedBinary(name, vendorPath);
|
|
177
51
|
return vendorPath;
|
|
178
52
|
}
|
|
179
53
|
|
|
180
54
|
const systemPath = await whichBinary(binaryName);
|
|
181
55
|
if (systemPath) {
|
|
182
|
-
|
|
56
|
+
setCachedBinary(name, systemPath);
|
|
183
57
|
return systemPath;
|
|
184
58
|
}
|
|
185
59
|
|
|
@@ -187,7 +61,7 @@ export async function resolveBinary(name: string): Promise<string> {
|
|
|
187
61
|
}
|
|
188
62
|
|
|
189
63
|
export function clearBinaryCache(): void {
|
|
190
|
-
|
|
64
|
+
clearCachedBinaries();
|
|
191
65
|
}
|
|
192
66
|
|
|
193
67
|
function getLoginShellPath(): string | null {
|