@alisio/server 0.8.0 → 0.10.0
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/index.d.ts +25 -0
- package/dist/index.js +40 -2
- package/dist/plugins/catalog-schema.d.ts +37 -0
- package/dist/plugins/catalog-schema.js +141 -0
- package/dist/plugins/catalog-service.d.ts +89 -0
- package/dist/plugins/catalog-service.js +306 -0
- package/dist/plugins/jobs.d.ts +41 -0
- package/dist/plugins/jobs.js +94 -0
- package/dist/plugins/sdk-version.d.ts +6 -0
- package/dist/plugins/sdk-version.js +28 -0
- package/dist/plugins/staging.d.ts +13 -0
- package/dist/plugins/staging.js +48 -0
- package/dist/routes/plugin-catalog.d.ts +103 -0
- package/dist/routes/plugin-catalog.js +436 -0
- package/dist/routes/server-restart.d.ts +7 -0
- package/dist/routes/server-restart.js +24 -0
- package/dist/routes/session-views.js +8 -0
- package/dist/web/assets/{AgentPicker-CHCnAWFV.js → AgentPicker-B2dNJHaW.js} +1 -1
- package/dist/web/assets/{AgentsModal-C9JANTC0.js → AgentsModal-BWrVQpCo.js} +2 -2
- package/dist/web/assets/{ArtifactPanel-CMy7S56f.js → ArtifactPanel-ZkBV87x7.js} +3 -3
- package/dist/web/assets/{BtwPanel-DXZdSCeO.js → BtwPanel-BtIHip-u.js} +1 -1
- package/dist/web/assets/{ChangelogDialog-B1zYDaIS.js → ChangelogDialog-NBwCG0Bo.js} +1 -1
- package/dist/web/assets/ContextTab-Bi8MZwMV.js +1 -0
- package/dist/web/assets/ContextTab-g4wwZXSY.css +1 -0
- package/dist/web/assets/{CopyButton-DEf4thwH.js → CopyButton-Bh3Dsk-o.js} +1 -1
- package/dist/web/assets/{GoalBar-CCT6XlB-.js → GoalBar-DVwQsVtS.js} +1 -1
- package/dist/web/assets/{MemoryTab-lph1Kv8C.js → MemoryTab-CjZTbnFG.js} +1 -1
- package/dist/web/assets/{PermissionsMenu-oMuzx7AI.js → PermissionsMenu-B03X019W.js} +1 -1
- package/dist/web/assets/{PlanReviewPanel-BXl-gcFF.js → PlanReviewPanel-CM1uxJVs.js} +1 -1
- package/dist/web/assets/{PlanViewer-TxP_h0KX.js → PlanViewer-DxAqn6IP.js} +1 -1
- package/dist/web/assets/ProofCard-B2nxuxaT.js +1 -0
- package/dist/web/assets/ScratchTab-BLkjPbJC.js +1 -0
- package/dist/web/assets/SettingsModal-BJj7ELxX.js +3 -0
- package/dist/web/assets/{SpreadsheetPanel-Db_lqwkD.js → SpreadsheetPanel-BJLNla1t.js} +1 -1
- package/dist/web/assets/{TasksTab-BnafNW7J.js → TasksTab-DhKGW5By.js} +1 -1
- package/dist/web/assets/{TextPreview-DHQCGoiY.js → TextPreview-CcGkaf6g.js} +1 -1
- package/dist/web/assets/{TrajectoryTab-DMJw5g-T.js → TrajectoryTab-U37sw2D0.js} +1 -1
- package/dist/web/assets/{agents.module-BlfBTz7i.js → agents.module-C5jXUIc1.js} +1 -1
- package/dist/web/assets/app-CGSX2FCK.js +3 -0
- package/dist/web/assets/{es-ySPgx3os.js → es-DP4ejr3u.js} +2 -2
- package/dist/web/assets/{goal-B1jQsfH5.js → goal-Cib1wj1f.js} +1 -1
- package/dist/web/assets/{i18n-C54K0mc6.js → i18n-DqOZSMVh.js} +2 -2
- package/dist/web/assets/{icons-DTCoNVsk.js → icons-BZSZMbqh.js} +1 -1
- package/dist/web/assets/index-CSQo6Bhi.js +79 -0
- package/dist/web/assets/{jsxRuntime-DWBdSTGf.js → jsxRuntime-BWhXelbu.js} +1 -1
- package/dist/web/assets/{panel-LBWYDX9B.js → panel-IZ40pPYp.js} +1 -1
- package/dist/web/assets/settings-BAW93knU.css +1 -0
- package/dist/web/assets/settings.module-ErkNzIk4.js +1 -0
- package/dist/web/assets/{shared-DLevBtPm.js → shared-C2fqplXu.js} +1 -1
- package/dist/web/assets/{strings-DDeSYQDZ.js → strings-BUuRkgRy.js} +1 -1
- package/dist/web/assets/{tasks-Sc_vWQpd.js → tasks-Bw0p2a6l.js} +1 -1
- package/dist/web/assets/{view-BFzfUho9.js → view-B95Eg9o2.js} +1 -1
- package/dist/web/assets/view-BeVPW5cE.js +1 -0
- package/dist/web/assets/{view-CChCbdid.js → view-BkR3NRaK.js} +1 -1
- package/dist/web/assets/{view-C4NPIs0Y.js → view-C99qRBgc.js} +1 -1
- package/dist/web/assets/{view-DooqXXnU.js → view-CI66xheW.js} +1 -1
- package/dist/web/assets/{view-Cs6w0jid.js → view-CSAgn7-g.js} +1 -1
- package/dist/web/assets/{view-D_Sw63-N.js → view-DJAfpDXs.js} +1 -1
- package/dist/web/assets/{view-s7NM5Jdr.js → view-DrHx2F_3.js} +1 -1
- package/dist/web/assets/{view-DF9hOwPO.js → view-Du7qfIhE.js} +1 -1
- package/dist/web/index.html +7 -7
- package/package.json +4 -4
- package/dist/web/assets/ProofCard-DFVScMg6.js +0 -1
- package/dist/web/assets/ScratchTab-f751fxOV.js +0 -1
- package/dist/web/assets/SettingsModal-DZhl5vvS.js +0 -1
- package/dist/web/assets/app-BknkHksg.js +0 -3
- package/dist/web/assets/index-XYSVNCIf.js +0 -79
- package/dist/web/assets/settings-DdWABXsT.css +0 -1
- package/dist/web/assets/settings.module-BClxMWCR.js +0 -1
- package/dist/web/assets/view-Cp3A_Nly.js +0 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin catalog routes: browse the published catalog (cached, revalidated on demand), proxy its
|
|
3
|
+
* covers, install a catalog plugin, uninstall a globally installed npm plugin and list the
|
|
4
|
+
* installed packages. Installing runs npm with the user's privileges and executes third-party
|
|
5
|
+
* code when the plugin loads: it needs an explicit `confirmed: true`, only catalog packages are
|
|
6
|
+
* accepted, and it is refused under `--read-only`. Types are local: the SDK contract is untouched.
|
|
7
|
+
*/
|
|
8
|
+
import { createWriteStream } from "node:fs";
|
|
9
|
+
import { stat } from "node:fs/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { catalogPluginState, comparePluginVersions, configHome, inspectPluginTarball, installedGlobalPlugins, installLocalPluginTarball, installPlugin, MAX_TARBALL_BYTES, NPM_NOT_FOUND_MESSAGE, parsePluginSpec, pluginCodeImportedFrom, pluginDependencySpec, satisfiesRange, uninstallPlugin, } from "@alisio/core";
|
|
12
|
+
import { readJson } from "../http/body.js";
|
|
13
|
+
import { HttpError } from "../http/errors.js";
|
|
14
|
+
import { CatalogUnavailableError, } from "../plugins/catalog-service.js";
|
|
15
|
+
import { InstallJobs } from "../plugins/jobs.js";
|
|
16
|
+
import { createStaging, isStagingId, removeStaging, stagingFile, sweepStaging, } from "../plugins/staging.js";
|
|
17
|
+
import { is, validate } from "../schemas.js";
|
|
18
|
+
import { workspaceApp } from "./management.js";
|
|
19
|
+
const COVER_CSP = "default-src 'none'; style-src 'unsafe-inline'; sandbox";
|
|
20
|
+
const SLUG = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
21
|
+
/** npm's answer for a version the registry does not have. */
|
|
22
|
+
const NOT_PUBLISHED = /\bETARGET\b|No matching version found/;
|
|
23
|
+
const PEER_CONFLICT = /ERESOLVE|Conflicting peer|require an older/;
|
|
24
|
+
const MAX_ERROR_CHARS = 1500;
|
|
25
|
+
/** How the uploaded version relates to the installed one. */
|
|
26
|
+
function relationOf(uploaded, installed) {
|
|
27
|
+
const order = uploaded ? comparePluginVersions(uploaded, installed) : undefined;
|
|
28
|
+
return order === undefined || order === 0 ? "same" : order > 0 ? "update" : "downgrade";
|
|
29
|
+
}
|
|
30
|
+
const megabytes = (bytes) => Math.round((bytes / (1024 * 1024)) * 10) / 10;
|
|
31
|
+
/** Streams the request body to `file`, failing as soon as it passes `max` bytes. */
|
|
32
|
+
async function receive(req, file, max) {
|
|
33
|
+
const out = createWriteStream(file, { mode: 0o600 });
|
|
34
|
+
let size = 0;
|
|
35
|
+
try {
|
|
36
|
+
for await (const chunk of req) {
|
|
37
|
+
size += chunk.length;
|
|
38
|
+
if (size > max)
|
|
39
|
+
throw new HttpError("validation_failed", `The file is larger than ${megabytes(max)} MB`);
|
|
40
|
+
if (!out.write(chunk))
|
|
41
|
+
await new Promise((resolve) => out.once("drain", resolve));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await new Promise((resolve) => out.end(resolve));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const errorText = (error) => (error instanceof Error ? error.message : String(error));
|
|
49
|
+
export function registerPluginCatalogRoutes(router, ctx, recycler, options) {
|
|
50
|
+
const { service } = options;
|
|
51
|
+
let mutating = false;
|
|
52
|
+
const jobs = new InstallJobs(options.jobTtlMs);
|
|
53
|
+
/** Packages updated on disk whose old code this process still runs (cleared by a restart). */
|
|
54
|
+
const pendingRestart = new Set();
|
|
55
|
+
const codeLoaded = (name) => pluginCodeImportedFrom(join(configHome(), "plugins", "node_modules", name));
|
|
56
|
+
const maxUpload = options.maxUploadBytes ?? MAX_TARBALL_BYTES;
|
|
57
|
+
void sweepStaging().catch(() => { });
|
|
58
|
+
const loadCatalog = async (refresh) => {
|
|
59
|
+
try {
|
|
60
|
+
return await service.get(refresh);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error instanceof CatalogUnavailableError)
|
|
64
|
+
throw new HttpError("provider_unavailable", `The plugin catalog could not be loaded and there is no cached copy: ${error.message}`);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const refuseReadOnly = (what) => {
|
|
69
|
+
if (ctx.base.readOnly)
|
|
70
|
+
throw new HttpError("capability_ceiling", `${what} is unavailable under --read-only`);
|
|
71
|
+
};
|
|
72
|
+
/** Installs and uninstalls never overlap: they share one npm prefix and one config file. */
|
|
73
|
+
const exclusive = async (task) => {
|
|
74
|
+
if (mutating)
|
|
75
|
+
throw new HttpError("runs_active", "Another plugin install or uninstall is in progress: wait for it to finish, then retry");
|
|
76
|
+
mutating = true;
|
|
77
|
+
try {
|
|
78
|
+
return await task();
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
mutating = false;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/** Recycles every open workspace so it re-reads the plugin set; true when any was deferred. */
|
|
85
|
+
const applyToWorkspaces = async () => {
|
|
86
|
+
let deferred = false;
|
|
87
|
+
for (const entry of ctx.workspaces.entries()) {
|
|
88
|
+
const reopened = await recycler.request(entry.id);
|
|
89
|
+
if (!reopened)
|
|
90
|
+
deferred = true;
|
|
91
|
+
recycler.announce(entry.id, ["plugins", "commands"]);
|
|
92
|
+
}
|
|
93
|
+
return deferred;
|
|
94
|
+
};
|
|
95
|
+
const toWire = (plugin, installed) => {
|
|
96
|
+
const { coverSource, ...rest } = plugin;
|
|
97
|
+
const state = catalogPluginState({
|
|
98
|
+
catalogVersion: plugin.version,
|
|
99
|
+
...(installed.has(plugin.package) ? { installedVersion: installed.get(plugin.package) } : {}),
|
|
100
|
+
...(plugin.sdk ? { sdkRange: plugin.sdk } : {}),
|
|
101
|
+
...(options.sdkVersion ? { sdkVersion: options.sdkVersion } : {}),
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
...rest,
|
|
105
|
+
...(coverSource
|
|
106
|
+
? { cover: `/api/plugin-catalog/covers/${encodeURIComponent(plugin.slug)}` }
|
|
107
|
+
: {}),
|
|
108
|
+
...(state.installedVersion ? { installedVersion: state.installedVersion } : {}),
|
|
109
|
+
updateAvailable: state.updateAvailable,
|
|
110
|
+
compatible: state.compatible,
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
// ---- Catalog ------------------------------------------------------------------------------
|
|
114
|
+
router.get("/api/plugin-catalog", async ({ url }) => {
|
|
115
|
+
const raw = url.searchParams.get("refresh");
|
|
116
|
+
if (raw !== null && raw !== "0" && raw !== "1" && raw !== "")
|
|
117
|
+
throw new HttpError("validation_failed", "Invalid query parameter", { fields: ["refresh"] });
|
|
118
|
+
const result = await loadCatalog(raw === "1");
|
|
119
|
+
const installed = await installedGlobalPlugins(configHome());
|
|
120
|
+
const versions = new Map(installed.map((item) => [item.name, item.version]));
|
|
121
|
+
const body = {
|
|
122
|
+
catalog: {
|
|
123
|
+
...(result.catalog.generatedAt ? { generatedAt: result.catalog.generatedAt } : {}),
|
|
124
|
+
categories: result.catalog.categories,
|
|
125
|
+
plugins: result.catalog.plugins.map((plugin) => toWire(plugin, versions)),
|
|
126
|
+
},
|
|
127
|
+
meta: result.meta,
|
|
128
|
+
installed: installed.map((item) => ({ package: item.name, version: item.version })),
|
|
129
|
+
...(options.sdkVersion ? { sdkVersion: options.sdkVersion } : {}),
|
|
130
|
+
};
|
|
131
|
+
return { body };
|
|
132
|
+
});
|
|
133
|
+
router.get("/api/plugin-catalog/covers/:slug", async ({ req, res, params }) => {
|
|
134
|
+
const slug = params.slug ?? "";
|
|
135
|
+
if (!SLUG.test(slug))
|
|
136
|
+
throw new HttpError("not_found", "Cover not found");
|
|
137
|
+
const result = await loadCatalog(false);
|
|
138
|
+
const plugin = result.catalog.plugins.find((entry) => entry.slug === slug);
|
|
139
|
+
if (!plugin?.coverSource)
|
|
140
|
+
throw new HttpError("not_found", "Cover not found");
|
|
141
|
+
const cover = await service.cover(plugin, result.meta.fetchedAt);
|
|
142
|
+
if (!cover)
|
|
143
|
+
throw new HttpError("not_found", "Cover not found");
|
|
144
|
+
res.setHeader("Content-Security-Policy", COVER_CSP);
|
|
145
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
146
|
+
res.setHeader("Cache-Control", "private, max-age=3600");
|
|
147
|
+
res.setHeader("ETag", cover.etag);
|
|
148
|
+
if (req.headers["if-none-match"] === cover.etag) {
|
|
149
|
+
res.writeHead(304);
|
|
150
|
+
res.end();
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
res.writeHead(200, {
|
|
154
|
+
"Content-Type": cover.contentType,
|
|
155
|
+
"Content-Length": String(cover.body.length),
|
|
156
|
+
});
|
|
157
|
+
res.end(cover.body);
|
|
158
|
+
return undefined;
|
|
159
|
+
});
|
|
160
|
+
// ---- Installed packages -------------------------------------------------------------------
|
|
161
|
+
router.get("/api/plugins/packages", async ({ url }) => {
|
|
162
|
+
const opened = await workspaceApp(ctx.workspaces, url.searchParams.get("workspace"));
|
|
163
|
+
const loaded = opened.app.pluginCatalog();
|
|
164
|
+
// Update info comes from the cached catalog: no network unless the cache is stale.
|
|
165
|
+
const catalog = await service.get(false).then((result) => result.catalog, () => undefined);
|
|
166
|
+
const pluginsDir = join(configHome(), "plugins");
|
|
167
|
+
const body = [];
|
|
168
|
+
for (const item of await installedGlobalPlugins(configHome())) {
|
|
169
|
+
const local = (await pluginDependencySpec(pluginsDir, item.name))?.startsWith("file:") ?? false;
|
|
170
|
+
const entry = local ? undefined : catalog?.plugins.find((p) => p.package === item.name);
|
|
171
|
+
const state = entry
|
|
172
|
+
? catalogPluginState({
|
|
173
|
+
catalogVersion: entry.version,
|
|
174
|
+
installedVersion: item.version,
|
|
175
|
+
...(entry.sdk ? { sdkRange: entry.sdk } : {}),
|
|
176
|
+
...(options.sdkVersion ? { sdkVersion: options.sdkVersion } : {}),
|
|
177
|
+
})
|
|
178
|
+
: undefined;
|
|
179
|
+
body.push({
|
|
180
|
+
package: item.name,
|
|
181
|
+
version: item.version,
|
|
182
|
+
pluginIds: loaded.filter((plugin) => plugin.spec === item.name).map((plugin) => plugin.id),
|
|
183
|
+
removable: !ctx.base.readOnly,
|
|
184
|
+
restartRequired: pendingRestart.has(item.name),
|
|
185
|
+
local,
|
|
186
|
+
...(entry ? { latestVersion: entry.version } : {}),
|
|
187
|
+
updateAvailable: state?.updateAvailable ?? false,
|
|
188
|
+
updateCompatible: state?.compatible ?? true,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return { body };
|
|
192
|
+
});
|
|
193
|
+
// ---- Install ------------------------------------------------------------------------------
|
|
194
|
+
router.post("/api/plugins/install", async ({ req }) => {
|
|
195
|
+
const input = validate(await readJson(req), {
|
|
196
|
+
package: { check: is.nonEmpty(214), required: true },
|
|
197
|
+
version: { check: is.nonEmpty(64) },
|
|
198
|
+
confirmed: { check: (v) => v === true, required: true },
|
|
199
|
+
});
|
|
200
|
+
refuseReadOnly("Installing plugins");
|
|
201
|
+
const { catalog } = await loadCatalog(false);
|
|
202
|
+
const entry = catalog.plugins.find((plugin) => plugin.package === input.package);
|
|
203
|
+
if (!entry)
|
|
204
|
+
throw new HttpError("validation_failed", "Only plugins listed in the catalog can be installed here", {
|
|
205
|
+
fields: ["package"],
|
|
206
|
+
});
|
|
207
|
+
if (input.version !== undefined && input.version !== entry.version)
|
|
208
|
+
throw new HttpError("validation_failed", `Only the catalog version (${entry.version}) can be installed here`, { fields: ["version"] });
|
|
209
|
+
const installed = new Map((await installedGlobalPlugins(configHome())).map((item) => [item.name, item.version]));
|
|
210
|
+
const state = catalogPluginState({
|
|
211
|
+
catalogVersion: entry.version,
|
|
212
|
+
...(installed.has(entry.package) ? { installedVersion: installed.get(entry.package) } : {}),
|
|
213
|
+
...(entry.sdk ? { sdkRange: entry.sdk } : {}),
|
|
214
|
+
...(options.sdkVersion ? { sdkVersion: options.sdkVersion } : {}),
|
|
215
|
+
});
|
|
216
|
+
if (!state.compatible)
|
|
217
|
+
throw new HttpError("validation_failed", `${entry.name} needs @alisio/sdk ${entry.sdk}, but this Alisio has ${options.sdkVersion}`, { fields: ["package"] });
|
|
218
|
+
return exclusive(async () => {
|
|
219
|
+
let result;
|
|
220
|
+
const loaded = codeLoaded(entry.package);
|
|
221
|
+
const attempt = (spec) => installPlugin({
|
|
222
|
+
spec,
|
|
223
|
+
// An installed package is replaced at the catalog version: update, reinstall or downgrade.
|
|
224
|
+
force: installed.has(entry.package),
|
|
225
|
+
configHome: configHome(),
|
|
226
|
+
...(options.installRunner ? { runner: options.installRunner } : {}),
|
|
227
|
+
});
|
|
228
|
+
try {
|
|
229
|
+
try {
|
|
230
|
+
result = await attempt(`npm:${entry.package}@${entry.version}`);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
// The catalog can list a version npm does not serve yet (it is generated before the
|
|
234
|
+
// publish finishes): fall back to the registry's latest rather than fail the install.
|
|
235
|
+
if (!NOT_PUBLISHED.test(errorText(error)))
|
|
236
|
+
throw error;
|
|
237
|
+
result = await attempt(`npm:${entry.package}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
const text = errorText(error).slice(0, MAX_ERROR_CHARS);
|
|
242
|
+
if (text.includes(NPM_NOT_FOUND_MESSAGE) || PEER_CONFLICT.test(text))
|
|
243
|
+
throw new HttpError("validation_failed", text);
|
|
244
|
+
throw new HttpError("provider_unavailable", text);
|
|
245
|
+
}
|
|
246
|
+
const restartRequired = loaded;
|
|
247
|
+
if (restartRequired)
|
|
248
|
+
pendingRestart.add(result.packageName);
|
|
249
|
+
const deferred = await applyToWorkspaces();
|
|
250
|
+
const body = {
|
|
251
|
+
package: result.packageName,
|
|
252
|
+
installedVersion: result.installedVersion,
|
|
253
|
+
alreadyInstalled: result.alreadyInstalled,
|
|
254
|
+
updated: result.updated,
|
|
255
|
+
...(result.previousVersion ? { previousVersion: result.previousVersion } : {}),
|
|
256
|
+
restartPending: deferred,
|
|
257
|
+
restartRequired,
|
|
258
|
+
};
|
|
259
|
+
return { body };
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
// ---- Install from a local file --------------------------------------------------------------
|
|
263
|
+
router.post("/api/plugins/local/inspect", async ({ req }) => {
|
|
264
|
+
refuseReadOnly("Installing plugins");
|
|
265
|
+
const type = String(req.headers["content-type"] ?? "")
|
|
266
|
+
.split(";")[0]
|
|
267
|
+
?.trim();
|
|
268
|
+
if (!["application/gzip", "application/x-gzip", "application/octet-stream"].includes(type ?? ""))
|
|
269
|
+
throw new HttpError("unsupported_media_type", "Send the .tgz as the raw body with Content-Type application/gzip");
|
|
270
|
+
const declared = Number(req.headers["content-length"] ?? Number.NaN);
|
|
271
|
+
if (Number.isFinite(declared) && declared > maxUpload)
|
|
272
|
+
throw new HttpError("validation_failed", `The file is larger than ${megabytes(maxUpload)} MB`);
|
|
273
|
+
await sweepStaging().catch(() => 0);
|
|
274
|
+
const staging = await createStaging();
|
|
275
|
+
try {
|
|
276
|
+
await receive(req, staging.file, maxUpload);
|
|
277
|
+
const info = await inspectPluginTarball(staging.file);
|
|
278
|
+
const installed = info.name
|
|
279
|
+
? (await installedGlobalPlugins(configHome())).find((item) => item.name === info.name)
|
|
280
|
+
: undefined;
|
|
281
|
+
const compatible = !info.sdkRange || !options.sdkVersion
|
|
282
|
+
? true
|
|
283
|
+
: satisfiesRange(options.sdkVersion, info.sdkRange);
|
|
284
|
+
const body = {
|
|
285
|
+
...(info.isAlisioPlugin ? { stagingId: staging.id } : {}),
|
|
286
|
+
...(info.name ? { package: info.name } : {}),
|
|
287
|
+
...(info.version ? { version: info.version } : {}),
|
|
288
|
+
...(info.description ? { description: info.description } : {}),
|
|
289
|
+
...(info.author ? { author: info.author } : {}),
|
|
290
|
+
...(info.license ? { license: info.license } : {}),
|
|
291
|
+
...(info.sdkRange ? { sdkRange: info.sdkRange } : {}),
|
|
292
|
+
compatible,
|
|
293
|
+
isAlisioPlugin: info.isAlisioPlugin,
|
|
294
|
+
reasons: info.reasons,
|
|
295
|
+
lifecycleScripts: info.lifecycleScripts,
|
|
296
|
+
...(installed
|
|
297
|
+
? {
|
|
298
|
+
installed: {
|
|
299
|
+
version: installed.version,
|
|
300
|
+
relation: relationOf(info.version, installed.version),
|
|
301
|
+
},
|
|
302
|
+
}
|
|
303
|
+
: {}),
|
|
304
|
+
size: info.size,
|
|
305
|
+
};
|
|
306
|
+
// Nothing to install from an invalid file: it does not stay on disk.
|
|
307
|
+
if (!info.isAlisioPlugin)
|
|
308
|
+
await removeStaging(staging.id);
|
|
309
|
+
return { body };
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
await removeStaging(staging.id);
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
router.delete("/api/plugins/local/staged/:stagingId", async ({ params }) => {
|
|
317
|
+
const id = params.stagingId ?? "";
|
|
318
|
+
if (!isStagingId(id))
|
|
319
|
+
throw new HttpError("validation_failed", "Invalid upload id", { fields: ["stagingId"] });
|
|
320
|
+
await removeStaging(id);
|
|
321
|
+
return { body: { removed: true } };
|
|
322
|
+
});
|
|
323
|
+
router.post("/api/plugins/local/install", async ({ req }) => {
|
|
324
|
+
const input = validate(await readJson(req), {
|
|
325
|
+
stagingId: { check: is.nonEmpty(64), required: true },
|
|
326
|
+
confirmed: { check: (v) => v === true, required: true },
|
|
327
|
+
});
|
|
328
|
+
refuseReadOnly("Installing plugins");
|
|
329
|
+
if (!isStagingId(input.stagingId))
|
|
330
|
+
throw new HttpError("validation_failed", "Invalid upload id", { fields: ["stagingId"] });
|
|
331
|
+
const file = stagingFile(input.stagingId);
|
|
332
|
+
if (!(await stat(file).then(() => true, () => false)))
|
|
333
|
+
throw new HttpError("not_found", "The upload was not found or has expired: select the file again");
|
|
334
|
+
const info = await inspectPluginTarball(file);
|
|
335
|
+
if (!info.isAlisioPlugin) {
|
|
336
|
+
await removeStaging(input.stagingId);
|
|
337
|
+
throw new HttpError("validation_failed", `Not a valid Alisio plugin package: ${info.reasons.join("; ")}`);
|
|
338
|
+
}
|
|
339
|
+
if (mutating)
|
|
340
|
+
throw new HttpError("runs_active", "Another plugin install or uninstall is in progress: wait for it to finish, then retry");
|
|
341
|
+
mutating = true;
|
|
342
|
+
const job = jobs.create();
|
|
343
|
+
job.start("uploading");
|
|
344
|
+
void (async () => {
|
|
345
|
+
const loaded = info.name ? codeLoaded(info.name) : false;
|
|
346
|
+
try {
|
|
347
|
+
const result = await installLocalPluginTarball({
|
|
348
|
+
tarballPath: file,
|
|
349
|
+
configHome: configHome(),
|
|
350
|
+
...(options.installRunner ? { runner: options.installRunner } : {}),
|
|
351
|
+
onPhase: (phase) => job.start(phase),
|
|
352
|
+
});
|
|
353
|
+
job.start("reloading");
|
|
354
|
+
if (loaded)
|
|
355
|
+
pendingRestart.add(result.packageName);
|
|
356
|
+
const deferred = await applyToWorkspaces();
|
|
357
|
+
job.start("cleanup");
|
|
358
|
+
await removeStaging(input.stagingId);
|
|
359
|
+
const body = {
|
|
360
|
+
package: result.packageName,
|
|
361
|
+
installedVersion: result.installedVersion,
|
|
362
|
+
alreadyInstalled: result.alreadyInstalled,
|
|
363
|
+
updated: result.updated,
|
|
364
|
+
...(result.previousVersion ? { previousVersion: result.previousVersion } : {}),
|
|
365
|
+
restartPending: deferred,
|
|
366
|
+
restartRequired: loaded,
|
|
367
|
+
};
|
|
368
|
+
job.succeed(body);
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
await removeStaging(input.stagingId).catch(() => { });
|
|
372
|
+
const text = errorText(error).slice(0, MAX_ERROR_CHARS);
|
|
373
|
+
const tail = error.logTail;
|
|
374
|
+
job.fail(text, tail);
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
mutating = false;
|
|
378
|
+
}
|
|
379
|
+
})();
|
|
380
|
+
return { status: 202, body: { jobId: job.id } };
|
|
381
|
+
});
|
|
382
|
+
router.get("/api/plugins/jobs/:jobId", ({ params }) => {
|
|
383
|
+
const job = jobs.get(params.jobId ?? "");
|
|
384
|
+
if (!job)
|
|
385
|
+
throw new HttpError("not_found", "Job not found or expired");
|
|
386
|
+
return { body: job.view() };
|
|
387
|
+
});
|
|
388
|
+
// ---- Uninstall ----------------------------------------------------------------------------
|
|
389
|
+
router.delete("/api/plugins/package/:name", async ({ params }) => {
|
|
390
|
+
const name = params.name ?? "";
|
|
391
|
+
let parsed;
|
|
392
|
+
try {
|
|
393
|
+
parsed = parsePluginSpec(name);
|
|
394
|
+
if (parsed.version !== undefined || parsed.name !== name)
|
|
395
|
+
throw new Error("not a bare name");
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
throw new HttpError("validation_failed", "Invalid package name", { fields: ["name"] });
|
|
399
|
+
}
|
|
400
|
+
refuseReadOnly("Uninstalling plugins");
|
|
401
|
+
const builtin = (ctx.base.builtins ?? []).some((plugin) => name === plugin.id || name === `@alisio/plugin-${plugin.id}`);
|
|
402
|
+
if (builtin)
|
|
403
|
+
throw new HttpError("not_manageable", "Built-in plugins cannot be uninstalled");
|
|
404
|
+
const installed = await installedGlobalPlugins(configHome());
|
|
405
|
+
if (!installed.some((item) => item.name === name)) {
|
|
406
|
+
const project = ctx.workspaces
|
|
407
|
+
.entries()
|
|
408
|
+
.some((entry) => entry.app.pluginCatalog().some((plugin) => plugin.spec === name));
|
|
409
|
+
if (project)
|
|
410
|
+
throw new HttpError("not_manageable", "This plugin is not installed in the global plugins directory: remove it from your project configuration");
|
|
411
|
+
throw new HttpError("not_found", "Plugin package is not installed");
|
|
412
|
+
}
|
|
413
|
+
return exclusive(async () => {
|
|
414
|
+
let removed;
|
|
415
|
+
try {
|
|
416
|
+
removed = await uninstallPlugin({
|
|
417
|
+
packageName: name,
|
|
418
|
+
configHome: configHome(),
|
|
419
|
+
...(options.installRunner ? { runner: options.installRunner } : {}),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
const text = errorText(error).slice(0, MAX_ERROR_CHARS);
|
|
424
|
+
throw new HttpError(text.includes(NPM_NOT_FOUND_MESSAGE) ? "validation_failed" : "provider_unavailable", text);
|
|
425
|
+
}
|
|
426
|
+
pendingRestart.delete(name);
|
|
427
|
+
const deferred = await applyToWorkspaces();
|
|
428
|
+
const body = {
|
|
429
|
+
package: removed.packageName,
|
|
430
|
+
removed: removed.removed,
|
|
431
|
+
restartPending: deferred,
|
|
432
|
+
};
|
|
433
|
+
return { body };
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Router } from "../http/router.ts";
|
|
2
|
+
export interface RestartRouteOptions {
|
|
3
|
+
/** Present only when a supervisor will respawn this process. */
|
|
4
|
+
restart?: () => void;
|
|
5
|
+
readOnly?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function registerRestartRoutes(router: Router, options: RestartRouteOptions): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET|POST /api/server/restart`: lets the UI restart the host when a supervisor (the desktop app)
|
|
3
|
+
* respawns it. An update of a plugin that is already loaded only takes effect in a new process,
|
|
4
|
+
* because Node keeps imported modules for the process lifetime. Without a supervisor nothing is
|
|
5
|
+
* exposed beyond `available: false`: a bare `alisio serve` must be restarted by its owner.
|
|
6
|
+
*/
|
|
7
|
+
import { readJson } from "../http/body.js";
|
|
8
|
+
import { HttpError } from "../http/errors.js";
|
|
9
|
+
import { validate } from "../schemas.js";
|
|
10
|
+
export function registerRestartRoutes(router, options) {
|
|
11
|
+
router.get("/api/server/restart", () => ({
|
|
12
|
+
body: { available: !!options.restart && !options.readOnly },
|
|
13
|
+
}));
|
|
14
|
+
router.post("/api/server/restart", async ({ req, res }) => {
|
|
15
|
+
validate(await readJson(req), { confirmed: { check: (v) => v === true, required: true } });
|
|
16
|
+
if (options.readOnly)
|
|
17
|
+
throw new HttpError("capability_ceiling", "Restarting is unavailable under --read-only");
|
|
18
|
+
if (!options.restart)
|
|
19
|
+
throw new HttpError("not_manageable", "This server has no supervisor to restart it: restart `alisio serve` yourself");
|
|
20
|
+
const restart = options.restart;
|
|
21
|
+
res.once("finish", () => restart());
|
|
22
|
+
return { body: { restarting: true } };
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -42,6 +42,14 @@ export function registerSessionViewRoutes(router, ctx) {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
});
|
|
45
|
+
// The Context Governor's explanation (spec 012 phase 5): the one `ContextReport` that `/context`
|
|
46
|
+
// in the TUI and the web panel render. Read only; ids, counts and metadata, never content. It
|
|
47
|
+
// lives beside the older `/context` (the ring's estimate), whose body stays as it was.
|
|
48
|
+
router.get("/api/sessions/:sid/context/report", async ({ params }) => {
|
|
49
|
+
const session = sessions.get(params.sid ?? "");
|
|
50
|
+
const { app } = await sessions.app(session);
|
|
51
|
+
return { body: app.contextReport(session.id) };
|
|
52
|
+
});
|
|
45
53
|
// Session log: one `RunEvent` line per durable event (events.seq order, `alisio run --json`
|
|
46
54
|
// format with the global `eventId`), then one `{"type":"message"}` line per stored message.
|
|
47
55
|
router.get("/api/sessions/:sid/export", ({ params, res }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,b as t,g as n,y as r}from"./i18n-
|
|
1
|
+
import{a as e,b as t,g as n,y as r}from"./i18n-DqOZSMVh.js";import{a as i,d as a,j as o,o as s,t as c}from"./app-CGSX2FCK.js";import{t as l}from"./jsxRuntime-BWhXelbu.js";import{t as u}from"./icons-BZSZMbqh.js";import{b as d,t as f,u as p}from"./agents.module-C5jXUIc1.js";import{t as m}from"./settings.module-ErkNzIk4.js";import{a as h,c as g,i as _,l as v,r as y}from"./shared-C2fqplXu.js";function b(){let b=g(),x=v(b?()=>a.agents(b.id):void 0,[b?.id],[`agents`]),S=v(()=>a.agentDefinitions(b?.id),[b?.id],[`agents`]),[C,w]=r(``),[T,E]=r(0),D=n(null);t(()=>{let e=document.activeElement;return D.current?.querySelector(`input`)?.focus(),()=>e?.focus()},[]);let O=()=>{i.value=!1},k=o.value?.agent??x.data?.find(e=>e.default)?.id,A=p(x.data??[],C),j=e=>{O(),c(e.id)},M=t=>{let n=d(t,S.data?.agents??[]);return n?l(y,{tone:n===`project`?`ok`:`off`,children:e(`agentsWin.scope.${n}`)}):l(y,{tone:`off`,children:t.source===`builtin`?e(`agentsWin.builtin`):e(`agentsWin.plugin`)})};return l(`div`,{class:m.backdrop,onMouseDown:e=>{e.target===e.currentTarget&&O()},children:l(`div`,{ref:D,class:`${m.dialog} ${f.picker}`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`agent-picker-title`,onKeyDown:e=>{if(e.key===`Escape`)e.preventDefault(),O();else if(e.key===`ArrowDown`||e.key===`ArrowUp`){e.preventDefault();let t=e.key===`ArrowDown`?1:-1;E(e=>A.length?(e+t+A.length)%A.length:0)}else if(e.key===`Enter`&&e.target.tagName===`INPUT`){e.preventDefault();let t=A[Math.min(T,A.length-1)];t&&j(t)}},children:[l(`div`,{class:f.pickerHead,children:[l(`div`,{class:f.listHead,children:[l(`div`,{children:[l(`h3`,{class:m.pageTitle,id:`agent-picker-title`,children:e(`agentsWin.picker`)}),l(`p`,{class:m.note,children:e(`agentsWin.pickerLead`)})]}),l(`button`,{type:`button`,class:`icon-btn`,"aria-label":e(`common.close`),onClick:O,children:l(u,{name:`x`,size:18})})]}),l(_,{value:C,label:e(`agentsWin.pickerSearch`),onInput:e=>{w(e),E(0)}})]}),l(`div`,{style:{padding:`0 18px`},children:l(h,{loading:x.loading&&!x.data,error:x.error,empty:b?void 0:`settings.noWorkspace`})}),l(`ul`,{class:f.pickerList,"aria-label":e(`agentsWin.picker`),children:[x.data&&!A.length?l(`li`,{class:m.note,children:e(`agentsWin.pickerEmpty`,{query:C})}):null,A.map((t,n)=>l(`li`,{children:l(`button`,{type:`button`,class:f.pickerItem,"data-selected":n===T,"aria-current":t.id===k?`true`:void 0,onMouseEnter:()=>E(n),onClick:()=>j(t),children:[l(`span`,{class:f.pickerTitle,children:[t.name,M(t),t.id===k?l(y,{tone:`ok`,children:e(`agentsWin.current`)}):null,t.id===`build`?l(y,{tone:`off`,children:e(`agentsWin.defaultAgent`)}):null,t.readOnly?l(y,{tone:`warn`,children:e(`agents.readOnly`)}):null]}),l(`span`,{class:m.cardText,children:t.description})]})},t.id)),l(`li`,{children:l(`button`,{type:`button`,class:f.pickerItem,onClick:()=>{O(),s.value=!0},children:l(`span`,{class:f.pickerTitle,children:[l(u,{name:`users`,size:15}),e(`agentsWin.manage`)]})})})]})]})})}export{b as AgentPicker};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`);return g(`section`,{class:j.code,"aria-label":n(`agentsWin.config`),children:[g(`div`,{class:j.codeHead,children:[g(`span`,{children:n(`agentsWin.config`)}),g(v,{text:()=>e.text,label:n(`agentsWin.configCopy`)})]}),g(`p`,{class:M.note,style:{margin:`8px 16px 0`},children:n(`agentsWin.configLead`)}),g(`pre`,{class:j.codeBody,children:g(`code`,{children:t.map((e,t)=>g(`span`,{class:j.codeLine,children:[g(`span`,{class:j.lineNo,"aria-hidden":`true`,children:t+1}),g(`span`,{children:y(e).map((e,t)=>g(`span`,{class:j[`tok-${e.kind}`],children:e.text},t))})]},t))})})]})}function le(e){let[t,i]=s(Date.now()),o=a(null);return r(()=>{o.current?.focus();let e=setInterval(()=>i(Date.now()),1e3);return()=>clearInterval(e)},[]),g(`div`,{class:j.overlay,role:`alertdialog`,"aria-modal":`true`,"aria-labelledby":`building-title`,"aria-describedby":`building-body`,onKeyDown:t=>{t.key===`Escape`&&(t.preventDefault(),e.onCancel())},children:g(`div`,{class:j.overlayCard,children:[g(`div`,{class:j.spinner,"aria-hidden":`true`}),g(`h3`,{class:j.overlayTitle,id:`building-title`,children:n(`agentsWin.building`)}),g(`p`,{class:M.note,id:`building-body`,role:`status`,children:n(`agentsWin.buildingBody`,{model:e.model})}),g(`div`,{class:j.progress,"aria-hidden":`true`,children:g(`div`,{class:j.progressBar})}),g(`p`,{class:M.meta,children:n(`agentsWin.buildingElapsed`,{seconds:Math.max(0,Math.round((t-e.started)/1e3))})}),g(`div`,{class:j.overlayActions,children:g(`button`,{ref:o,type:`button`,class:M.secondary,onClick:e.onCancel,children:n(`common.cancel`)})})]})})}function ue(e){let t=a(null);return r(()=>t.current?.focus(),[]),g(`div`,{class:j.overlay,role:`alertdialog`,"aria-modal":`true`,"aria-labelledby":`created-title`,onKeyDown:t=>{t.key===`Escape`&&(t.preventDefault(),e.onStay())},children:g(`div`,{class:j.overlayCard,children:[g(`h3`,{class:j.overlayTitle,id:`created-title`,children:n(`agentsWin.createdTitle`,{name:e.name})}),g(`p`,{class:M.note,children:e.live?n(`agentsWin.createdBody`):n(`agentsWin.createdRestart`)}),g(`div`,{class:j.overlayActions,children:[e.live?g(`button`,{ref:t,type:`button`,class:M.primary,onClick:e.onTry,children:n(`agentsWin.tryIt`)}):null,g(`button`,{ref:e.live?void 0:t,type:`button`,class:M.secondary,onClick:e.onStay,children:n(`agentsWin.stay`)})]})]})})}function B(i){let{workspace:a,models:o}=i,[v,y]=s(i.initial),[D,te]=s(i.saved),[P,I]=s(`setup`),[L,B]=s(``),[V,H]=s(),[U,W]=s(),[de,G]=s(!1),[K,q]=s(),[fe,pe]=s(!b());r(()=>{if(!v.model&&o?.length){let e=o.find(e=>e.active)??o[0];e&&y(t=>C({...t,model:e.reference},e.capabilities))}},[o]);let me=!D||T(v)!==T(D);r(()=>i.onDirty(me&&!!D),[me,D]);let J=re(o,v.model),Y=F(D?.id?()=>u.agentSessions(D.id):void 0,[D?.id],[`agents`]),X=i.overview?.scopes??(a?[`project`,`global`]:[`global`]),Z=e=>y(t=>({...t,...e})),he=t(()=>x(v,{origin:typeof location<`u`?location.origin:`http://127.0.0.1:4096`,...a?{workspace:a.id}:{}}),[v,a?.id]),ge=o?.find(e=>e.active)?.id??`Alisio`,Q=!!v.instructions.trim(),_e=async e=>{G(!0);try{let t={...w(e),scope:e.scope,...a?{workspace:a.id}:{}},r=e.id?await u.updateAgent(e.id,t):await u.createAgent(t),o=ee(r.agent);y(o),te(o),i.onSaved(o),e.scope===`project`&&m(),p(n(r.live?`agentsWin.saved`:`agentsWin.savedRestart`)),e.id||q({form:o,live:r.live})}catch(e){p(n(`agentsWin.saveFailed`,{message:c(e)}))}finally{G(!1)}},ve=async()=>{if(!a)return;let e=new AbortController;W(void 0),H({controller:e,started:Date.now()});let t;try{let r=await u.draftAgent({workspace:a.id,description:L.trim(),...Q?{base:{name:v.name,description:v.description,instructions:v.instructions}}:{}},e.signal);t=C(ne(v,r),J),y(t),B(``),p(n(`agentsWin.buildDone`,{model:r.generatedBy,guidance:r.guidance}))}catch(t){e.signal.aborted||W(c(t))}finally{H(void 0)}t&&!t.id&&k(t,void 0)&&await _e(t)},$=oe(v.name),ye=ie({saved:!!D?.id,workspace:!!a,sessions:Y.data?.items??[]}),be={define:[`agentsWin.step.define`,`agentsWin.step.defineBody`],runtime:[`agentsWin.step.runtime`,`agentsWin.step.runtimeBody`],session:[`agentsWin.step.session`,`agentsWin.step.sessionBody`],events:[`agentsWin.step.events`,`agentsWin.step.eventsBody`]},xe=async e=>{a&&e.id&&await f(a.id,e.id,e.model||void 0)&&(h.value=!1)};return g(`div`,{style:{position:`relative`},children:[g(`div`,{class:M.tabs,role:`tablist`,"aria-label":n(`agentsWin.title`),children:[`setup`,`sessions`].map(e=>g(`button`,{type:`button`,role:`tab`,class:M.tab,"aria-selected":P===e,onClick:()=>I(e),children:n(`agentsWin.${e}`)},e))}),P===`sessions`?g(`div`,{role:`tabpanel`,children:D?.id?g(e,{children:[g(N,{loading:Y.loading&&!Y.data,error:Y.error}),Y.data&&!Y.data.items.length?g(`p`,{class:M.note,children:n(`agentsWin.sessionsEmpty`)}):null,g(`ul`,{class:M.rows,children:(Y.data?.items??[]).map(e=>g(`li`,{class:M.row,children:g(`button`,{type:`button`,class:j.rowButton,onClick:()=>{h.value=!1,l(e.id)},children:[g(`span`,{class:M.cardName,children:e.title??n(`agentsWin.untitled`)}),g(`p`,{class:M.meta,children:[e.model,` · `,e.workspace]})]})},e.id))}),a?g(`button`,{type:`button`,class:`${M.primary} ${M.addButton}`,onClick:()=>void xe(D),children:n(`agentsWin.startSession`)}):g(`p`,{class:M.note,children:n(`agentsWin.noWorkspace`)})]}):g(`p`,{class:M.note,children:n(`agentsWin.sessionsUnsaved`)})}):g(`div`,{class:j.columns,role:`tabpanel`,children:[g(`div`,{class:j.column,children:[a?g(`section`,{class:j.assist,"aria-labelledby":`assist-title`,children:[g(`h4`,{class:j.assistTitle,id:`assist-title`,children:[g(_,{name:`sparkle`,size:16}),n(Q?`agentsWin.assistRefine`:`agentsWin.assistTitle`)]}),g(`p`,{class:M.note,children:n(Q?`agentsWin.assistRefineLead`:`agentsWin.assistLead`)}),g(`textarea`,{class:j.control,style:{minHeight:`64px`},value:L,placeholder:n(`agentsWin.assistPlaceholder`),"aria-label":n(`agentsWin.assistTitle`),maxLength:2e3,onInput:e=>B(e.target.value)}),U?g(`p`,{class:M.errorText,role:`alert`,children:n(`agentsWin.buildFailed`,{message:U})}):null,g(`div`,{class:j.assistActions,children:g(`button`,{type:`button`,class:M.primary,disabled:!!V||!Q&&!L.trim(),onClick:()=>void ve(),children:n(Q?`agentsWin.assistRefine`:`agentsWin.assistCreate`)})})]}):null,g(se,{title:n(`agentsWin.definition`),children:[g(z,{label:n(`agentsWin.name`),error:$?n(`agentsWin.nameError.${$}`):void 0,children:g(`input`,{class:j.control,value:v.name,maxLength:80,"aria-invalid":$?`true`:void 0,onInput:e=>Z({name:e.target.value})})}),g(z,{label:n(`agentsWin.description`),children:g(`input`,{class:j.control,value:v.description,maxLength:300,placeholder:n(`agentsWin.descriptionPlaceholder`),onInput:e=>Z({description:e.target.value})})}),g(z,{label:n(`agentsWin.instructions`),hint:`${v.instructions.length} / 24000`,children:g(`textarea`,{class:j.control,value:v.instructions,maxLength:24e3,placeholder:n(`agentsWin.instructionsPlaceholder`),onInput:e=>Z({instructions:e.target.value})})}),!v.id&&X.length>1?g(z,{label:n(`agentsWin.scope`),hint:n(`agentsWin.scopeHint`),children:g(`select`,{class:j.control,value:v.scope,title:i.overview?.dirs[v.scope]??``,onChange:e=>Z({scope:e.target.value}),children:X.map(e=>g(`option`,{value:e,title:i.overview?.dirs[e]??``,children:[n(`agentsWin.scope.${e}`),i.overview?.dirs[e]?` — ${ae(e,i.overview.dirs[e])}`:``]},e))})}):g(`p`,{class:M.meta,children:[n(`agentsWin.scope`),`: `,n(`agentsWin.scope.${v.scope}`)]}),v.scope===`project`&&i.overview&&!i.overview.trusted?g(`div`,{class:M.callout,role:`note`,children:[g(_,{name:`shield`,size:16}),g(`div`,{class:M.stack,children:[g(`span`,{children:n(`agentsWin.scopeUntrusted`)}),a?g(`button`,{type:`button`,class:M.secondary,style:{justifySelf:`start`},onClick:async()=>{await d(a,!0)&&i.onTrusted()},children:n(`trust.grant`)}):null]})]}):null]}),g(se,{title:n(`agentsWin.modelSection`),children:[g(z,{label:n(`agentsWin.model`),children:g(`select`,{class:j.control,value:v.model,onChange:e=>{let t=e.target.value;y(e=>C({...e,model:t},re(o,t)))},children:[g(`option`,{value:``,disabled:!0,children:n(`agentsWin.chooseModel`)}),v.model&&!o?.some(e=>e.reference===v.model)?g(`option`,{value:v.model,children:v.model}):null,(o??[]).map(e=>g(`option`,{value:e.reference,children:[e.active?n(`agentsWin.modelActive`,{name:e.name??e.id}):e.name??e.id,` `,`· `,e.providerName,e.labels.length?` — ${e.labels.join(` · `)}`:``]},e.reference))]})}),i.modelsError?g(`p`,{class:M.errorText,children:i.modelsError}):J.known?null:g(`p`,{class:j.fieldHint,children:n(`agentsWin.capsUnknown`)}),g(`div`,{class:j.settingsGrid,children:[g(z,{label:n(`agentsWin.textFormat`),children:g(`select`,{class:j.control,value:v.format,onChange:e=>Z({format:e.target.value}),children:A.filter(e=>J.textFormats.includes(e)).map(e=>g(`option`,{value:e,children:n(`agentsWin.format.${e}`)},e))})}),g(z,{label:n(`agentsWin.effort`),hint:J.reasoning?void 0:n(`agentsWin.unsupported`),children:g(`select`,{class:j.control,value:v.effort,disabled:!J.reasoning,onChange:e=>Z({effort:e.target.value}),children:[g(`option`,{value:``,children:[n(`agentsWin.modelDefault`),J.defaultEffort?` (${R(J.defaultEffort)})`:``]}),J.effortLevels.map(e=>g(`option`,{value:e,children:R(e)},e))]})}),g(z,{label:n(`agentsWin.verbosity`),hint:J.verbosity?void 0:n(`agentsWin.unsupported`),children:g(`select`,{class:j.control,value:v.verbosity,disabled:!J.verbosity,onChange:e=>Z({verbosity:e.target.value}),children:[g(`option`,{value:``,children:n(`agentsWin.modelDefault`)}),E.map(e=>g(`option`,{value:e,children:R(e)},e))]})}),g(z,{label:n(`agentsWin.summary`),hint:J.summary?void 0:n(`agentsWin.unsupported`),children:g(`select`,{class:j.control,value:v.summary,disabled:!J.summary,onChange:e=>Z({summary:e.target.value}),children:[g(`option`,{value:``,children:n(`agentsWin.modelDefault`)}),O.map(e=>g(`option`,{value:e,children:n(`agentsWin.summary.${e}`)},e))]})})]})]}),g(`button`,{type:`button`,class:`${M.primary} ${j.saveButton}`,disabled:de||!!V||!k(v,D),onClick:()=>void _e(v),children:n(`agentsWin.save`)})]}),g(`div`,{class:j.column,children:[g(ce,{text:he}),fe?g(`section`,{class:j.stepper,"aria-labelledby":`getting-started`,children:[g(`h4`,{class:j.stepperHead,id:`getting-started`,children:[n(`agentsWin.gettingStarted`),g(`button`,{type:`button`,class:`icon-btn`,"aria-label":n(`agentsWin.dismiss`),title:n(`agentsWin.dismiss`),onClick:()=>{S(),pe(!1)},children:g(_,{name:`x`,size:15})})]}),g(`ol`,{class:j.steps,children:ye.map((e,t)=>{let[r,i]=be[e.id];return g(`li`,{class:j.step,"data-done":e.done,children:[g(`span`,{class:j.stepNode,"aria-hidden":`true`,children:e.done?`✓`:t+1}),g(`div`,{children:[g(`p`,{class:j.stepTitle,children:[n(r),g(`span`,{class:`sr-only`,children:e.done?` ✓`:``})]}),g(`p`,{class:j.stepText,children:n(i)})]})]},e.id)})})]}):null]})]}),V?g(le,{model:ge,started:V.started,onCancel:()=>V.controller.abort()}):null,K?g(ue,{name:K.form.name,live:K.live&&!!a,onTry:()=>{let e=K.form;q(void 0),xe(e)},onStay:()=>q(void 0)}):null]})}var V=`alisio.agents.maximized`,H=`button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex='0']`;function U(t){let{agent:r}=t;return g(e,{children:[g(I,{tone:r.scope===`project`?`ok`:`off`,children:n(`agentsWin.scope.${r.scope}`)}),r.overridesGlobal?g(I,{tone:`warn`,children:n(`agentsWin.overrides`)}):null,r.overriddenByProject?g(I,{tone:`off`,children:n(`agentsWin.overridden`)}):null]})}function W(){let t=P(),[l,d]=s({kind:`list`}),[f,m]=s(!1),[v,y]=s(()=>i(V)===`1`),b=a(null),x=F(()=>u.agentDefinitions(t?.id),[t?.id],[`agents`]),S=F(()=>u.agentTemplates(),[]),C=F(t?()=>u.agentModels(t.id):void 0,[t?.id]);r(()=>{let e=document.activeElement;return b.current?.querySelector(`button`)?.focus(),()=>e?.focus()},[]);let w=x.data?.defaultScope??(t?`project`:`global`),T=()=>C.data?.find(e=>e.active)?.reference??``,E=()=>l.kind===`editor`&&f&&!window.confirm(n(`agentsWin.unsavedLeave`))?!1:(m(!1),d({kind:`list`}),!0),O=()=>{(l.kind!==`editor`||!f||window.confirm(n(`agentsWin.unsavedLeave`)))&&(h.value=!1)},k=e=>d({kind:`editor`,key:`template:${e.id}:${Date.now()}`,form:te(e,w,T()),assist:!0}),ne=async e=>{if(window.confirm(n(`agentsWin.deleteConfirm`,{path:e.path})))try{let r=await u.deleteAgent(e.id,e.scope,t?.id);p(n(r.live?`agentsWin.deleted`:`agentsWin.deletedRestart`)),x.reload()}catch(e){p(c(e))}},A=x.data?.agents??[];return g(`div`,{class:M.backdrop,onMouseDown:e=>{e.target===e.currentTarget&&O()},children:g(`div`,{ref:b,class:`${M.dialog} ${j.shell}${v?` ${j.maximized}`:``}`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`agents-title`,onKeyDown:e=>{if(e.key===`Escape`&&!e.defaultPrevented&&(e.preventDefault(),l.kind===`editor`?E():O()),e.key===`Tab`){let t=[...b.current?.querySelectorAll(H)??[]],n=t[0],r=t.at(-1);e.shiftKey&&document.activeElement===n?(e.preventDefault(),r?.focus()):!e.shiftKey&&document.activeElement===r&&(e.preventDefault(),n?.focus())}},children:g(`div`,{class:`${M.content} ${j.scroll}`,children:[g(`div`,{class:M.topBar,children:[g(`nav`,{class:j.breadcrumb,"aria-label":n(`agentsWin.title`),children:l.kind===`editor`?g(e,{children:[g(`button`,{type:`button`,class:j.crumbLink,onClick:E,children:n(`agentsWin.back`)}),g(_,{name:`chevronRight`,size:14}),g(`span`,{class:j.crumbCurrent,id:`agents-title`,"aria-current":`page`,children:l.form.id?l.form.name:n(`agentsWin.newAgent`)})]}):g(`span`,{class:j.crumbCurrent,id:`agents-title`,children:n(`agentsWin.title`)})}),g(`button`,{type:`button`,class:`icon-btn`,"aria-label":n(v?`agentsWin.restore`:`agentsWin.maximize`),title:n(v?`agentsWin.restore`:`agentsWin.maximize`),"aria-pressed":v,onClick:()=>{let e=!v;y(e),o(V,e?`1`:void 0)},children:g(_,{name:v?`shrink`:`expand`,size:16})}),g(`button`,{type:`button`,class:`icon-btn`,"aria-label":n(`common.close`),onClick:O,children:g(_,{name:`x`,size:18})})]}),l.kind===`editor`?g(B,{initial:l.form,...l.saved?{saved:l.saved}:{},assist:l.assist,workspace:t,overview:x.data,models:C.data,modelsError:C.error,onDirty:m,onTrusted:()=>{x.reload(),C.reload()},onSaved:e=>{x.reload(),d(t=>t.kind===`editor`?{...t,form:e,saved:e}:t)}},l.key):g(e,{children:[g(`header`,{class:`${M.intro} ${j.listHead}`,children:[g(`div`,{children:[g(`h3`,{class:M.pageTitle,children:n(`agentsWin.title`)}),g(`p`,{class:M.lead,children:n(`agentsWin.lead`)}),t?null:g(`p`,{class:M.note,children:n(`agentsWin.noWorkspace`)})]}),g(`button`,{type:`button`,class:M.primary,onClick:()=>d({kind:`editor`,key:`new:${Date.now()}`,form:D(w,T()),assist:!0}),children:n(`agentsWin.create`)})]}),g(`h4`,{class:M.listTitle,children:[n(`agentsWin.yours`),g(`span`,{class:M.count,children:A.length})]}),g(N,{loading:x.loading&&!x.data,error:x.error}),x.data&&!A.length?g(`p`,{class:M.note,children:n(`agentsWin.none`)}):null,g(`ul`,{class:M.rows,children:A.map(e=>g(`li`,{class:M.row,"data-muted":e.overriddenByProject?`true`:void 0,children:[g(`button`,{type:`button`,class:j.rowButton,onClick:()=>{let t=ee(e);d({kind:`editor`,key:`${e.scope}:${e.id}`,form:t,saved:t,assist:!1})},children:[g(`span`,{class:M.rowTitle,children:[g(`span`,{class:M.cardName,children:e.name}),g(U,{agent:e})]}),g(`p`,{class:M.cardText,children:e.description}),g(`p`,{class:M.meta,children:[e.model||`—`,` · `,g(`code`,{children:e.id})]})]}),g(`div`,{class:M.rowActions,children:g(`button`,{type:`button`,class:`icon-btn`,"aria-label":`${n(`agentsWin.delete`)} ${e.name}`,title:n(`agentsWin.delete`),onClick:()=>void ne(e),children:g(_,{name:`archive`,size:16})})})]},`${e.scope}:${e.id}`))}),g(`h4`,{class:M.listTitle,children:n(`agentsWin.templates`)}),g(N,{loading:S.loading&&!S.data,error:S.error}),g(`ul`,{class:M.grid,children:(S.data??[]).map(e=>g(`li`,{children:g(`button`,{type:`button`,class:j.templateCard,onClick:()=>k(e),children:[g(`span`,{class:j.templateName,children:e.name}),g(`span`,{class:j.templateText,children:e.description})]})},e.id))})]})]})})})}export{W as AgentsModal};
|
|
1
|
+
import{E as e,_ as t,a as n,b as r,c as i,g as a,l as o,y as s}from"./i18n-DqOZSMVh.js";import{K as c,Ut as l,d as u,gt as d,it as f,o as p,pt as m,vt as h}from"./app-CGSX2FCK.js";import{t as g}from"./jsxRuntime-BWhXelbu.js";import{t as _}from"./icons-BZSZMbqh.js";import{t as v}from"./CopyButton-Bh3Dsk-o.js";import{S as y,_ as b,a as x,c as S,d as C,f as ee,g as w,h as T,i as E,l as D,m as te,n as O,o as k,p as ne,r as A,s as re,t as j,v as ie,x as ae,y as oe}from"./agents.module-C5jXUIc1.js";import{t as M}from"./settings.module-ErkNzIk4.js";import{a as N,c as P,l as F,r as I}from"./shared-C2fqplXu.js";var L=new Set([`minimal`,`low`,`medium`,`high`,`xhigh`]),R=e=>L.has(e)?n(`agentsWin.level.${e}`):e;function se(e){let[t,n]=s(!0);return g(`section`,{class:j.section,children:[g(`button`,{type:`button`,class:j.sectionHead,"aria-expanded":t,onClick:()=>n(!t),children:[g(_,{name:t?`chevronDown`:`chevronRight`,size:15}),e.title]}),t?g(`div`,{class:j.sectionBody,children:e.children}):null]})}function z(e){return g(`label`,{class:j.field,children:[g(`span`,{class:j.fieldLabel,children:[e.label,e.hint?g(`span`,{class:j.fieldHint,children:e.hint}):null]}),e.children,e.error?g(`span`,{class:j.fieldError,children:e.error}):null]})}function ce(e){let t=e.text.split(`
|
|
2
|
+
`);return g(`section`,{class:j.code,"aria-label":n(`agentsWin.config`),children:[g(`div`,{class:j.codeHead,children:[g(`span`,{children:n(`agentsWin.config`)}),g(v,{text:()=>e.text,label:n(`agentsWin.configCopy`)})]}),g(`p`,{class:M.note,style:{margin:`8px 16px 0`},children:n(`agentsWin.configLead`)}),g(`pre`,{class:j.codeBody,children:g(`code`,{children:t.map((e,t)=>g(`span`,{class:j.codeLine,children:[g(`span`,{class:j.lineNo,"aria-hidden":`true`,children:t+1}),g(`span`,{children:y(e).map((e,t)=>g(`span`,{class:j[`tok-${e.kind}`],children:e.text},t))})]},t))})})]})}function le(e){let[t,i]=s(Date.now()),o=a(null);return r(()=>{o.current?.focus();let e=setInterval(()=>i(Date.now()),1e3);return()=>clearInterval(e)},[]),g(`div`,{class:j.overlay,role:`alertdialog`,"aria-modal":`true`,"aria-labelledby":`building-title`,"aria-describedby":`building-body`,onKeyDown:t=>{t.key===`Escape`&&(t.preventDefault(),e.onCancel())},children:g(`div`,{class:j.overlayCard,children:[g(`div`,{class:j.spinner,"aria-hidden":`true`}),g(`h3`,{class:j.overlayTitle,id:`building-title`,children:n(`agentsWin.building`)}),g(`p`,{class:M.note,id:`building-body`,role:`status`,children:n(`agentsWin.buildingBody`,{model:e.model})}),g(`div`,{class:j.progress,"aria-hidden":`true`,children:g(`div`,{class:j.progressBar})}),g(`p`,{class:M.meta,children:n(`agentsWin.buildingElapsed`,{seconds:Math.max(0,Math.round((t-e.started)/1e3))})}),g(`div`,{class:j.overlayActions,children:g(`button`,{ref:o,type:`button`,class:M.secondary,onClick:e.onCancel,children:n(`common.cancel`)})})]})})}function ue(e){let t=a(null);return r(()=>t.current?.focus(),[]),g(`div`,{class:j.overlay,role:`alertdialog`,"aria-modal":`true`,"aria-labelledby":`created-title`,onKeyDown:t=>{t.key===`Escape`&&(t.preventDefault(),e.onStay())},children:g(`div`,{class:j.overlayCard,children:[g(`h3`,{class:j.overlayTitle,id:`created-title`,children:n(`agentsWin.createdTitle`,{name:e.name})}),g(`p`,{class:M.note,children:e.live?n(`agentsWin.createdBody`):n(`agentsWin.createdRestart`)}),g(`div`,{class:j.overlayActions,children:[e.live?g(`button`,{ref:t,type:`button`,class:M.primary,onClick:e.onTry,children:n(`agentsWin.tryIt`)}):null,g(`button`,{ref:e.live?void 0:t,type:`button`,class:M.secondary,onClick:e.onStay,children:n(`agentsWin.stay`)})]})]})})}function B(i){let{workspace:a,models:o}=i,[v,y]=s(i.initial),[D,te]=s(i.saved),[P,I]=s(`setup`),[L,B]=s(``),[V,H]=s(),[U,W]=s(),[de,G]=s(!1),[K,q]=s(),[fe,pe]=s(!b());r(()=>{if(!v.model&&o?.length){let e=o.find(e=>e.active)??o[0];e&&y(t=>C({...t,model:e.reference},e.capabilities))}},[o]);let me=!D||T(v)!==T(D);r(()=>i.onDirty(me&&!!D),[me,D]);let J=re(o,v.model),Y=F(D?.id?()=>u.agentSessions(D.id):void 0,[D?.id],[`agents`]),X=i.overview?.scopes??(a?[`project`,`global`]:[`global`]),Z=e=>y(t=>({...t,...e})),he=t(()=>x(v,{origin:typeof location<`u`?location.origin:`http://127.0.0.1:4096`,...a?{workspace:a.id}:{}}),[v,a?.id]),ge=o?.find(e=>e.active)?.id??`Alisio`,Q=!!v.instructions.trim(),_e=async e=>{G(!0);try{let t={...w(e),scope:e.scope,...a?{workspace:a.id}:{}},r=e.id?await u.updateAgent(e.id,t):await u.createAgent(t),o=ee(r.agent);y(o),te(o),i.onSaved(o),e.scope===`project`&&f(),d(n(r.live?`agentsWin.saved`:`agentsWin.savedRestart`)),e.id||q({form:o,live:r.live})}catch(e){d(n(`agentsWin.saveFailed`,{message:l(e)}))}finally{G(!1)}},ve=async()=>{if(!a)return;let e=new AbortController;W(void 0),H({controller:e,started:Date.now()});let t;try{let r=await u.draftAgent({workspace:a.id,description:L.trim(),...Q?{base:{name:v.name,description:v.description,instructions:v.instructions}}:{}},e.signal);t=C(ne(v,r),J),y(t),B(``),d(n(`agentsWin.buildDone`,{model:r.generatedBy,guidance:r.guidance}))}catch(t){e.signal.aborted||W(l(t))}finally{H(void 0)}t&&!t.id&&k(t,void 0)&&await _e(t)},$=oe(v.name),ye=ie({saved:!!D?.id,workspace:!!a,sessions:Y.data?.items??[]}),be={define:[`agentsWin.step.define`,`agentsWin.step.defineBody`],runtime:[`agentsWin.step.runtime`,`agentsWin.step.runtimeBody`],session:[`agentsWin.step.session`,`agentsWin.step.sessionBody`],events:[`agentsWin.step.events`,`agentsWin.step.eventsBody`]},xe=async e=>{a&&e.id&&await h(a.id,e.id,e.model||void 0)&&(p.value=!1)};return g(`div`,{style:{position:`relative`},children:[g(`div`,{class:M.tabs,role:`tablist`,"aria-label":n(`agentsWin.title`),children:[`setup`,`sessions`].map(e=>g(`button`,{type:`button`,role:`tab`,class:M.tab,"aria-selected":P===e,onClick:()=>I(e),children:n(`agentsWin.${e}`)},e))}),P===`sessions`?g(`div`,{role:`tabpanel`,children:D?.id?g(e,{children:[g(N,{loading:Y.loading&&!Y.data,error:Y.error}),Y.data&&!Y.data.items.length?g(`p`,{class:M.note,children:n(`agentsWin.sessionsEmpty`)}):null,g(`ul`,{class:M.rows,children:(Y.data?.items??[]).map(e=>g(`li`,{class:M.row,children:g(`button`,{type:`button`,class:j.rowButton,onClick:()=>{p.value=!1,c(e.id)},children:[g(`span`,{class:M.cardName,children:e.title??n(`agentsWin.untitled`)}),g(`p`,{class:M.meta,children:[e.model,` · `,e.workspace]})]})},e.id))}),a?g(`button`,{type:`button`,class:`${M.primary} ${M.addButton}`,onClick:()=>void xe(D),children:n(`agentsWin.startSession`)}):g(`p`,{class:M.note,children:n(`agentsWin.noWorkspace`)})]}):g(`p`,{class:M.note,children:n(`agentsWin.sessionsUnsaved`)})}):g(`div`,{class:j.columns,role:`tabpanel`,children:[g(`div`,{class:j.column,children:[a?g(`section`,{class:j.assist,"aria-labelledby":`assist-title`,children:[g(`h4`,{class:j.assistTitle,id:`assist-title`,children:[g(_,{name:`sparkle`,size:16}),n(Q?`agentsWin.assistRefine`:`agentsWin.assistTitle`)]}),g(`p`,{class:M.note,children:n(Q?`agentsWin.assistRefineLead`:`agentsWin.assistLead`)}),g(`textarea`,{class:j.control,style:{minHeight:`64px`},value:L,placeholder:n(`agentsWin.assistPlaceholder`),"aria-label":n(`agentsWin.assistTitle`),maxLength:2e3,onInput:e=>B(e.target.value)}),U?g(`p`,{class:M.errorText,role:`alert`,children:n(`agentsWin.buildFailed`,{message:U})}):null,g(`div`,{class:j.assistActions,children:g(`button`,{type:`button`,class:M.primary,disabled:!!V||!Q&&!L.trim(),onClick:()=>void ve(),children:n(Q?`agentsWin.assistRefine`:`agentsWin.assistCreate`)})})]}):null,g(se,{title:n(`agentsWin.definition`),children:[g(z,{label:n(`agentsWin.name`),error:$?n(`agentsWin.nameError.${$}`):void 0,children:g(`input`,{class:j.control,value:v.name,maxLength:80,"aria-invalid":$?`true`:void 0,onInput:e=>Z({name:e.target.value})})}),g(z,{label:n(`agentsWin.description`),children:g(`input`,{class:j.control,value:v.description,maxLength:300,placeholder:n(`agentsWin.descriptionPlaceholder`),onInput:e=>Z({description:e.target.value})})}),g(z,{label:n(`agentsWin.instructions`),hint:`${v.instructions.length} / 24000`,children:g(`textarea`,{class:j.control,value:v.instructions,maxLength:24e3,placeholder:n(`agentsWin.instructionsPlaceholder`),onInput:e=>Z({instructions:e.target.value})})}),!v.id&&X.length>1?g(z,{label:n(`agentsWin.scope`),hint:n(`agentsWin.scopeHint`),children:g(`select`,{class:j.control,value:v.scope,title:i.overview?.dirs[v.scope]??``,onChange:e=>Z({scope:e.target.value}),children:X.map(e=>g(`option`,{value:e,title:i.overview?.dirs[e]??``,children:[n(`agentsWin.scope.${e}`),i.overview?.dirs[e]?` — ${ae(e,i.overview.dirs[e])}`:``]},e))})}):g(`p`,{class:M.meta,children:[n(`agentsWin.scope`),`: `,n(`agentsWin.scope.${v.scope}`)]}),v.scope===`project`&&i.overview&&!i.overview.trusted?g(`div`,{class:M.callout,role:`note`,children:[g(_,{name:`shield`,size:16}),g(`div`,{class:M.stack,children:[g(`span`,{children:n(`agentsWin.scopeUntrusted`)}),a?g(`button`,{type:`button`,class:M.secondary,style:{justifySelf:`start`},onClick:async()=>{await m(a,!0)&&i.onTrusted()},children:n(`trust.grant`)}):null]})]}):null]}),g(se,{title:n(`agentsWin.modelSection`),children:[g(z,{label:n(`agentsWin.model`),children:g(`select`,{class:j.control,value:v.model,onChange:e=>{let t=e.target.value;y(e=>C({...e,model:t},re(o,t)))},children:[g(`option`,{value:``,disabled:!0,children:n(`agentsWin.chooseModel`)}),v.model&&!o?.some(e=>e.reference===v.model)?g(`option`,{value:v.model,children:v.model}):null,(o??[]).map(e=>g(`option`,{value:e.reference,children:[e.active?n(`agentsWin.modelActive`,{name:e.name??e.id}):e.name??e.id,` `,`· `,e.providerName,e.labels.length?` — ${e.labels.join(` · `)}`:``]},e.reference))]})}),i.modelsError?g(`p`,{class:M.errorText,children:i.modelsError}):J.known?null:g(`p`,{class:j.fieldHint,children:n(`agentsWin.capsUnknown`)}),g(`div`,{class:j.settingsGrid,children:[g(z,{label:n(`agentsWin.textFormat`),children:g(`select`,{class:j.control,value:v.format,onChange:e=>Z({format:e.target.value}),children:A.filter(e=>J.textFormats.includes(e)).map(e=>g(`option`,{value:e,children:n(`agentsWin.format.${e}`)},e))})}),g(z,{label:n(`agentsWin.effort`),hint:J.reasoning?void 0:n(`agentsWin.unsupported`),children:g(`select`,{class:j.control,value:v.effort,disabled:!J.reasoning,onChange:e=>Z({effort:e.target.value}),children:[g(`option`,{value:``,children:[n(`agentsWin.modelDefault`),J.defaultEffort?` (${R(J.defaultEffort)})`:``]}),J.effortLevels.map(e=>g(`option`,{value:e,children:R(e)},e))]})}),g(z,{label:n(`agentsWin.verbosity`),hint:J.verbosity?void 0:n(`agentsWin.unsupported`),children:g(`select`,{class:j.control,value:v.verbosity,disabled:!J.verbosity,onChange:e=>Z({verbosity:e.target.value}),children:[g(`option`,{value:``,children:n(`agentsWin.modelDefault`)}),E.map(e=>g(`option`,{value:e,children:R(e)},e))]})}),g(z,{label:n(`agentsWin.summary`),hint:J.summary?void 0:n(`agentsWin.unsupported`),children:g(`select`,{class:j.control,value:v.summary,disabled:!J.summary,onChange:e=>Z({summary:e.target.value}),children:[g(`option`,{value:``,children:n(`agentsWin.modelDefault`)}),O.map(e=>g(`option`,{value:e,children:n(`agentsWin.summary.${e}`)},e))]})})]})]}),g(`button`,{type:`button`,class:`${M.primary} ${j.saveButton}`,disabled:de||!!V||!k(v,D),onClick:()=>void _e(v),children:n(`agentsWin.save`)})]}),g(`div`,{class:j.column,children:[g(ce,{text:he}),fe?g(`section`,{class:j.stepper,"aria-labelledby":`getting-started`,children:[g(`h4`,{class:j.stepperHead,id:`getting-started`,children:[n(`agentsWin.gettingStarted`),g(`button`,{type:`button`,class:`icon-btn`,"aria-label":n(`agentsWin.dismiss`),title:n(`agentsWin.dismiss`),onClick:()=>{S(),pe(!1)},children:g(_,{name:`x`,size:15})})]}),g(`ol`,{class:j.steps,children:ye.map((e,t)=>{let[r,i]=be[e.id];return g(`li`,{class:j.step,"data-done":e.done,children:[g(`span`,{class:j.stepNode,"aria-hidden":`true`,children:e.done?`✓`:t+1}),g(`div`,{children:[g(`p`,{class:j.stepTitle,children:[n(r),g(`span`,{class:`sr-only`,children:e.done?` ✓`:``})]}),g(`p`,{class:j.stepText,children:n(i)})]})]},e.id)})})]}):null]})]}),V?g(le,{model:ge,started:V.started,onCancel:()=>V.controller.abort()}):null,K?g(ue,{name:K.form.name,live:K.live&&!!a,onTry:()=>{let e=K.form;q(void 0),xe(e)},onStay:()=>q(void 0)}):null]})}var V=`alisio.agents.maximized`,H=`button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex='0']`;function U(t){let{agent:r}=t;return g(e,{children:[g(I,{tone:r.scope===`project`?`ok`:`off`,children:n(`agentsWin.scope.${r.scope}`)}),r.overridesGlobal?g(I,{tone:`warn`,children:n(`agentsWin.overrides`)}):null,r.overriddenByProject?g(I,{tone:`off`,children:n(`agentsWin.overridden`)}):null]})}function W(){let t=P(),[c,f]=s({kind:`list`}),[m,h]=s(!1),[v,y]=s(()=>i(V)===`1`),b=a(null),x=F(()=>u.agentDefinitions(t?.id),[t?.id],[`agents`]),S=F(()=>u.agentTemplates(),[]),C=F(t?()=>u.agentModels(t.id):void 0,[t?.id]);r(()=>{let e=document.activeElement;return b.current?.querySelector(`button`)?.focus(),()=>e?.focus()},[]);let w=x.data?.defaultScope??(t?`project`:`global`),T=()=>C.data?.find(e=>e.active)?.reference??``,E=()=>c.kind===`editor`&&m&&!window.confirm(n(`agentsWin.unsavedLeave`))?!1:(h(!1),f({kind:`list`}),!0),O=()=>{(c.kind!==`editor`||!m||window.confirm(n(`agentsWin.unsavedLeave`)))&&(p.value=!1)},k=e=>f({kind:`editor`,key:`template:${e.id}:${Date.now()}`,form:te(e,w,T()),assist:!0}),ne=async e=>{if(window.confirm(n(`agentsWin.deleteConfirm`,{path:e.path})))try{let r=await u.deleteAgent(e.id,e.scope,t?.id);d(n(r.live?`agentsWin.deleted`:`agentsWin.deletedRestart`)),x.reload()}catch(e){d(l(e))}},A=x.data?.agents??[];return g(`div`,{class:M.backdrop,onMouseDown:e=>{e.target===e.currentTarget&&O()},children:g(`div`,{ref:b,class:`${M.dialog} ${j.shell}${v?` ${j.maximized}`:``}`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`agents-title`,onKeyDown:e=>{if(e.key===`Escape`&&!e.defaultPrevented&&(e.preventDefault(),c.kind===`editor`?E():O()),e.key===`Tab`){let t=[...b.current?.querySelectorAll(H)??[]],n=t[0],r=t.at(-1);e.shiftKey&&document.activeElement===n?(e.preventDefault(),r?.focus()):!e.shiftKey&&document.activeElement===r&&(e.preventDefault(),n?.focus())}},children:g(`div`,{class:`${M.content} ${j.scroll}`,children:[g(`div`,{class:M.topBar,children:[g(`nav`,{class:j.breadcrumb,"aria-label":n(`agentsWin.title`),children:c.kind===`editor`?g(e,{children:[g(`button`,{type:`button`,class:j.crumbLink,onClick:E,children:n(`agentsWin.back`)}),g(_,{name:`chevronRight`,size:14}),g(`span`,{class:j.crumbCurrent,id:`agents-title`,"aria-current":`page`,children:c.form.id?c.form.name:n(`agentsWin.newAgent`)})]}):g(`span`,{class:j.crumbCurrent,id:`agents-title`,children:n(`agentsWin.title`)})}),g(`button`,{type:`button`,class:`icon-btn`,"aria-label":n(v?`agentsWin.restore`:`agentsWin.maximize`),title:n(v?`agentsWin.restore`:`agentsWin.maximize`),"aria-pressed":v,onClick:()=>{let e=!v;y(e),o(V,e?`1`:void 0)},children:g(_,{name:v?`shrink`:`expand`,size:16})}),g(`button`,{type:`button`,class:`icon-btn`,"aria-label":n(`common.close`),onClick:O,children:g(_,{name:`x`,size:18})})]}),c.kind===`editor`?g(B,{initial:c.form,...c.saved?{saved:c.saved}:{},assist:c.assist,workspace:t,overview:x.data,models:C.data,modelsError:C.error,onDirty:h,onTrusted:()=>{x.reload(),C.reload()},onSaved:e=>{x.reload(),f(t=>t.kind===`editor`?{...t,form:e,saved:e}:t)}},c.key):g(e,{children:[g(`header`,{class:`${M.intro} ${j.listHead}`,children:[g(`div`,{children:[g(`h3`,{class:M.pageTitle,children:n(`agentsWin.title`)}),g(`p`,{class:M.lead,children:n(`agentsWin.lead`)}),t?null:g(`p`,{class:M.note,children:n(`agentsWin.noWorkspace`)})]}),g(`button`,{type:`button`,class:M.primary,onClick:()=>f({kind:`editor`,key:`new:${Date.now()}`,form:D(w,T()),assist:!0}),children:n(`agentsWin.create`)})]}),g(`h4`,{class:M.listTitle,children:[n(`agentsWin.yours`),g(`span`,{class:M.count,children:A.length})]}),g(N,{loading:x.loading&&!x.data,error:x.error}),x.data&&!A.length?g(`p`,{class:M.note,children:n(`agentsWin.none`)}):null,g(`ul`,{class:M.rows,children:A.map(e=>g(`li`,{class:M.row,"data-muted":e.overriddenByProject?`true`:void 0,children:[g(`button`,{type:`button`,class:j.rowButton,onClick:()=>{let t=ee(e);f({kind:`editor`,key:`${e.scope}:${e.id}`,form:t,saved:t,assist:!1})},children:[g(`span`,{class:M.rowTitle,children:[g(`span`,{class:M.cardName,children:e.name}),g(U,{agent:e})]}),g(`p`,{class:M.cardText,children:e.description}),g(`p`,{class:M.meta,children:[e.model||`—`,` · `,g(`code`,{children:e.id})]})]}),g(`div`,{class:M.rowActions,children:g(`button`,{type:`button`,class:`icon-btn`,"aria-label":`${n(`agentsWin.delete`)} ${e.name}`,title:n(`agentsWin.delete`),onClick:()=>void ne(e),children:g(_,{name:`archive`,size:16})})})]},`${e.scope}:${e.id}`))}),g(`h4`,{class:M.listTitle,children:n(`agentsWin.templates`)}),g(N,{loading:S.loading&&!S.data,error:S.error}),g(`ul`,{class:M.grid,children:(S.data??[]).map(e=>g(`li`,{children:g(`button`,{type:`button`,class:j.templateCard,onClick:()=>k(e),children:[g(`span`,{class:j.templateName,children:e.name}),g(`span`,{class:j.templateText,children:e.description})]})},e.id))})]})]})})})}export{W as AgentsModal};
|