@moderniser/plugin 0.1.3 → 0.1.5
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/client.d.ts +1 -10
- package/dist/context.d.ts +1 -0
- package/dist/index.js +86 -274
- package/package.json +2 -2
- package/dist/tools-boards.d.ts +0 -2
- package/dist/tools-context-hub.d.ts +0 -2
package/dist/client.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
export declare const API: () => string;
|
|
2
|
-
export declare class ApiError extends Error {
|
|
3
|
-
status: number;
|
|
4
|
-
constructor(status: number, message: string);
|
|
5
|
-
}
|
|
6
2
|
export declare function setStoredKey(key: string): void;
|
|
7
3
|
export declare function storedKey(): string | undefined;
|
|
8
|
-
export declare function
|
|
9
|
-
export declare const get: <T>(path: string) => Promise<T>;
|
|
10
|
-
export declare const post: <T>(path: string, body: unknown) => Promise<T>;
|
|
11
|
-
export declare const patch: <T>(path: string, body: unknown) => Promise<T>;
|
|
12
|
-
export declare const del: <T>(path: string) => Promise<T>;
|
|
13
|
-
export declare const json: (value: unknown) => string;
|
|
4
|
+
export declare function resolveKey(): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function moderniserContext(): string;
|
package/dist/index.js
CHANGED
|
@@ -6,15 +6,6 @@ import { readFileSync } from "node:fs";
|
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
var API = () => (process.env.MODERNISER_API_URL ?? "https://v2.moderniser.ai/api").replace(/\/+$/, "");
|
|
9
|
-
var BASE = API;
|
|
10
|
-
|
|
11
|
-
class ApiError extends Error {
|
|
12
|
-
status;
|
|
13
|
-
constructor(status, message) {
|
|
14
|
-
super(message);
|
|
15
|
-
this.status = status;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
9
|
var stored;
|
|
19
10
|
function setStoredKey(key) {
|
|
20
11
|
stored = key;
|
|
@@ -31,61 +22,18 @@ function fromAuthFile() {
|
|
|
31
22
|
if (fileKey !== undefined)
|
|
32
23
|
return fileKey ?? undefined;
|
|
33
24
|
fileKey = null;
|
|
34
|
-
let raw;
|
|
35
|
-
try {
|
|
36
|
-
raw = readFileSync(authFilePath(), "utf8");
|
|
37
|
-
} catch {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
25
|
try {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return key;
|
|
49
|
-
} catch {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
26
|
+
const parsed = JSON.parse(readFileSync(authFilePath(), "utf8"));
|
|
27
|
+
const entry = parsed?.moderniser;
|
|
28
|
+
if (typeof entry === "object" && entry !== null && entry.type === "api" && typeof entry.key === "string" && entry.key.length > 0) {
|
|
29
|
+
fileKey = entry.key;
|
|
30
|
+
}
|
|
31
|
+
} catch {}
|
|
32
|
+
return fileKey ?? undefined;
|
|
52
33
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
if (!value) {
|
|
56
|
-
throw new Error(`Not signed in to Moderniser. Run: moderniser providers login
|
|
57
|
-
` + "(or set MODERNISER_API_KEY to a token from Settings in the web app).");
|
|
58
|
-
}
|
|
59
|
-
return value;
|
|
34
|
+
function resolveKey() {
|
|
35
|
+
return process.env.MODERNISER_API_KEY ?? stored ?? fromAuthFile();
|
|
60
36
|
}
|
|
61
|
-
async function api(path, init = {}) {
|
|
62
|
-
const bearer = token();
|
|
63
|
-
let res;
|
|
64
|
-
try {
|
|
65
|
-
res = await fetch(`${BASE()}${path}`, {
|
|
66
|
-
...init,
|
|
67
|
-
headers: {
|
|
68
|
-
"Content-Type": "application/json",
|
|
69
|
-
Authorization: `Bearer ${bearer}`,
|
|
70
|
-
...init.headers
|
|
71
|
-
},
|
|
72
|
-
signal: AbortSignal.timeout(30000)
|
|
73
|
-
});
|
|
74
|
-
} catch (error) {
|
|
75
|
-
throw new ApiError(0, `Could not reach Moderniser at ${BASE()}: ${error.message}`);
|
|
76
|
-
}
|
|
77
|
-
if (res.status === 204)
|
|
78
|
-
return;
|
|
79
|
-
const body = await res.json().catch(() => ({}));
|
|
80
|
-
if (!res.ok) {
|
|
81
|
-
throw new ApiError(res.status, body.error ?? `Request failed with ${res.status}`);
|
|
82
|
-
}
|
|
83
|
-
return body;
|
|
84
|
-
}
|
|
85
|
-
var get = (path) => api(path);
|
|
86
|
-
var post = (path, body) => api(path, { method: "POST", body: JSON.stringify(body) });
|
|
87
|
-
var patch = (path, body) => api(path, { method: "PATCH", body: JSON.stringify(body) });
|
|
88
|
-
var json = (value) => JSON.stringify(value, null, 2);
|
|
89
37
|
|
|
90
38
|
// packages/plugin/src/auth.ts
|
|
91
39
|
async function start() {
|
|
@@ -141,14 +89,14 @@ function moderniserAuth() {
|
|
|
141
89
|
const started = await start();
|
|
142
90
|
return {
|
|
143
91
|
url: started.verificationUrl,
|
|
144
|
-
instructions: `Check that your browser shows this code: ${started.userCode}
|
|
92
|
+
instructions: `Check that your browser shows this code: ${started.userCode}. After signing in for the first time, restart to pick up the Moderniser tools.`,
|
|
145
93
|
method: "auto",
|
|
146
94
|
async callback() {
|
|
147
|
-
const
|
|
148
|
-
if (!
|
|
95
|
+
const token = await poll(started.deviceCode, started.expiresIn, started.interval);
|
|
96
|
+
if (!token)
|
|
149
97
|
return { type: "failed" };
|
|
150
|
-
setStoredKey(
|
|
151
|
-
return { type: "success", key:
|
|
98
|
+
setStoredKey(token);
|
|
99
|
+
return { type: "success", key: token };
|
|
152
100
|
}
|
|
153
101
|
};
|
|
154
102
|
}
|
|
@@ -166,211 +114,33 @@ function moderniserAuth() {
|
|
|
166
114
|
};
|
|
167
115
|
}
|
|
168
116
|
|
|
169
|
-
// packages/plugin/src/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
117
|
+
// packages/plugin/src/context.ts
|
|
118
|
+
function moderniserContext() {
|
|
119
|
+
const lines = [
|
|
120
|
+
"You are the Moderniser agent — part of a platform for modernising enterprise " + "systems. Moderniser keeps a board (projects containing tasks containing " + "subtasks) and a Context Hub (the company's own wiki, searchable and citable).",
|
|
121
|
+
"",
|
|
122
|
+
"Vocabulary that means something specific here, not the generic sense:",
|
|
123
|
+
"- Project: an area of work on the board. Not a folder, not a repo scaffold.",
|
|
124
|
+
"- Task: one unit of work a team would plan together.",
|
|
125
|
+
"- Subtask: one concrete change in one codebase, small enough to finish and check off.",
|
|
126
|
+
"",
|
|
127
|
+
"When the user says project, task, board or ticket, they mean Moderniser's — " + "reach for the board tools (moderniser_list_projects first, then " + "moderniser_create_task / moderniser_update_task / moderniser_create_subtask / …). " + "Do not answer by scaffolding files or writing a spec document instead, " + "unless a document is what they actually asked for."
|
|
128
|
+
];
|
|
129
|
+
const workspace = process.env.MODERNISER_WORKSPACE_NAME;
|
|
130
|
+
const company = process.env.MODERNISER_COMPANY_NAME;
|
|
131
|
+
const repo = process.env.REPO_URL;
|
|
132
|
+
if (workspace || company || repo) {
|
|
133
|
+
lines.push("", "Where you are right now:");
|
|
134
|
+
if (workspace)
|
|
135
|
+
lines.push(`- Workspace: ${workspace}`);
|
|
136
|
+
if (company)
|
|
137
|
+
lines.push(`- Company: ${company}`);
|
|
138
|
+
if (repo)
|
|
139
|
+
lines.push(`- Repository: ${repo}`);
|
|
178
140
|
}
|
|
179
|
-
return
|
|
141
|
+
return lines.join(`
|
|
142
|
+
`);
|
|
180
143
|
}
|
|
181
|
-
var boardTools = {
|
|
182
|
-
list_projects: tool({
|
|
183
|
-
description: "List the projects belonging to the caller's company. Call this first — every task belongs to a project, and you need a project id before you can create one.",
|
|
184
|
-
args: {},
|
|
185
|
-
async execute() {
|
|
186
|
-
return json((await get("/projects")).projects);
|
|
187
|
-
}
|
|
188
|
-
}),
|
|
189
|
-
create_project: tool({
|
|
190
|
-
description: "Create a project. A project is an area of work, and it may span several codebases — a checkout modernisation touches the storefront and the system that receives the order. Check list_projects first, since a duplicate fragments the board.",
|
|
191
|
-
args: {
|
|
192
|
-
name: z.string().describe("Short name, e.g. 'Checkout modernisation'"),
|
|
193
|
-
description: z.string().describe("What this covers and why it is being worked on").optional(),
|
|
194
|
-
keyPrefix: z.string().regex(/^[A-Z]{3}$/).describe("Three uppercase letters used to key tasks, e.g. 'CAD' gives CAD-1. Omit it and one is derived from the name.").optional()
|
|
195
|
-
},
|
|
196
|
-
async execute(args) {
|
|
197
|
-
return json((await post("/projects", args)).project);
|
|
198
|
-
}
|
|
199
|
-
}),
|
|
200
|
-
list_tasks: tool({
|
|
201
|
-
description: "List the tasks in a project with their status. Use this to see what already exists before creating more.",
|
|
202
|
-
args: {
|
|
203
|
-
projectId: z.string().describe("Project id from list_projects"),
|
|
204
|
-
status: z.enum(STATUS).describe("Only tasks in this status").optional()
|
|
205
|
-
},
|
|
206
|
-
async execute({ projectId, status }) {
|
|
207
|
-
const query = status ? `?status=${status}` : "";
|
|
208
|
-
return json((await get(`/projects/${projectId}/tasks${query}`)).tasks);
|
|
209
|
-
}
|
|
210
|
-
}),
|
|
211
|
-
get_task: tool({
|
|
212
|
-
description: "Fetch one task with all of its subtasks. Use this before updating anything, so you are editing against the current state rather than what you remember.",
|
|
213
|
-
args: { taskId: z.string().describe("Task id") },
|
|
214
|
-
async execute(args) {
|
|
215
|
-
return json((await get(`/projects/tasks/${args.taskId}`)).task);
|
|
216
|
-
}
|
|
217
|
-
}),
|
|
218
|
-
create_task: tool({
|
|
219
|
-
description: "Create a task: one unit of work a team would plan together, such as 'Upgrade to SAP Commerce 2211'. Read the relevant code before writing the description — a task that describes the actual state of the code is worth keeping, one written from assumption is not. Break it into subtasks afterwards with create_subtask.",
|
|
220
|
-
args: {
|
|
221
|
-
projectId: z.string().describe("Project id from list_projects"),
|
|
222
|
-
title: z.string().max(300).describe("Imperative and specific: 'Replace deprecated FlexibleSearch calls'"),
|
|
223
|
-
description: z.string().describe("What needs doing and why, including what you found in the code — current version, affected modules, known blockers"),
|
|
224
|
-
type: z.enum(TYPE).describe("Defaults to feature").optional()
|
|
225
|
-
},
|
|
226
|
-
async execute({ projectId, ...body }) {
|
|
227
|
-
return json((await post(`/projects/${projectId}/tasks`, body)).task);
|
|
228
|
-
}
|
|
229
|
-
}),
|
|
230
|
-
update_task: tool({
|
|
231
|
-
description: "Update a task's title, description, type or status. Move it to in_progress when work starts. Prefer in_review over done when you have finished but a person has not checked it — done is a human's word. Use blocked when you cannot proceed, and say why in a comment.",
|
|
232
|
-
args: {
|
|
233
|
-
taskId: z.string().describe("Task id"),
|
|
234
|
-
title: z.string().max(300).describe("New title").optional(),
|
|
235
|
-
description: z.string().describe("New description").optional(),
|
|
236
|
-
type: z.enum(TYPE).describe("New type").optional(),
|
|
237
|
-
status: z.enum(STATUS).describe("New status").optional()
|
|
238
|
-
},
|
|
239
|
-
async execute({ taskId, ...body }) {
|
|
240
|
-
return json((await patch(`/projects/tasks/${taskId}`, body)).task);
|
|
241
|
-
}
|
|
242
|
-
}),
|
|
243
|
-
list_subtasks: tool({
|
|
244
|
-
description: "List the subtasks of a task, in order.",
|
|
245
|
-
args: { taskId: z.string().describe("Task id") },
|
|
246
|
-
async execute(args) {
|
|
247
|
-
return json((await get(`/projects/tasks/${args.taskId}/subtasks`)).subtasks);
|
|
248
|
-
}
|
|
249
|
-
}),
|
|
250
|
-
create_subtask: tool({
|
|
251
|
-
description: "Add a subtask: one concrete change a developer could pick up and finish, in one codebase. Populate filesToModify with paths you have actually opened; leave it empty rather than filling it with guesses. Work that genuinely spans two codebases is two subtasks, which is also what keeps 'done' honest.",
|
|
252
|
-
args: {
|
|
253
|
-
taskId: z.string().describe("Task id"),
|
|
254
|
-
title: z.string().max(300).describe("One specific change"),
|
|
255
|
-
description: z.string().describe("What to change and how you know it needs changing"),
|
|
256
|
-
filesToModify: z.array(z.string()).describe(FILES_TO_MODIFY).optional(),
|
|
257
|
-
implementationNotes: z.string().describe("Anything that saves the next person a search: the symbol to grep for, the call sites, the order to do things in").optional(),
|
|
258
|
-
order: z.number().int().describe("Position in the list. Lower runs first.").optional()
|
|
259
|
-
},
|
|
260
|
-
async execute({ taskId, ...body }) {
|
|
261
|
-
return json((await post(`/projects/tasks/${taskId}/subtasks`, body)).subtask);
|
|
262
|
-
}
|
|
263
|
-
}),
|
|
264
|
-
update_subtask: tool({
|
|
265
|
-
description: "Update a subtask. Use this to mark one done as you finish it, so the board reflects reality while the work is happening rather than after.",
|
|
266
|
-
args: {
|
|
267
|
-
subtaskId: z.string().describe("Subtask id"),
|
|
268
|
-
title: z.string().max(300).describe("New title").optional(),
|
|
269
|
-
description: z.string().describe("New description").optional(),
|
|
270
|
-
status: z.enum(STATUS).describe("New status").optional(),
|
|
271
|
-
filesToModify: z.array(z.string()).describe(FILES_TO_MODIFY).optional(),
|
|
272
|
-
implementationNotes: z.string().describe("Notes for whoever picks this up").optional(),
|
|
273
|
-
order: z.number().int().describe("Position in the list").optional()
|
|
274
|
-
},
|
|
275
|
-
async execute({ subtaskId, ...body }) {
|
|
276
|
-
return json((await patch(`/projects/subtasks/${subtaskId}`, body)).subtask);
|
|
277
|
-
}
|
|
278
|
-
}),
|
|
279
|
-
list_comments: tool({
|
|
280
|
-
description: "Read the comment thread on a task or a subtask. Do this before acting on work someone else planned — a correction from a human usually lives here rather than in the description. Pass exactly one of taskId or subtaskId.",
|
|
281
|
-
args: {
|
|
282
|
-
taskId: z.string().describe("Task id").optional(),
|
|
283
|
-
subtaskId: z.string().describe("Subtask id").optional()
|
|
284
|
-
},
|
|
285
|
-
async execute(args) {
|
|
286
|
-
const path = commentPath(args.taskId, args.subtaskId);
|
|
287
|
-
return json((await get(path)).comments);
|
|
288
|
-
}
|
|
289
|
-
}),
|
|
290
|
-
add_comment: tool({
|
|
291
|
-
description: "Leave a comment on a task or subtask. Use it to record what you found, what you changed, or why you departed from the plan — not to restate the description. Comments you write are labelled as coming from the agent. Pass exactly one of taskId or subtaskId.",
|
|
292
|
-
args: {
|
|
293
|
-
body: z.string().max(1e4).describe("The comment"),
|
|
294
|
-
taskId: z.string().describe("Task id").optional(),
|
|
295
|
-
subtaskId: z.string().describe("Subtask id").optional()
|
|
296
|
-
},
|
|
297
|
-
async execute(args) {
|
|
298
|
-
const path = commentPath(args.taskId, args.subtaskId);
|
|
299
|
-
return json((await post(path, { body: args.body, source: "agent" })).comment);
|
|
300
|
-
}
|
|
301
|
-
})
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
// packages/plugin/src/tools-context-hub.ts
|
|
305
|
-
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
306
|
-
var z2 = tool2.schema;
|
|
307
|
-
var contextHubTools = {
|
|
308
|
-
search_context_hub: tool2({
|
|
309
|
-
description: "Search the company's internal documentation. Run this before answering a question about how something works here — the wiki usually explains the intent that the code only implies, and it is where conventions, past decisions and known traps are written down. Searches by meaning as well as by keyword, so a question phrased differently to the page still finds it. Results carry a score and a staleness flag; a stale page is past its review date and may be wrong. Follow up with read_context_hub_page for the full text.",
|
|
310
|
-
args: {
|
|
311
|
-
query: z2.string().describe("What you want to know. A phrase or question works better than a single word."),
|
|
312
|
-
mode: z2.enum(["hybrid", "keyword", "semantic"]).describe("hybrid (default) searches by meaning and by keyword. Use keyword when looking for an exact identifier, class name or error string.").optional(),
|
|
313
|
-
spaceId: z2.string().describe("Optional: restrict to one space, from list_context_hub_spaces").optional(),
|
|
314
|
-
tag: z2.string().describe("Optional: restrict to pages carrying this tag").optional(),
|
|
315
|
-
limit: z2.number().int().describe("Maximum results, default 20, maximum 50").optional()
|
|
316
|
-
},
|
|
317
|
-
async execute(args) {
|
|
318
|
-
const params = new URLSearchParams({ q: args.query });
|
|
319
|
-
if (args.mode)
|
|
320
|
-
params.set("mode", args.mode);
|
|
321
|
-
if (args.spaceId)
|
|
322
|
-
params.set("spaceId", args.spaceId);
|
|
323
|
-
if (args.tag)
|
|
324
|
-
params.set("tag", args.tag);
|
|
325
|
-
if (args.limit)
|
|
326
|
-
params.set("limit", String(args.limit));
|
|
327
|
-
return json((await get(`/context-hub/search?${params}`)).results);
|
|
328
|
-
}
|
|
329
|
-
}),
|
|
330
|
-
list_context_hub_spaces: tool2({
|
|
331
|
-
description: "List the spaces in the Context Hub. A space groups related pages — typically one per system, team or imported wiki. Use it to narrow a search when a keyword is too common.",
|
|
332
|
-
args: {},
|
|
333
|
-
async execute() {
|
|
334
|
-
return json((await get("/context-hub/spaces")).spaces);
|
|
335
|
-
}
|
|
336
|
-
}),
|
|
337
|
-
list_context_hub_pages: tool2({
|
|
338
|
-
description: "List the pages in a space, with titles and when they were last updated. Bodies are not included — read_context_hub_page fetches those.",
|
|
339
|
-
args: { spaceId: z2.string().describe("Space id from list_context_hub_spaces") },
|
|
340
|
-
async execute(args) {
|
|
341
|
-
return json((await get(`/context-hub/spaces/${args.spaceId}/pages`)).pages);
|
|
342
|
-
}
|
|
343
|
-
}),
|
|
344
|
-
read_context_hub_page: tool2({
|
|
345
|
-
description: "Read one page in full. Cite what you found from it when it changes your answer, so the person can check the source — the wiki can be out of date, and saying where a claim came from is what lets them notice.",
|
|
346
|
-
args: { pageId: z2.string().describe("Page id from search or list") },
|
|
347
|
-
async execute(args) {
|
|
348
|
-
return json((await get(`/context-hub/pages/${args.pageId}`)).page);
|
|
349
|
-
}
|
|
350
|
-
}),
|
|
351
|
-
create_context_hub_page: tool2({
|
|
352
|
-
description: "Write a new page. Use this when you have worked something out that the next person would otherwise have to rediscover — a migration procedure, why an approach was rejected, how two systems actually talk to each other. Search first: adding a second page on a subject that already has one makes both less trustworthy.",
|
|
353
|
-
args: {
|
|
354
|
-
spaceId: z2.string().describe("Space to create the page in"),
|
|
355
|
-
title: z2.string().max(300).describe("Specific and searchable: 'Upgrading SAP Commerce 1905 to 2211'"),
|
|
356
|
-
content: z2.string().describe("Markdown body")
|
|
357
|
-
},
|
|
358
|
-
async execute({ spaceId, ...body }) {
|
|
359
|
-
return json((await post(`/context-hub/spaces/${spaceId}/pages`, body)).page);
|
|
360
|
-
}
|
|
361
|
-
}),
|
|
362
|
-
update_context_hub_page: tool2({
|
|
363
|
-
description: "Replace a page's title or body. The content you send replaces what is there — read the page first and return the whole document with your changes folded in, or you will delete the parts you did not mention.",
|
|
364
|
-
args: {
|
|
365
|
-
pageId: z2.string().describe("Page id"),
|
|
366
|
-
title: z2.string().max(300).describe("New title").optional(),
|
|
367
|
-
content: z2.string().describe("The complete new markdown body, not a fragment").optional()
|
|
368
|
-
},
|
|
369
|
-
async execute({ pageId, ...body }) {
|
|
370
|
-
return json((await patch(`/context-hub/pages/${pageId}`, body)).page);
|
|
371
|
-
}
|
|
372
|
-
})
|
|
373
|
-
};
|
|
374
144
|
|
|
375
145
|
// packages/plugin/src/index.ts
|
|
376
146
|
var API2 = () => (process.env.MODERNISER_API_URL ?? "https://v2.moderniser.ai/api").replace(/\/+$/, "");
|
|
@@ -396,19 +166,49 @@ async function call(path, body) {
|
|
|
396
166
|
return null;
|
|
397
167
|
}
|
|
398
168
|
}
|
|
169
|
+
function positiveInt(value) {
|
|
170
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
171
|
+
}
|
|
172
|
+
async function gatewayProduct() {
|
|
173
|
+
const key = KEY() ?? storedKey();
|
|
174
|
+
if (!key)
|
|
175
|
+
return {};
|
|
176
|
+
try {
|
|
177
|
+
const res = await fetch(`${GATEWAY()}/models`, {
|
|
178
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
179
|
+
signal: AbortSignal.timeout(5000)
|
|
180
|
+
});
|
|
181
|
+
if (!res.ok)
|
|
182
|
+
return {};
|
|
183
|
+
const entry = (await res.json())?.data?.[0];
|
|
184
|
+
if (!entry)
|
|
185
|
+
return {};
|
|
186
|
+
const efforts = Array.isArray(entry.efforts) ? entry.efforts.filter((e) => typeof e === "string" && e.length > 0) : undefined;
|
|
187
|
+
return {
|
|
188
|
+
contextWindow: positiveInt(entry.context_window),
|
|
189
|
+
maxOutputTokens: positiveInt(entry.max_output_tokens),
|
|
190
|
+
efforts: efforts?.length ? efforts : undefined
|
|
191
|
+
};
|
|
192
|
+
} catch {
|
|
193
|
+
return {};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
399
196
|
var registered = new Set;
|
|
400
197
|
var reported = new Map;
|
|
401
198
|
var ModerniserPlugin = async () => {
|
|
402
199
|
return {
|
|
403
200
|
config: async (config) => {
|
|
404
201
|
const modalities = { input: ["text", "image"], output: ["text"] };
|
|
202
|
+
const product = await gatewayProduct();
|
|
203
|
+
const efforts = (product.efforts ?? ["high"]).filter((effort) => effort !== "default");
|
|
405
204
|
const models = {
|
|
406
|
-
emm: {
|
|
407
|
-
|
|
408
|
-
name: "Enterprise Modernisation Model (deep reasoning)",
|
|
205
|
+
emm: {
|
|
206
|
+
name: "Enterprise Modernisation Model",
|
|
409
207
|
tool_call: true,
|
|
410
208
|
reasoning: true,
|
|
411
|
-
modalities
|
|
209
|
+
modalities,
|
|
210
|
+
variants: Object.fromEntries(efforts.map((effort) => [effort, { reasoning_effort: effort }])),
|
|
211
|
+
...product.contextWindow && product.maxOutputTokens ? { limit: { context: product.contextWindow, output: product.maxOutputTokens } } : {}
|
|
412
212
|
}
|
|
413
213
|
};
|
|
414
214
|
const key = KEY();
|
|
@@ -425,8 +225,20 @@ var ModerniserPlugin = async () => {
|
|
|
425
225
|
config.model ??= requested || DEFAULT_MODEL;
|
|
426
226
|
config.small_model ??= config.model;
|
|
427
227
|
}
|
|
228
|
+
const mcpKey = resolveKey();
|
|
229
|
+
if (mcpKey) {
|
|
230
|
+
config.mcp ??= {};
|
|
231
|
+
config.mcp[PROVIDER_ID] ??= {
|
|
232
|
+
type: "remote",
|
|
233
|
+
url: `${API2()}/mcp`,
|
|
234
|
+
headers: { Authorization: `Bearer ${mcpKey}` },
|
|
235
|
+
oauth: false
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
240
|
+
output.system.push(moderniserContext());
|
|
428
241
|
},
|
|
429
|
-
tool: { ...boardTools, ...contextHubTools },
|
|
430
242
|
auth: moderniserAuth(),
|
|
431
243
|
event: async ({ event }) => {
|
|
432
244
|
const type = event?.type;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moderniser/plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Moderniser plugin for opencode: points inference at the gateway,
|
|
5
|
+
"description": "Moderniser plugin for opencode: points inference at the gateway, declares the Moderniser remote MCP server, and reports session telemetry",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
package/dist/tools-boards.d.ts
DELETED