@ottocode/server 0.1.252 → 0.1.254
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 +3 -3
- package/src/openapi/paths/config.ts +0 -13
- package/src/openapi/paths/skills.ts +0 -1
- package/src/presets.ts +1 -0
- package/src/routes/config/models.ts +42 -60
- package/src/routes/config/providers.ts +2 -6
- package/src/routes/session-files.ts +20 -2
- package/src/routes/skills.ts +1 -2
- package/src/runtime/agent/registry.ts +3 -0
- package/src/runtime/agent/runner-setup.ts +9 -3
- package/src/runtime/agent/runner.ts +1 -0
- package/src/runtime/tools/approval.ts +1 -0
- package/src/runtime/tools/guards.ts +6 -3
- package/src/runtime/tools/mapping.ts +2 -0
- package/src/tools/adapter.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.254",
|
|
4
4
|
"description": "HTTP API server for ottocode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@ottocode/database": "0.1.
|
|
53
|
-
"@ottocode/sdk": "0.1.
|
|
52
|
+
"@ottocode/database": "0.1.254",
|
|
53
|
+
"@ottocode/sdk": "0.1.254",
|
|
54
54
|
"ai-sdk-ollama": "^3.8.3",
|
|
55
55
|
"drizzle-orm": "^0.44.5",
|
|
56
56
|
"hono": "^4.9.9",
|
|
@@ -139,10 +139,6 @@ export const configPaths = {
|
|
|
139
139
|
items: { type: 'string' },
|
|
140
140
|
},
|
|
141
141
|
allowAnyModel: { type: 'boolean' },
|
|
142
|
-
scope: {
|
|
143
|
-
type: 'string',
|
|
144
|
-
enum: ['global', 'local'],
|
|
145
|
-
},
|
|
146
142
|
},
|
|
147
143
|
},
|
|
148
144
|
},
|
|
@@ -176,15 +172,6 @@ export const configPaths = {
|
|
|
176
172
|
summary: 'Delete provider override or custom provider entry',
|
|
177
173
|
parameters: [
|
|
178
174
|
projectQueryParam(),
|
|
179
|
-
{
|
|
180
|
-
in: 'query',
|
|
181
|
-
name: 'scope',
|
|
182
|
-
required: false,
|
|
183
|
-
schema: {
|
|
184
|
-
type: 'string',
|
|
185
|
-
enum: ['global', 'local'],
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
175
|
{
|
|
189
176
|
in: 'path',
|
|
190
177
|
name: 'provider',
|
package/src/presets.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { Hono } from 'hono';
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_REMOTE_MODEL_CATALOG_URL,
|
|
4
|
+
catalog,
|
|
4
5
|
discoverOllamaModels,
|
|
5
6
|
loadConfig,
|
|
6
|
-
catalog,
|
|
7
|
-
getConfiguredProviderModels,
|
|
8
7
|
getProviderDefinition,
|
|
9
8
|
providerAllowsAnyModel,
|
|
10
9
|
getAuth,
|
|
@@ -29,6 +28,7 @@ import {
|
|
|
29
28
|
const COPILOT_MODELS_URL = 'https://api.githubcopilot.com/models';
|
|
30
29
|
const REMOTE_CATALOG_REFRESH_TTL_MS = 5 * 60 * 1000;
|
|
31
30
|
const PROVIDER_MODEL_REFRESH_TTL_MS = 60 * 1000;
|
|
31
|
+
const USE_BUILTIN_MODEL_CATALOG = process.env.CI === 'true';
|
|
32
32
|
|
|
33
33
|
type UiModel = {
|
|
34
34
|
id: string;
|
|
@@ -76,6 +76,12 @@ function getRemoteCatalogUrl(): string {
|
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function getModelCatalogProviders(
|
|
80
|
+
cachedCatalog: Awaited<ReturnType<typeof readCachedModelCatalog>>,
|
|
81
|
+
) {
|
|
82
|
+
return cachedCatalog?.providers ?? (USE_BUILTIN_MODEL_CATALOG ? catalog : {});
|
|
83
|
+
}
|
|
84
|
+
|
|
79
85
|
async function refreshRemoteCatalogInBackground(): Promise<void> {
|
|
80
86
|
const url = getRemoteCatalogUrl();
|
|
81
87
|
const now = Date.now();
|
|
@@ -132,19 +138,11 @@ async function refreshProviderModelsInBackground(args: {
|
|
|
132
138
|
projectRoot,
|
|
133
139
|
});
|
|
134
140
|
if (!discoveredModels) return;
|
|
135
|
-
const configuredModels = getConfiguredProviderModels(
|
|
136
|
-
await loadConfig(projectRoot),
|
|
137
|
-
provider,
|
|
138
|
-
);
|
|
139
|
-
const models = mergeConfiguredAndCachedModels(
|
|
140
|
-
configuredModels,
|
|
141
|
-
discoveredModels,
|
|
142
|
-
);
|
|
143
141
|
await mergeCachedModelCatalog({
|
|
144
142
|
[provider]: {
|
|
145
143
|
id: provider,
|
|
146
144
|
label: providerDefinition.label,
|
|
147
|
-
models,
|
|
145
|
+
models: discoveredModels,
|
|
148
146
|
},
|
|
149
147
|
});
|
|
150
148
|
} catch (error) {
|
|
@@ -261,40 +259,13 @@ function shouldLazyLoadProviderModels(
|
|
|
261
259
|
);
|
|
262
260
|
}
|
|
263
261
|
|
|
264
|
-
function mergeConfiguredAndCachedModels(
|
|
265
|
-
configuredModels: ModelInfo[],
|
|
266
|
-
cachedModels: ModelInfo[],
|
|
267
|
-
): ModelInfo[] {
|
|
268
|
-
const modelsById = new Map<string, ModelInfo>();
|
|
269
|
-
for (const model of configuredModels) {
|
|
270
|
-
modelsById.set(model.id, model);
|
|
271
|
-
}
|
|
272
|
-
for (const model of cachedModels) {
|
|
273
|
-
const configuredModel = modelsById.get(model.id);
|
|
274
|
-
modelsById.set(
|
|
275
|
-
model.id,
|
|
276
|
-
configuredModel ? { ...model, ...configuredModel } : model,
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
return Array.from(modelsById.values());
|
|
280
|
-
}
|
|
281
|
-
|
|
282
262
|
function getProviderModelsForUi(args: {
|
|
283
|
-
providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>;
|
|
284
263
|
catalogModels: ModelInfo[] | undefined;
|
|
285
|
-
cfg: Awaited<ReturnType<typeof loadConfig>>;
|
|
286
264
|
provider: ProviderId;
|
|
287
265
|
authType: 'api' | 'oauth' | 'wallet' | undefined;
|
|
288
266
|
}): ModelInfo[] {
|
|
289
|
-
const configuredModels = getConfiguredProviderModels(args.cfg, args.provider);
|
|
290
267
|
const catalogModels = args.catalogModels ?? [];
|
|
291
|
-
|
|
292
|
-
return mergeConfiguredAndCachedModels(configuredModels, catalogModels);
|
|
293
|
-
}
|
|
294
|
-
if (catalogModels.length > 0) {
|
|
295
|
-
return filterModelsForAuthType(args.provider, catalogModels, args.authType);
|
|
296
|
-
}
|
|
297
|
-
return configuredModels;
|
|
268
|
+
return filterModelsForAuthType(args.provider, catalogModels, args.authType);
|
|
298
269
|
}
|
|
299
270
|
|
|
300
271
|
function getUiProviderLabel(
|
|
@@ -318,6 +289,9 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
318
289
|
|
|
319
290
|
const projectRoot = c.req.query('project') || process.cwd();
|
|
320
291
|
const cfg = await loadConfig(projectRoot);
|
|
292
|
+
const cachedCatalog = await readCachedModelCatalog();
|
|
293
|
+
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
294
|
+
const providerCatalog = modelCatalogProviders[provider];
|
|
321
295
|
|
|
322
296
|
const authorized = await isProviderAuthorizedHybrid(
|
|
323
297
|
embeddedConfig,
|
|
@@ -325,17 +299,13 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
325
299
|
provider,
|
|
326
300
|
);
|
|
327
301
|
|
|
328
|
-
if (!authorized) {
|
|
302
|
+
if (!authorized && !providerCatalog) {
|
|
329
303
|
logger.warn('Provider not authorized', { provider });
|
|
330
304
|
return c.json({ error: 'Provider not authorized' }, 403);
|
|
331
305
|
}
|
|
332
306
|
|
|
333
|
-
const cachedCatalog = await readCachedModelCatalog();
|
|
334
|
-
const providerCatalog =
|
|
335
|
-
cachedCatalog?.providers[provider] ??
|
|
336
|
-
catalog[provider as keyof typeof catalog];
|
|
337
307
|
const providerDefinition = getProviderDefinition(cfg, provider);
|
|
338
|
-
if (!providerDefinition) {
|
|
308
|
+
if (!providerDefinition && !providerCatalog) {
|
|
339
309
|
logger.warn('Provider not found in catalog', { provider });
|
|
340
310
|
return c.json({ error: 'Provider not found' }, 404);
|
|
341
311
|
}
|
|
@@ -346,7 +316,10 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
346
316
|
provider,
|
|
347
317
|
projectRoot,
|
|
348
318
|
);
|
|
349
|
-
if (
|
|
319
|
+
if (
|
|
320
|
+
providerDefinition &&
|
|
321
|
+
shouldLazyLoadProviderModels(providerDefinition)
|
|
322
|
+
) {
|
|
350
323
|
void refreshProviderModelsInBackground({
|
|
351
324
|
provider,
|
|
352
325
|
providerDefinition,
|
|
@@ -354,9 +327,7 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
354
327
|
});
|
|
355
328
|
}
|
|
356
329
|
const filteredModels = getProviderModelsForUi({
|
|
357
|
-
providerDefinition,
|
|
358
330
|
catalogModels: providerCatalog?.models,
|
|
359
|
-
cfg,
|
|
360
331
|
provider,
|
|
361
332
|
authType,
|
|
362
333
|
});
|
|
@@ -378,8 +349,12 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
378
349
|
embeddedConfig?.defaults?.model,
|
|
379
350
|
cfg.defaults.model,
|
|
380
351
|
),
|
|
381
|
-
allowAnyModel:
|
|
382
|
-
|
|
352
|
+
allowAnyModel: providerDefinition
|
|
353
|
+
? providerAllowsAnyModel(cfg, provider)
|
|
354
|
+
: undefined,
|
|
355
|
+
label: providerDefinition
|
|
356
|
+
? getUiProviderLabel(providerDefinition)
|
|
357
|
+
: (providerCatalog?.label ?? provider),
|
|
383
358
|
});
|
|
384
359
|
} catch (error) {
|
|
385
360
|
logger.error('Failed to get provider models', error);
|
|
@@ -405,24 +380,31 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
405
380
|
);
|
|
406
381
|
|
|
407
382
|
const cachedCatalog = await readCachedModelCatalog();
|
|
383
|
+
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
408
384
|
void refreshRemoteCatalogInBackground();
|
|
409
385
|
|
|
410
386
|
const modelsMap: Record<string, UiProviderModels> = {};
|
|
411
387
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
388
|
+
const cachedProviderIds = Object.keys(
|
|
389
|
+
modelCatalogProviders,
|
|
390
|
+
) as ProviderId[];
|
|
391
|
+
const modelProviders = Array.from(
|
|
392
|
+
new Set<ProviderId>([...authorizedProviders, ...cachedProviderIds]),
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
for (const provider of modelProviders) {
|
|
396
|
+
const providerCatalog = modelCatalogProviders[provider];
|
|
416
397
|
const providerDefinition = getProviderDefinition(cfg, provider);
|
|
417
|
-
if (
|
|
398
|
+
if (providerCatalog) {
|
|
418
399
|
const dynamicModels =
|
|
400
|
+
providerDefinition &&
|
|
419
401
|
shouldLazyLoadProviderModels(providerDefinition);
|
|
420
402
|
const authType = await getAuthTypeForProvider(
|
|
421
403
|
embeddedConfig,
|
|
422
404
|
provider,
|
|
423
405
|
projectRoot,
|
|
424
406
|
);
|
|
425
|
-
if (dynamicModels) {
|
|
407
|
+
if (dynamicModels && providerDefinition) {
|
|
426
408
|
void refreshProviderModelsInBackground({
|
|
427
409
|
provider,
|
|
428
410
|
providerDefinition,
|
|
@@ -430,16 +412,16 @@ export function registerModelsRoutes(app: Hono) {
|
|
|
430
412
|
});
|
|
431
413
|
}
|
|
432
414
|
const filteredModels = getProviderModelsForUi({
|
|
433
|
-
providerDefinition,
|
|
434
415
|
catalogModels: providerCatalog?.models,
|
|
435
|
-
cfg,
|
|
436
416
|
provider,
|
|
437
417
|
authType,
|
|
438
418
|
});
|
|
439
419
|
modelsMap[provider] = {
|
|
440
|
-
label:
|
|
420
|
+
label: providerDefinition
|
|
421
|
+
? getUiProviderLabel(providerDefinition)
|
|
422
|
+
: (providerCatalog.label ?? provider),
|
|
441
423
|
authType,
|
|
442
|
-
allowAnyModel: providerDefinition
|
|
424
|
+
allowAnyModel: providerDefinition?.allowAnyModel,
|
|
443
425
|
dynamicModels,
|
|
444
426
|
models: filteredModels.map(toUiModel),
|
|
445
427
|
};
|
|
@@ -31,7 +31,6 @@ type ProviderMutationBody = {
|
|
|
31
31
|
apiKeyEnv?: string | null;
|
|
32
32
|
models?: string[];
|
|
33
33
|
allowAnyModel?: boolean;
|
|
34
|
-
scope?: 'global' | 'local';
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
type ProviderDiscoveryBody = {
|
|
@@ -165,7 +164,6 @@ export function registerProvidersRoute(app: Hono) {
|
|
|
165
164
|
const projectRoot = c.req.query('project') || process.cwd();
|
|
166
165
|
const provider = c.req.param('provider').trim();
|
|
167
166
|
const body = await c.req.json<ProviderMutationBody>();
|
|
168
|
-
const scope = body.scope || 'local';
|
|
169
167
|
if (!provider) return c.json({ error: 'Provider is required' }, 400);
|
|
170
168
|
|
|
171
169
|
const updates: ProviderSettingsEntry = {
|
|
@@ -202,7 +200,7 @@ export function registerProvidersRoute(app: Hono) {
|
|
|
202
200
|
return c.json({ error: 'Custom providers require compatibility' }, 400);
|
|
203
201
|
}
|
|
204
202
|
|
|
205
|
-
await writeProviderSettings(
|
|
203
|
+
await writeProviderSettings('global', provider, updates, projectRoot);
|
|
206
204
|
const cfg = await loadConfig(projectRoot);
|
|
207
205
|
const details = await getProviderDetails(undefined, cfg);
|
|
208
206
|
return c.json({
|
|
@@ -230,11 +228,9 @@ export function registerProvidersRoute(app: Hono) {
|
|
|
230
228
|
|
|
231
229
|
const projectRoot = c.req.query('project') || process.cwd();
|
|
232
230
|
const provider = c.req.param('provider').trim();
|
|
233
|
-
const scope =
|
|
234
|
-
(c.req.query('scope') as 'global' | 'local' | undefined) || 'local';
|
|
235
231
|
if (!provider) return c.json({ error: 'Provider is required' }, 400);
|
|
236
232
|
|
|
237
|
-
await removeProviderSettings(
|
|
233
|
+
await removeProviderSettings('global', provider, projectRoot);
|
|
238
234
|
const cfg = await loadConfig(projectRoot);
|
|
239
235
|
const details = await getProviderDetails(undefined, cfg);
|
|
240
236
|
return c.json({ success: true, provider, details });
|
|
@@ -10,10 +10,12 @@ const FILE_EDIT_TOOLS = [
|
|
|
10
10
|
'Write',
|
|
11
11
|
'Edit',
|
|
12
12
|
'MultiEdit',
|
|
13
|
+
'CopyInto',
|
|
13
14
|
'ApplyPatch',
|
|
14
15
|
'write',
|
|
15
16
|
'edit',
|
|
16
17
|
'multiedit',
|
|
18
|
+
'copy_into',
|
|
17
19
|
'apply_patch',
|
|
18
20
|
];
|
|
19
21
|
|
|
@@ -42,6 +44,7 @@ interface SessionFile {
|
|
|
42
44
|
|
|
43
45
|
interface ToolResultData {
|
|
44
46
|
path?: string;
|
|
47
|
+
targetPath?: string;
|
|
45
48
|
args?: Record<string, unknown>;
|
|
46
49
|
files?: Array<string | { path: string }>;
|
|
47
50
|
result?: {
|
|
@@ -75,6 +78,10 @@ function extractFilePathFromToolCall(
|
|
|
75
78
|
if (args && typeof args.path === 'string') return args.path;
|
|
76
79
|
if (typeof c.path === 'string') return c.path;
|
|
77
80
|
}
|
|
81
|
+
if (name === 'copyinto' || name === 'copy_into') {
|
|
82
|
+
if (args && typeof args.targetPath === 'string') return args.targetPath;
|
|
83
|
+
if (typeof c.targetPath === 'string') return c.targetPath;
|
|
84
|
+
}
|
|
78
85
|
|
|
79
86
|
if (name === 'applypatch' || name === 'apply_patch') {
|
|
80
87
|
const patch = args?.patch ?? c.patch;
|
|
@@ -146,6 +153,16 @@ function extractFilesFromToolResult(
|
|
|
146
153
|
if (args && typeof args.path === 'string' && !files.includes(args.path)) {
|
|
147
154
|
files.push(args.path);
|
|
148
155
|
}
|
|
156
|
+
if (
|
|
157
|
+
args &&
|
|
158
|
+
typeof args.targetPath === 'string' &&
|
|
159
|
+
!files.includes(args.targetPath)
|
|
160
|
+
) {
|
|
161
|
+
files.push(args.targetPath);
|
|
162
|
+
}
|
|
163
|
+
if (typeof c.targetPath === 'string' && !files.includes(c.targetPath)) {
|
|
164
|
+
files.push(c.targetPath);
|
|
165
|
+
}
|
|
149
166
|
|
|
150
167
|
if (Array.isArray(c.files)) {
|
|
151
168
|
for (const f of c.files) {
|
|
@@ -199,7 +216,7 @@ function extractDataFromToolResult(
|
|
|
199
216
|
}
|
|
200
217
|
|
|
201
218
|
if (
|
|
202
|
-
(name === 'edit' || name === 'multiedit') &&
|
|
219
|
+
(name === 'edit' || name === 'multiedit' || name === 'copy_into') &&
|
|
203
220
|
typeof c.result?.artifact?.patch === 'string'
|
|
204
221
|
) {
|
|
205
222
|
patch = c.result.artifact.patch;
|
|
@@ -229,7 +246,8 @@ function extractDataFromToolResult(
|
|
|
229
246
|
function getOperationType(toolName: string): 'write' | 'patch' | 'create' {
|
|
230
247
|
const name = toolName.toLowerCase();
|
|
231
248
|
if (name === 'write') return 'write';
|
|
232
|
-
if (name === 'edit' || name === 'multiedit'
|
|
249
|
+
if (name === 'edit' || name === 'multiedit' || name === 'copy_into')
|
|
250
|
+
return 'patch';
|
|
233
251
|
if (name === 'applypatch' || name === 'apply_patch') return 'patch';
|
|
234
252
|
return 'write';
|
|
235
253
|
}
|
package/src/routes/skills.ts
CHANGED
|
@@ -97,10 +97,9 @@ export function registerSkillsRoutes(app: Hono) {
|
|
|
97
97
|
const body = await c.req.json<{
|
|
98
98
|
enabled?: boolean;
|
|
99
99
|
items?: Record<string, { enabled?: boolean }>;
|
|
100
|
-
scope?: 'global' | 'local';
|
|
101
100
|
}>();
|
|
102
101
|
await writeSkillSettings(
|
|
103
|
-
|
|
102
|
+
'global',
|
|
104
103
|
{
|
|
105
104
|
...(body.enabled !== undefined ? { enabled: body.enabled } : {}),
|
|
106
105
|
...(body.items ? { items: body.items } : {}),
|
|
@@ -124,6 +124,7 @@ const defaultToolExtras: Record<string, string[]> = {
|
|
|
124
124
|
'edit',
|
|
125
125
|
'multiedit',
|
|
126
126
|
'write',
|
|
127
|
+
'copy_into',
|
|
127
128
|
'ls',
|
|
128
129
|
'tree',
|
|
129
130
|
'shell',
|
|
@@ -142,6 +143,7 @@ const defaultToolExtras: Record<string, string[]> = {
|
|
|
142
143
|
'edit',
|
|
143
144
|
'multiedit',
|
|
144
145
|
'write',
|
|
146
|
+
'copy_into',
|
|
145
147
|
'ls',
|
|
146
148
|
'tree',
|
|
147
149
|
'shell',
|
|
@@ -156,6 +158,7 @@ const defaultToolExtras: Record<string, string[]> = {
|
|
|
156
158
|
'edit',
|
|
157
159
|
'multiedit',
|
|
158
160
|
'write',
|
|
161
|
+
'copy_into',
|
|
159
162
|
'ls',
|
|
160
163
|
'tree',
|
|
161
164
|
'shell',
|
|
@@ -115,7 +115,13 @@ export function mergeProviderOptions(
|
|
|
115
115
|
return base;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const EDITING_TOOL_NAMES = [
|
|
118
|
+
const EDITING_TOOL_NAMES = [
|
|
119
|
+
'edit',
|
|
120
|
+
'multiedit',
|
|
121
|
+
'write',
|
|
122
|
+
'copy_into',
|
|
123
|
+
'apply_patch',
|
|
124
|
+
];
|
|
119
125
|
const MODEL_FAMILY_EDIT_TOOL_POLICY_AGENTS = new Set([
|
|
120
126
|
'build',
|
|
121
127
|
'general',
|
|
@@ -148,8 +154,8 @@ export function applyModelFamilyEditToolPolicy(
|
|
|
148
154
|
);
|
|
149
155
|
const preferredEditingTools =
|
|
150
156
|
family === 'anthropic' || family === 'openai'
|
|
151
|
-
? ['write', 'apply_patch']
|
|
152
|
-
: ['write', 'edit', 'multiedit'];
|
|
157
|
+
? ['write', 'copy_into', 'apply_patch']
|
|
158
|
+
: ['write', 'edit', 'multiedit', 'copy_into'];
|
|
153
159
|
|
|
154
160
|
return Array.from(new Set([...next, ...preferredEditingTools]));
|
|
155
161
|
}
|
|
@@ -25,6 +25,7 @@ export function guardToolCall(
|
|
|
25
25
|
case 'read':
|
|
26
26
|
return guardReadPath(String(a.path ?? ''), context.projectRoot);
|
|
27
27
|
case 'write':
|
|
28
|
+
case 'copy_into':
|
|
28
29
|
return guardWritePath(toolName, a);
|
|
29
30
|
default:
|
|
30
31
|
return { type: 'allow' };
|
|
@@ -193,9 +194,11 @@ function guardWritePath(
|
|
|
193
194
|
const path =
|
|
194
195
|
typeof args.path === 'string'
|
|
195
196
|
? args.path
|
|
196
|
-
: typeof args.
|
|
197
|
-
? args.
|
|
198
|
-
: ''
|
|
197
|
+
: typeof args.targetPath === 'string'
|
|
198
|
+
? args.targetPath
|
|
199
|
+
: typeof args.filePath === 'string'
|
|
200
|
+
? args.filePath
|
|
201
|
+
: '';
|
|
199
202
|
if (!path) return { type: 'allow' };
|
|
200
203
|
const p = path.trim();
|
|
201
204
|
|
|
@@ -20,6 +20,7 @@ export const CANONICAL_TO_PASCAL: Record<string, string> = {
|
|
|
20
20
|
edit: 'Edit',
|
|
21
21
|
multiedit: 'MultiEdit',
|
|
22
22
|
write: 'Write',
|
|
23
|
+
copy_into: 'CopyInto',
|
|
23
24
|
ls: 'Ls',
|
|
24
25
|
tree: 'Tree',
|
|
25
26
|
cd: 'Cd',
|
|
@@ -61,6 +62,7 @@ export const PASCAL_TO_CANONICAL: Record<string, string> = {
|
|
|
61
62
|
Edit: 'edit',
|
|
62
63
|
MultiEdit: 'multiedit',
|
|
63
64
|
Write: 'write',
|
|
65
|
+
CopyInto: 'copy_into',
|
|
64
66
|
Ls: 'ls',
|
|
65
67
|
Tree: 'tree',
|
|
66
68
|
Cd: 'cd',
|