@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.
Files changed (70) hide show
  1. package/dist/index.d.ts +25 -0
  2. package/dist/index.js +40 -2
  3. package/dist/plugins/catalog-schema.d.ts +37 -0
  4. package/dist/plugins/catalog-schema.js +141 -0
  5. package/dist/plugins/catalog-service.d.ts +89 -0
  6. package/dist/plugins/catalog-service.js +306 -0
  7. package/dist/plugins/jobs.d.ts +41 -0
  8. package/dist/plugins/jobs.js +94 -0
  9. package/dist/plugins/sdk-version.d.ts +6 -0
  10. package/dist/plugins/sdk-version.js +28 -0
  11. package/dist/plugins/staging.d.ts +13 -0
  12. package/dist/plugins/staging.js +48 -0
  13. package/dist/routes/plugin-catalog.d.ts +103 -0
  14. package/dist/routes/plugin-catalog.js +436 -0
  15. package/dist/routes/server-restart.d.ts +7 -0
  16. package/dist/routes/server-restart.js +24 -0
  17. package/dist/routes/session-views.js +8 -0
  18. package/dist/web/assets/{AgentPicker-CHCnAWFV.js → AgentPicker-B2dNJHaW.js} +1 -1
  19. package/dist/web/assets/{AgentsModal-C9JANTC0.js → AgentsModal-BWrVQpCo.js} +2 -2
  20. package/dist/web/assets/{ArtifactPanel-CMy7S56f.js → ArtifactPanel-ZkBV87x7.js} +3 -3
  21. package/dist/web/assets/{BtwPanel-DXZdSCeO.js → BtwPanel-BtIHip-u.js} +1 -1
  22. package/dist/web/assets/{ChangelogDialog-B1zYDaIS.js → ChangelogDialog-NBwCG0Bo.js} +1 -1
  23. package/dist/web/assets/ContextTab-Bi8MZwMV.js +1 -0
  24. package/dist/web/assets/ContextTab-g4wwZXSY.css +1 -0
  25. package/dist/web/assets/{CopyButton-DEf4thwH.js → CopyButton-Bh3Dsk-o.js} +1 -1
  26. package/dist/web/assets/{GoalBar-CCT6XlB-.js → GoalBar-DVwQsVtS.js} +1 -1
  27. package/dist/web/assets/{MemoryTab-lph1Kv8C.js → MemoryTab-CjZTbnFG.js} +1 -1
  28. package/dist/web/assets/{PermissionsMenu-oMuzx7AI.js → PermissionsMenu-B03X019W.js} +1 -1
  29. package/dist/web/assets/{PlanReviewPanel-BXl-gcFF.js → PlanReviewPanel-CM1uxJVs.js} +1 -1
  30. package/dist/web/assets/{PlanViewer-TxP_h0KX.js → PlanViewer-DxAqn6IP.js} +1 -1
  31. package/dist/web/assets/ProofCard-B2nxuxaT.js +1 -0
  32. package/dist/web/assets/ScratchTab-BLkjPbJC.js +1 -0
  33. package/dist/web/assets/SettingsModal-BJj7ELxX.js +3 -0
  34. package/dist/web/assets/{SpreadsheetPanel-Db_lqwkD.js → SpreadsheetPanel-BJLNla1t.js} +1 -1
  35. package/dist/web/assets/{TasksTab-BnafNW7J.js → TasksTab-DhKGW5By.js} +1 -1
  36. package/dist/web/assets/{TextPreview-DHQCGoiY.js → TextPreview-CcGkaf6g.js} +1 -1
  37. package/dist/web/assets/{TrajectoryTab-DMJw5g-T.js → TrajectoryTab-U37sw2D0.js} +1 -1
  38. package/dist/web/assets/{agents.module-BlfBTz7i.js → agents.module-C5jXUIc1.js} +1 -1
  39. package/dist/web/assets/app-CGSX2FCK.js +3 -0
  40. package/dist/web/assets/{es-ySPgx3os.js → es-DP4ejr3u.js} +2 -2
  41. package/dist/web/assets/{goal-B1jQsfH5.js → goal-Cib1wj1f.js} +1 -1
  42. package/dist/web/assets/{i18n-C54K0mc6.js → i18n-DqOZSMVh.js} +2 -2
  43. package/dist/web/assets/{icons-DTCoNVsk.js → icons-BZSZMbqh.js} +1 -1
  44. package/dist/web/assets/index-CSQo6Bhi.js +79 -0
  45. package/dist/web/assets/{jsxRuntime-DWBdSTGf.js → jsxRuntime-BWhXelbu.js} +1 -1
  46. package/dist/web/assets/{panel-LBWYDX9B.js → panel-IZ40pPYp.js} +1 -1
  47. package/dist/web/assets/settings-BAW93knU.css +1 -0
  48. package/dist/web/assets/settings.module-ErkNzIk4.js +1 -0
  49. package/dist/web/assets/{shared-DLevBtPm.js → shared-C2fqplXu.js} +1 -1
  50. package/dist/web/assets/{strings-DDeSYQDZ.js → strings-BUuRkgRy.js} +1 -1
  51. package/dist/web/assets/{tasks-Sc_vWQpd.js → tasks-Bw0p2a6l.js} +1 -1
  52. package/dist/web/assets/{view-BFzfUho9.js → view-B95Eg9o2.js} +1 -1
  53. package/dist/web/assets/view-BeVPW5cE.js +1 -0
  54. package/dist/web/assets/{view-CChCbdid.js → view-BkR3NRaK.js} +1 -1
  55. package/dist/web/assets/{view-C4NPIs0Y.js → view-C99qRBgc.js} +1 -1
  56. package/dist/web/assets/{view-DooqXXnU.js → view-CI66xheW.js} +1 -1
  57. package/dist/web/assets/{view-Cs6w0jid.js → view-CSAgn7-g.js} +1 -1
  58. package/dist/web/assets/{view-D_Sw63-N.js → view-DJAfpDXs.js} +1 -1
  59. package/dist/web/assets/{view-s7NM5Jdr.js → view-DrHx2F_3.js} +1 -1
  60. package/dist/web/assets/{view-DF9hOwPO.js → view-Du7qfIhE.js} +1 -1
  61. package/dist/web/index.html +7 -7
  62. package/package.json +4 -4
  63. package/dist/web/assets/ProofCard-DFVScMg6.js +0 -1
  64. package/dist/web/assets/ScratchTab-f751fxOV.js +0 -1
  65. package/dist/web/assets/SettingsModal-DZhl5vvS.js +0 -1
  66. package/dist/web/assets/app-BknkHksg.js +0 -3
  67. package/dist/web/assets/index-XYSVNCIf.js +0 -79
  68. package/dist/web/assets/settings-DdWABXsT.css +0 -1
  69. package/dist/web/assets/settings.module-BClxMWCR.js +0 -1
  70. 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-C54K0mc6.js";import{a as i,d as a,k as o,o as s,t as c}from"./app-BknkHksg.js";import{t as l}from"./jsxRuntime-DWBdSTGf.js";import{t as u}from"./icons-DTCoNVsk.js";import{b as d,t as f,u as p}from"./agents.module-BlfBTz7i.js";import{t as m}from"./settings.module-BClxMWCR.js";import{a as h,c as g,i as _,l as v,r as y}from"./shared-DLevBtPm.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
+ 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{T as e,_ as t,a as n,b as r,c as i,g as a,l as o,y as s}from"./i18n-C54K0mc6.js";import{Vt as c,W as l,d as u,dt as d,gt as f,mt as p,nt as m,o as h}from"./app-BknkHksg.js";import{t as g}from"./jsxRuntime-DWBdSTGf.js";import{t as _}from"./icons-DTCoNVsk.js";import{t as v}from"./CopyButton-DEf4thwH.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-BlfBTz7i.js";import{t as M}from"./settings.module-BClxMWCR.js";import{a as N,c as P,l as F,r as I}from"./shared-DLevBtPm.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`&&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};