@myagentroam/node 0.9.114 → 0.9.116
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.
|
@@ -4,6 +4,7 @@ import { declaredRunnerProfiles } from '../runner-profiles.js';
|
|
|
4
4
|
import { AbstractRunner } from './abstract-runner.js';
|
|
5
5
|
import { isWithinWorkspace } from './codex/conversation-parser.js';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
7
8
|
import { openCodeConversationPage } from './opencode/conversation-parser.js';
|
|
8
9
|
import { nodeLog } from '../operational.js';
|
|
9
10
|
const PROFILE_ENTRY_TTL_MS = 30 * 60_000;
|
|
@@ -12,9 +13,8 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
12
13
|
clientFactory;
|
|
13
14
|
name = 'opencode';
|
|
14
15
|
discoveryScope = 'WORKSPACE_REQUIRED';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
profileTimersByWorkspace = new Map();
|
|
16
|
+
contextLimits = new Map();
|
|
17
|
+
contextLimitsExpiresAt = 0;
|
|
18
18
|
profileCache = new Map();
|
|
19
19
|
profileRefreshes = new Map();
|
|
20
20
|
execution = new Map();
|
|
@@ -34,11 +34,11 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
34
34
|
}).find((profile) => profile.runner === this.name);
|
|
35
35
|
return { ...base, models: [] };
|
|
36
36
|
}
|
|
37
|
-
async refreshProfile(capabilities,
|
|
38
|
-
if (capabilities.openCode?.available !== true
|
|
37
|
+
async refreshProfile(capabilities, _workspace, environment = {}) {
|
|
38
|
+
if (capabilities.openCode?.available !== true) {
|
|
39
39
|
return this.profile(capabilities);
|
|
40
40
|
}
|
|
41
|
-
const cacheKey = this.
|
|
41
|
+
const cacheKey = this.environmentFingerprint(environment);
|
|
42
42
|
const now = Date.now();
|
|
43
43
|
for (const [key, entry] of this.profileCache)
|
|
44
44
|
if (entry.expiresAt <= now)
|
|
@@ -46,46 +46,42 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
46
46
|
const cached = this.profileCache.get(cacheKey);
|
|
47
47
|
if (cached !== undefined) {
|
|
48
48
|
nodeLog('runner.opencode.profile.cache-hit', {
|
|
49
|
-
workspaceId: workspace.id,
|
|
50
49
|
models: cached.models.length,
|
|
51
50
|
remainingTtlMs: cached.expiresAt - now
|
|
52
51
|
});
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
55
|
-
this.scheduleProfileExpiry(workspace.path);
|
|
52
|
+
this.contextLimits = cached.contextLimits;
|
|
53
|
+
this.contextLimitsExpiresAt = cached.expiresAt;
|
|
56
54
|
return { ...this.profile(capabilities), models: cached.models };
|
|
57
55
|
}
|
|
58
56
|
const current = this.profileRefreshes.get(cacheKey);
|
|
59
57
|
if (current !== undefined) {
|
|
60
|
-
nodeLog('runner.opencode.profile.single-flight-joined', {
|
|
58
|
+
nodeLog('runner.opencode.profile.single-flight-joined', {});
|
|
61
59
|
return current;
|
|
62
60
|
}
|
|
63
|
-
const refresh = this.fetchProfile(capabilities,
|
|
61
|
+
const refresh = this.fetchProfile(capabilities, environment, cacheKey).finally(() => {
|
|
64
62
|
if (this.profileRefreshes.get(cacheKey) === refresh)
|
|
65
63
|
this.profileRefreshes.delete(cacheKey);
|
|
66
64
|
});
|
|
67
65
|
this.profileRefreshes.set(cacheKey, refresh);
|
|
68
66
|
return refresh;
|
|
69
67
|
}
|
|
70
|
-
async fetchProfile(capabilities,
|
|
68
|
+
async fetchProfile(capabilities, environment, cacheKey) {
|
|
71
69
|
const client = this.managedClient(environment);
|
|
72
70
|
const startedAt = performance.now();
|
|
73
71
|
try {
|
|
74
|
-
const catalog = await client.providers(
|
|
72
|
+
const catalog = await client.providers(homedir());
|
|
75
73
|
const models = openCodeProfileModels(catalog);
|
|
76
74
|
const contextLimits = openCodeContextLimits(catalog);
|
|
77
|
-
|
|
78
|
-
this.
|
|
75
|
+
const expiresAt = Date.now() + PROFILE_ENTRY_TTL_MS;
|
|
76
|
+
this.contextLimits = contextLimits;
|
|
77
|
+
this.contextLimitsExpiresAt = expiresAt;
|
|
79
78
|
this.profileCache.set(cacheKey, {
|
|
80
|
-
expiresAt
|
|
81
|
-
|
|
82
|
-
environmentFingerprint: this.environmentFingerprint(environment),
|
|
79
|
+
expiresAt,
|
|
80
|
+
environmentFingerprint: cacheKey,
|
|
83
81
|
models,
|
|
84
82
|
contextLimits
|
|
85
83
|
});
|
|
86
|
-
this.scheduleProfileExpiry(workspace.path);
|
|
87
84
|
nodeLog('runner.opencode.profile.providers.completed', {
|
|
88
|
-
workspaceId: workspace.id,
|
|
89
85
|
models: models.length,
|
|
90
86
|
durationMs: Math.round(performance.now() - startedAt)
|
|
91
87
|
});
|
|
@@ -93,7 +89,6 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
93
89
|
}
|
|
94
90
|
catch {
|
|
95
91
|
nodeLog('runner.opencode.profile.providers.failed', {
|
|
96
|
-
workspaceId: workspace.id,
|
|
97
92
|
durationMs: Math.round(performance.now() - startedAt)
|
|
98
93
|
});
|
|
99
94
|
}
|
|
@@ -102,11 +97,6 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
102
97
|
}
|
|
103
98
|
return this.profile(capabilities);
|
|
104
99
|
}
|
|
105
|
-
profileCacheKey(workspacePath, environment) {
|
|
106
|
-
return createHash('sha256')
|
|
107
|
-
.update(JSON.stringify({ workspacePath, environment: this.environmentFingerprint(environment) }))
|
|
108
|
-
.digest('hex');
|
|
109
|
-
}
|
|
110
100
|
environmentFingerprint(environment) {
|
|
111
101
|
const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
|
|
112
102
|
return createHash('sha256').update(JSON.stringify(entries)).digest('hex');
|
|
@@ -125,18 +115,18 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
125
115
|
openCode: { available: true }
|
|
126
116
|
});
|
|
127
117
|
}
|
|
128
|
-
supportsConfiguration(configuration,
|
|
118
|
+
supportsConfiguration(configuration, _workspace, environment = {}) {
|
|
129
119
|
if (configuration.runner !== this.name)
|
|
130
120
|
return false;
|
|
131
|
-
const models = this.cachedModels(
|
|
121
|
+
const models = this.cachedModels(environment);
|
|
132
122
|
const model = models.find((candidate) => candidate.id === configuration.model);
|
|
133
123
|
return (model !== undefined &&
|
|
134
124
|
(model.supportedEfforts.length === 0 ||
|
|
135
125
|
model.supportedEfforts.includes(configuration.effort)) &&
|
|
136
126
|
this.profileForValidation().accessOptions.some((candidate) => candidate.id === configuration.access));
|
|
137
127
|
}
|
|
138
|
-
defaultConfiguration(
|
|
139
|
-
const model = this.cachedModels(
|
|
128
|
+
defaultConfiguration(_workspace, environment = {}) {
|
|
129
|
+
const model = this.cachedModels(environment)[0];
|
|
140
130
|
return {
|
|
141
131
|
runner: this.name,
|
|
142
132
|
model: model?.id ?? '',
|
|
@@ -144,13 +134,11 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
144
134
|
access: 'default'
|
|
145
135
|
};
|
|
146
136
|
}
|
|
147
|
-
cachedModels(
|
|
137
|
+
cachedModels(environment) {
|
|
148
138
|
const fingerprint = this.environmentFingerprint(environment);
|
|
149
139
|
const now = Date.now();
|
|
150
140
|
return [...this.profileCache.values()]
|
|
151
|
-
.filter((entry) => entry.expiresAt > now &&
|
|
152
|
-
entry.environmentFingerprint === fingerprint &&
|
|
153
|
-
(workspace === undefined || entry.workspacePath === workspace.path))
|
|
141
|
+
.filter((entry) => entry.expiresAt > now && entry.environmentFingerprint === fingerprint)
|
|
154
142
|
.flatMap((entry) => entry.models);
|
|
155
143
|
}
|
|
156
144
|
mcpConfiguration(raw, secrets) {
|
|
@@ -306,24 +294,20 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
306
294
|
const cache = object(tokens?.cache);
|
|
307
295
|
const usedTokens = positiveNumber(tokens?.input) + positiveNumber(cache?.read);
|
|
308
296
|
const modelId = provider !== undefined && model !== undefined ? `${provider}/${model}` : session.model;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
: { model: modelId, usedTokens, maxTokens };
|
|
297
|
+
if (modelId === null || usedTokens <= 0)
|
|
298
|
+
return undefined;
|
|
299
|
+
const maxTokens = Date.now() >= this.contextLimitsExpiresAt ? undefined : this.contextLimits.get(modelId);
|
|
300
|
+
return maxTokens === undefined ? undefined : { model: modelId, usedTokens, maxTokens };
|
|
314
301
|
}
|
|
315
302
|
async stop() {
|
|
316
303
|
for (const execution of this.execution.values())
|
|
317
304
|
execution.abort.abort();
|
|
318
305
|
this.execution.clear();
|
|
319
306
|
this.managedTurns.clear();
|
|
320
|
-
for (const timer of this.profileTimersByWorkspace.values())
|
|
321
|
-
clearTimeout(timer);
|
|
322
|
-
this.profileTimersByWorkspace.clear();
|
|
323
307
|
this.profileCache.clear();
|
|
324
308
|
this.profileRefreshes.clear();
|
|
325
|
-
this.
|
|
326
|
-
this.
|
|
309
|
+
this.contextLimits = new Map();
|
|
310
|
+
this.contextLimitsExpiresAt = 0;
|
|
327
311
|
const stopping = [
|
|
328
312
|
this.client.stop(),
|
|
329
313
|
...[...this.environmentClients.values()].map(({ client }) => client.stop()),
|
|
@@ -333,22 +317,6 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
333
317
|
await Promise.allSettled(stopping);
|
|
334
318
|
this.stoppingClients.clear();
|
|
335
319
|
}
|
|
336
|
-
scheduleProfileExpiry(workspacePath) {
|
|
337
|
-
this.clearProfileTimer(workspacePath);
|
|
338
|
-
const timer = setTimeout(() => {
|
|
339
|
-
this.profileTimersByWorkspace.delete(workspacePath);
|
|
340
|
-
this.modelsByWorkspace.delete(workspacePath);
|
|
341
|
-
this.contextLimitsByWorkspace.delete(workspacePath);
|
|
342
|
-
}, PROFILE_ENTRY_TTL_MS);
|
|
343
|
-
timer.unref();
|
|
344
|
-
this.profileTimersByWorkspace.set(workspacePath, timer);
|
|
345
|
-
}
|
|
346
|
-
clearProfileTimer(workspacePath) {
|
|
347
|
-
const timer = this.profileTimersByWorkspace.get(workspacePath);
|
|
348
|
-
if (timer !== undefined)
|
|
349
|
-
clearTimeout(timer);
|
|
350
|
-
this.profileTimersByWorkspace.delete(workspacePath);
|
|
351
|
-
}
|
|
352
320
|
managedClient(environment, isolationKey) {
|
|
353
321
|
const entries = Object.entries(environment).sort(([left], [right]) => left.localeCompare(right));
|
|
354
322
|
if (entries.length === 0 && isolationKey === undefined)
|
|
@@ -22,11 +22,8 @@ export class RunnerService {
|
|
|
22
22
|
profiles() {
|
|
23
23
|
return this.runners.profiles(this.capabilities());
|
|
24
24
|
}
|
|
25
|
-
refreshProfiles(
|
|
26
|
-
|
|
27
|
-
if (workspaceId !== undefined && workspace === undefined)
|
|
28
|
-
throw new Error('WORKSPACE_NOT_FOUND');
|
|
29
|
-
return this.runners.refreshProfiles(this.capabilities(), workspace, environment);
|
|
25
|
+
refreshProfiles(environment = {}) {
|
|
26
|
+
return this.runners.refreshProfiles(this.capabilities(), undefined, environment);
|
|
30
27
|
}
|
|
31
28
|
async usageStatuses() {
|
|
32
29
|
return {
|
|
@@ -51,9 +48,6 @@ export class RunnerService {
|
|
|
51
48
|
'configureModels' in marAgent &&
|
|
52
49
|
typeof marAgent.configureModels === 'function')
|
|
53
50
|
marAgent.configureModels(input.marAgentModels);
|
|
54
|
-
const workspaceId = typeof input.workspaceId === 'string' && input.workspaceId.length > 0
|
|
55
|
-
? input.workspaceId
|
|
56
|
-
: undefined;
|
|
57
51
|
const environment = parseSecretEnvironment(input.secretEnvironment);
|
|
58
52
|
const defaults = this.database()
|
|
59
53
|
.getRunnerDefaults()
|
|
@@ -68,7 +62,7 @@ export class RunnerService {
|
|
|
68
62
|
return normalized;
|
|
69
63
|
});
|
|
70
64
|
return {
|
|
71
|
-
profiles: await this.refreshProfiles(
|
|
65
|
+
profiles: await this.refreshProfiles(environment),
|
|
72
66
|
defaults
|
|
73
67
|
};
|
|
74
68
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tmpdir } from 'node:os';
|
|
2
|
-
import { isAbsolute, win32 } from 'node:path';
|
|
2
|
+
import { isAbsolute, relative, resolve, win32 } from 'node:path';
|
|
3
|
+
import { realpath } from 'node:fs/promises';
|
|
3
4
|
import { listWorkspaceFiles, mutateWorkspaceFile, normalizeFilePreviewPath, readAllowedFileContentRange, readWorkspaceFileContentRange, readAllowedTextFile, readWorkspaceTextFile, writeWorkspaceTextFile, WorkspaceFileIndex } from '../workspace.js';
|
|
4
5
|
const INDEX_CACHE_LIMIT = 32;
|
|
5
6
|
export class WorkspaceFileService {
|
|
@@ -52,6 +53,19 @@ export class WorkspaceFileService {
|
|
|
52
53
|
const path = normalizeFilePreviewPath(input.path);
|
|
53
54
|
if (workspacePath !== workspace.path && isFilePreviewAbsolutePath(path))
|
|
54
55
|
throw new Error('FILE_PATH_ESCAPE');
|
|
56
|
+
if (input.previewDirectory !== undefined) {
|
|
57
|
+
if (typeof input.previewDirectory !== 'string' ||
|
|
58
|
+
isFilePreviewAbsolutePath(path) ||
|
|
59
|
+
isFilePreviewAbsolutePath(input.previewDirectory))
|
|
60
|
+
throw new Error('FILE_PATH_ESCAPE');
|
|
61
|
+
const root = await realpath(resolve(workspacePath, input.previewDirectory));
|
|
62
|
+
const target = await realpath(resolve(workspacePath, path));
|
|
63
|
+
const suffix = relative(root, target);
|
|
64
|
+
if (suffix === '..' ||
|
|
65
|
+
suffix.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) ||
|
|
66
|
+
isAbsolute(suffix))
|
|
67
|
+
throw new Error('FILE_PATH_ESCAPE');
|
|
68
|
+
}
|
|
55
69
|
const offset = typeof input.offset === 'number' ? input.offset : 0;
|
|
56
70
|
const limit = typeof input.limit === 'number' ? input.limit : 512 * 1024;
|
|
57
71
|
return isFilePreviewAbsolutePath(path)
|
|
@@ -150,7 +150,8 @@ export class WorkspaceWorkbenchService {
|
|
|
150
150
|
const workspace = this.options.requireWorkspace(input.workspaceId);
|
|
151
151
|
const repository = await this.fileRepository(workspace, input);
|
|
152
152
|
return {
|
|
153
|
-
file: await this.options.files.readContent(workspace, input, repository)
|
|
153
|
+
file: await this.options.files.readContent(workspace, input, repository),
|
|
154
|
+
...(input.previewDirectory === undefined ? {} : { previewBoundaryVerified: true })
|
|
154
155
|
};
|
|
155
156
|
}
|
|
156
157
|
async directFileMetadata(input) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.116",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"npm": "11.12.1",
|
|
31
31
|
"ws": "^8.21.3",
|
|
32
32
|
"zod": "4.4.3",
|
|
33
|
-
"@myagentroam/
|
|
34
|
-
"@myagentroam/
|
|
35
|
-
"@myagentroam/
|
|
33
|
+
"@myagentroam/protocol": "0.9.116",
|
|
34
|
+
"@myagentroam/agent": "0.9.116",
|
|
35
|
+
"@myagentroam/code-navigation": "0.9.116"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/ws": "^8.18.1"
|