@monster-orange/den 0.4.14 โ†’ 0.4.16

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 (57) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +7 -7
  3. package/docs/distributions.md +226 -19
  4. package/package.json +1 -1
  5. package/src/shared/actor-token.ts +12 -0
  6. package/src/shared/agents/conversations.ts +155 -8
  7. package/src/shared/agents/den-claude.ts +25 -3
  8. package/src/shared/agents/records.ts +37 -0
  9. package/src/shared/agents/runtime.ts +11 -1
  10. package/src/shared/claim-code.ts +163 -0
  11. package/src/shared/cli-command.ts +31 -0
  12. package/src/shared/den-config.ts +12 -0
  13. package/src/shared/environment-manager/workspaces.ts +20 -0
  14. package/src/shared/github-app.ts +7 -0
  15. package/src/shared/web-session.ts +64 -5
  16. package/src/ui/cli/agent-ops.ts +372 -23
  17. package/src/ui/cli/agents-command.ts +71 -4
  18. package/src/ui/cli/doctor-command.ts +29 -3
  19. package/src/ui/cli/doctor-onboard.ts +26 -0
  20. package/src/ui/cli/flag-parser.ts +9 -1
  21. package/src/ui/cli/index.ts +29 -3
  22. package/src/ui/cli/onboard-command.ts +180 -0
  23. package/src/ui/cli/onboard-reads.ts +613 -0
  24. package/src/ui/cli/repo-command.ts +156 -10
  25. package/src/ui/cli/web-command.ts +92 -1
  26. package/src/ui/mcp/api.ts +14 -2
  27. package/src/ui/mcp/server.ts +12 -2
  28. package/src/ui/mcp/tools.ts +368 -1
  29. package/src/ui/server/agent-routes.ts +91 -7
  30. package/src/ui/server/approval-routes.ts +390 -0
  31. package/src/ui/server/approvals.ts +416 -0
  32. package/src/ui/server/github-routes.ts +13 -5
  33. package/src/ui/server/held-actions.ts +203 -0
  34. package/src/ui/server/http-helpers.ts +35 -0
  35. package/src/ui/server/index.ts +308 -21
  36. package/src/ui/server/lifecycle-exec.ts +70 -0
  37. package/src/ui/server/onboard-routes.ts +132 -0
  38. package/src/ui/server/pair-routes.ts +7 -4
  39. package/src/ui/server/public/agents-page.js +378 -30
  40. package/src/ui/server/public/agents.html +1 -0
  41. package/src/ui/server/public/error-page.js +97 -18
  42. package/src/ui/server/public/error.html +18 -0
  43. package/src/ui/server/public/header.js +1 -1
  44. package/src/ui/server/public/i18n/es.js +65 -5
  45. package/src/ui/server/public/i18n/ja.js +67 -5
  46. package/src/ui/server/public/index.html +1 -1
  47. package/src/ui/server/public/local-dev.html +5 -4
  48. package/src/ui/server/public/persona-ui.css +32 -0
  49. package/src/ui/server/public/release.html +0 -1
  50. package/src/ui/server/public/settings-registry.js +3 -1
  51. package/src/ui/server/public/settings.html +9 -1
  52. package/src/ui/server/public/shell.js +348 -5
  53. package/src/ui/server/request-guard.ts +2 -0
  54. package/src/ui/server/route-classes.ts +34 -1
  55. package/src/ui/server/session-routes.ts +14 -3
  56. package/src/ui/server/workspace-routes.ts +21 -10
  57. package/themes/den/den-theme.json +6 -6
@@ -139,7 +139,7 @@ function withCmd(text, cmd) {
139
139
  function withHost(t, key, vars) {
140
140
  const marks = {};
141
141
  const v = { ...vars };
142
- for (const k of ["host", "target"])
142
+ for (const k of ["host", "target", "user"])
143
143
  if (k in v) {
144
144
  marks[k] = v[k];
145
145
  v[k] = `\u0001${k}\u0001`;
@@ -203,6 +203,20 @@ export function countdownText(ms) {
203
203
  * Where a signed-in page goes: the page that was asked for. On /signin,
204
204
  * its same-origin `next` (else `/`); anywhere else, the same URL.
205
205
  */
206
+ /**
207
+ * 0.4.15: keep a sign-in's approval key (web-session.ts) in this origin's
208
+ * localStorage, where shell.js sends it from. It comes in the sign-in's
209
+ * own answer only.
210
+ */
211
+ function keepApproveKey(b) {
212
+ if (!b || typeof b.approveKey !== "string") return;
213
+ try {
214
+ localStorage.setItem("den.approveKey", b.approveKey);
215
+ } catch {
216
+ /* storage blocked: this browser signs in again to approve */
217
+ }
218
+ }
219
+
206
220
  export function landingAfterSignIn(loc) {
207
221
  if (loc.pathname !== "/signin") return loc.pathname + loc.search + loc.hash;
208
222
  const n = new URLSearchParams(loc.search).get("next") || "/";
@@ -212,30 +226,84 @@ export function landingAfterSignIn(loc) {
212
226
  }
213
227
 
214
228
  /**
215
- * Sign in with GitHub (SEC-2a): the main button, shown only when a client
216
- * id is configured. The server runs GitHub's device flow; the page shows
217
- * the code and github.com/login/device, polls the server, and goes on once
218
- * the owner approved it (the server sets the cookie). Another account is
219
- * refused by name. With no owner linked, it says what to run on the server.
229
+ * Sign in with GitHub (SEC-2a): shown only when a client id is configured.
230
+ * The server runs GitHub's device flow; the page shows the code and
231
+ * github.com/login/device, polls the server, and goes on once the owner
232
+ * approved it (the server sets the cookie). Another account is refused by
233
+ * name.
234
+ *
235
+ * SEC-2a-UX: the same three steps in every state (link the owner on the
236
+ * machine, enter the code on GitHub, come back and press the button).
237
+ * Once an owner is linked, step 1 is done and names it, and the button
238
+ * follows the steps. Below them: only the owner's account signs in this
239
+ * way, and Learn more.
220
240
  */
221
241
  function githubBlock(s, t) {
222
242
  const g = s.github;
223
243
  if (!g || s.localhost || g.state === "no_client_id") return "";
224
244
  const host = s.machine || "";
225
- if (g.state === "no_owner")
226
- return (
227
- `<div id="den-gh"><p>${withHost(t, "err.signin.githubNoOwner", { name: s.name, host })}</p>` +
228
- cmdLine("den-gh-owner-cmd", g.owner, t) +
229
- `</div>`
230
- );
245
+ const ready = g.state === "ready";
246
+ const step1 = withHost(t, "err.signin.ghStep1", {
247
+ host,
248
+ cmd: "\u0000",
249
+ }).replace("\u0000", `<code class="mono">${esc(g.owner)}</code>`);
231
250
  return (
232
- `<div id="den-gh"><div class="acts" style="margin-top:0">` +
233
- `<button type="button" class="btn primary" id="den-gh-start">${esc(t("err.signin.github"))}</button>` +
234
- `</div><p class="den-gh-note" style="margin-top:8px">${esc(t("err.signin.githubNote", { name: s.name }))}</p>` +
235
- `<div id="den-gh-flow" aria-live="polite"></div></div>`
251
+ `<div id="den-gh">` +
252
+ `<ol class="den-steps" id="den-gh-steps">` +
253
+ `<li id="den-gh-step1"${ready ? ' class="done"' : ""}><p>${step1}</p>` +
254
+ (ready && g.ownerLogin
255
+ ? `<p class="den-gh-owner" id="den-gh-owner">${esc(t("err.signin.ghOwner", { login: g.ownerLogin }))}</p>`
256
+ : "") +
257
+ `</li>` +
258
+ `<li id="den-gh-step2"><p>${esc(t("err.signin.ghStep2"))}</p></li>` +
259
+ `<li id="den-gh-step3"><p>${esc(t("err.signin.ghStep3"))}</p></li>` +
260
+ `</ol>` +
261
+ (ready
262
+ ? `<div class="acts" style="margin-top:0">` +
263
+ `<button type="button" class="btn primary" id="den-gh-start">${esc(t("err.signin.github"))}</button>` +
264
+ `</div><div id="den-gh-flow" aria-live="polite"></div>`
265
+ : "") +
266
+ `<p class="den-gh-note" id="den-gh-only">${esc(t("err.signin.ghOnly"))}` +
267
+ (g.learnMore
268
+ ? ` <a id="den-gh-learn" href="${esc(g.learnMore)}" target="_blank" rel="noopener noreferrer">${esc(t("err.signin.ghLearn"))}</a>`
269
+ : "") +
270
+ `</p></div>`
236
271
  );
237
272
  }
238
273
 
274
+ /** How often, and how long, the page watches for an owner (SEC-2a-UX). */
275
+ export const OWNER_POLL_MS = 5000;
276
+ const OWNER_WATCH_MS = 30 * 60 * 1000;
277
+
278
+ /**
279
+ * SEC-2a-UX: with no owner linked yet, watch the session probe; once the
280
+ * owner is linked on the machine, step 1 shows done and Sign in with
281
+ * GitHub appears, with no reload.
282
+ */
283
+ function watchOwner(root, t, s, pollMs = OWNER_POLL_MS) {
284
+ if (s.github?.state !== "no_owner" || !root.querySelector("#den-gh")) return;
285
+ const until = Date.now() + OWNER_WATCH_MS;
286
+ const tick = () => {
287
+ if (Date.now() > until || !root.querySelector("#den-gh")) return;
288
+ fetch("/api/session", { cache: "no-store", credentials: "same-origin" })
289
+ .then((r) => r.json().catch(() => ({})))
290
+ .then((b) => {
291
+ const g = b?.github;
292
+ if (g?.state === "ready" && g.ownerLogin) {
293
+ s.github = { ...s.github, state: "ready", ownerLogin: g.ownerLogin };
294
+ const old = root.querySelector("#den-gh");
295
+ if (!old) return;
296
+ old.outerHTML = githubBlock(s, t);
297
+ wireGithub(root, t, s);
298
+ return;
299
+ }
300
+ setTimeout(tick, pollMs);
301
+ })
302
+ .catch(() => setTimeout(tick, pollMs));
303
+ };
304
+ setTimeout(tick, pollMs);
305
+ }
306
+
239
307
  /** The device flow on the page: start, show the code, poll, go on. */
240
308
  function wireGithub(root, t, s) {
241
309
  const start = root.querySelector("#den-gh-start");
@@ -263,6 +331,7 @@ function wireGithub(root, t, s) {
263
331
  return;
264
332
  }
265
333
  if (b.state === "ok") {
334
+ keepApproveKey(b);
266
335
  show(
267
336
  `<p class="lead" style="margin-top:12px">${esc(t("err.signin.githubDone"))}</p>`,
268
337
  );
@@ -351,6 +420,9 @@ function wirePair(root, t, s) {
351
420
  if (!box) return;
352
421
  const host = s.machine || "";
353
422
  const target = s.ssh?.target || "";
423
+ // SEC-2a-UX: the account to log in as, so the SSH app doesn't guess one.
424
+ const user = s.ssh?.user || "";
425
+ const login = user ? `${user}@${target}` : target;
354
426
  let poll = null;
355
427
  let tick = null;
356
428
  let gen = 0;
@@ -376,7 +448,7 @@ function wirePair(root, t, s) {
376
448
  const show = (b) => {
377
449
  const term =
378
450
  s.ssh?.name && target
379
- ? `<a class="btn" id="den-pair-term" href="ssh://${esc(target)}">${esc(t("err.signin.openTerminal"))}</a>`
451
+ ? `<a class="btn" id="den-pair-term" href="ssh://${esc(login)}">${esc(t("err.signin.openTerminal"))}</a>`
380
452
  : "";
381
453
  box.innerHTML =
382
454
  `<ol class="den-steps">` +
@@ -389,7 +461,11 @@ function wirePair(root, t, s) {
389
461
  `<p>${withHost(t, "err.signin.noTerminal", { host })}</p>` +
390
462
  cmdLine("den-pair-ssh", b.ssh, t, term) +
391
463
  (term
392
- ? `<p class="den-pair-note">${withHost(t, "err.signin.sshNote", { target })}</p>`
464
+ ? `<p class="den-pair-note" id="den-pair-note">${
465
+ user
466
+ ? withHost(t, "err.signin.sshUserNote", { host, user })
467
+ : withHost(t, "err.signin.sshNote", { target })
468
+ }</p>`
393
469
  : "");
394
470
  wireCopy(box, t);
395
471
  };
@@ -432,6 +508,7 @@ function wirePair(root, t, s) {
432
508
  return;
433
509
  }
434
510
  if (p.state === "ok") {
511
+ keepApproveKey(p);
435
512
  stop();
436
513
  box.innerHTML = `<p class="lead" id="den-pair-in">${esc(t("err.signin.youreIn"))}</p>`;
437
514
  location.replace(landingAfterSignIn(location));
@@ -524,6 +601,7 @@ function mountSignedOut(root, sh, d) {
524
601
  .then((b) => {
525
602
  go.disabled = false;
526
603
  if (b.ok) {
604
+ keepApproveKey(b);
527
605
  err.textContent = "";
528
606
  form.innerHTML = `<p class="lead" id="den-confirm-in">${esc(t("err.signin.youreIn"))}</p>`;
529
607
  location.replace(typeof b.next === "string" ? b.next : "/");
@@ -580,6 +658,7 @@ function mountSignedOut(root, sh, d) {
580
658
  `</div>`;
581
659
  wireCopy(root, t);
582
660
  wireGithub(root, t, s);
661
+ watchOwner(root, t, s, window.__denOwnerPollMs || OWNER_POLL_MS);
583
662
  wirePair(root, t, s);
584
663
  if (s.expired) return;
585
664
  let tried = false;
@@ -265,6 +265,24 @@
265
265
  color: var(--text);
266
266
  font-weight: 600;
267
267
  }
268
+ /* SEC-2a-UX: a step already done (the owner is linked). */
269
+ ol.den-steps li.done::marker {
270
+ content: "\2713 ";
271
+ color: var(--ok, var(--text));
272
+ }
273
+ ol.den-steps li.done > p:first-child {
274
+ color: var(--muted);
275
+ }
276
+ ol.den-steps code {
277
+ overflow-wrap: anywhere;
278
+ }
279
+ p.den-gh-note {
280
+ margin-top: 8px;
281
+ }
282
+ p.den-gh-owner {
283
+ color: var(--text);
284
+ font-weight: 600;
285
+ }
268
286
  details.den-other {
269
287
  margin: 0 0 16px;
270
288
  }
@@ -251,7 +251,7 @@ function _injectChangelogStyles() {
251
251
  padding: 5px 16px; border-radius: 5px; font-size: 12px;
252
252
  font-weight: 600; cursor: pointer;
253
253
  }
254
- .cl-got-it:hover { opacity: .85; }
254
+ .cl-got-it:hover { filter: brightness(0.92); }
255
255
  `;
256
256
  document.head.appendChild(style);
257
257
 
@@ -254,12 +254,18 @@
254
254
  "err.signin.title": "Inicia sesiรณn en {name}",
255
255
  "err.signin.where":
256
256
  "Estรกs abriendo {name} desde otro ordenador. {name} corre en {host}, asรญ que necesita saber que eres tรบ.",
257
- "err.signin.githubNote":
258
- "Usa tu cuenta de GitHub. El propietario de este {name} tiene que haber vinculado la suya.",
259
- "err.signin.githubNoOwner":
260
- "Iniciar sesiรณn con GitHub necesita un propietario, y este {name} aรบn no tiene. En {host} (no en este ordenador), ejecuta:",
257
+ "err.signin.ghStep1":
258
+ "En {host}, ejecuta {cmd}, o usa Ajustes โ€บ Acceso allรญ.",
259
+ "err.signin.ghStep2":
260
+ "Muestra un cรณdigo. Introdรบcelo en github.com/login/device y apruรฉbalo.",
261
+ "err.signin.ghStep3":
262
+ "Despuรฉs vuelve aquรญ y pulsa Iniciar sesiรณn con GitHub, con la misma cuenta de GitHub.",
263
+ "err.signin.ghOwner": "Propietario: @{login}",
264
+ "err.signin.ghOnly":
265
+ "Solo la cuenta de GitHub del propietario puede iniciar sesiรณn asรญ. Para cualquier otra persona, usa ยซAprueba este ordenadorยป.",
266
+ "err.signin.ghLearn": "Mรกs informaciรณn",
261
267
  "err.signin.githubRefused":
262
- "@{login} no es el propietario de este {name}. Pide al propietario que te aรฑada, o usa ยซAprobar este ordenadorยป.",
268
+ "@{login} no es el propietario de este {name}. Solo la cuenta del propietario puede usar Iniciar sesiรณn con GitHub. Usa ยซAprueba este ordenadorยป en su lugar.",
263
269
  "err.signin.approveOr": "O aprueba este ordenador",
264
270
  "err.signin.approve": "Aprueba este ordenador",
265
271
  "err.signin.step1": "En {host} (no en este ordenador), ejecuta:",
@@ -271,6 +277,8 @@
271
277
  "err.signin.noTerminal":
272
278
  "ยฟNo tienes una terminal en {host}? Desde este ordenador, ejecuta:",
273
279
  "err.signin.openTerminal": "Abrir Terminal",
280
+ "err.signin.sshUserNote":
281
+ "Inicia sesiรณn con tu cuenta de {host} ({user}) y su contraseรฑa, o con una clave SSH.",
274
282
  "err.signin.sshNote":
275
283
  "Abrir Terminal abre ssh://{target} en tu aplicaciรณn de terminal. Necesita acceso SSH a {target}.",
276
284
  "err.signin.whyTitle": "ยฟPor quรฉ?",
@@ -459,6 +467,11 @@
459
467
  "ag.new.name": "Nombre (opcional)",
460
468
  "ag.new.as": "Se inicia como {id}",
461
469
  "ag.new.openRunning": "Abrir el que estรก en ejecuciรณn",
470
+ "ag.new.isRunning": "{id} ya estรก en ejecuciรณn",
471
+ "ag.new.openIt": "Abrirlo",
472
+ "ag.new.isStopped": "{id} estรก detenido",
473
+ "ag.new.resumeIt": "Reanudarlo",
474
+ "ag.new.orNext": "o Iniciar crea {id}",
462
475
  "ag.new.taskPh":
463
476
  "p. ej., Toma la historia del estado vacรญo. Sigue el diseรฑo y aรฑade una prueba.",
464
477
  "ag.new.adv": "Avanzado: modelo y permisos",
@@ -482,12 +495,58 @@
482
495
  "ag.bring.agent": "Agente {id}",
483
496
  "ag.bring.note":
484
497
  "La continรบa en la carpeta donde se ejecutรณ, con las herramientas y protecciones de Den desde ahora. Sigue en ejecuciรณn si cierras el panel. Ciรฉrrala antes en cualquier otro terminal: Den solo la ve abierta si se escribiรณ en el รบltimo minuto.",
498
+ "ag.keep.model": "Mantiene {m} de la conversaciรณn.",
499
+ "ag.keep.modelNew": "Usa {m} a partir de ahora.",
500
+ "ag.keep.from":
501
+ "A partir de ahora trabaja como {p}: su rol, sus permisos ({mode}), las herramientas y protecciones de Den. Su historial se mantiene.",
502
+ "ag.keep.lost":
503
+ "No tendrรก: {s}. Den ejecuta los agentes solo con las herramientas de Den.",
504
+ "ag.keep.opt": "Mantener {m}",
505
+ "ag.keep.optNone": "Mantener el modelo que usa",
506
+ "ag.keep.advNote":
507
+ "El modelo que usa se mantiene salvo que elijas otro aquรญ. Los permisos vienen de la persona.",
508
+ "ag.pc.item": "Cambiar personaโ€ฆ",
509
+ "ag.pc.title": "Cambiar la persona de {id}",
510
+ "ag.pc.note":
511
+ "Den lo detiene y reanuda la misma conversaciรณn con la nueva persona. Su id, nombre, carpeta y pestaรฑa se mantienen.",
512
+ "ag.pc.go": "Cambiar persona",
513
+ "ag.pc.going": "Cambiandoโ€ฆ",
514
+ "ag.pc.current": "actual",
515
+ "ag.pc.busy":
516
+ "Estรก trabajando. ยฟCambiar despuรฉs de este turno o detenerlo ahora?",
517
+ "ag.pc.after": "Despuรฉs de este turno",
518
+ "ag.pc.now": "Detenerlo ahora",
519
+ "ag.pc.done": "{id} ahora es {p}",
520
+ "ag.pc.pending": "{id} pasarรก a ser {p} cuando termine este turno",
521
+ "ag.more": "Mรกs para {id}",
485
522
  "ag.bring.go": "Traer",
486
523
  "ag.bring.going": "Trayendoโ€ฆ",
487
524
  "ag.bring.done": "Traรญda como {id}",
488
525
  "ag.bring.now": "ahora mismo",
489
526
  "ag.bring.min": "hace {n} min",
490
527
  "ag.bring.h": "hace {n} h",
528
+ "ap.title": "{agent} quiere:",
529
+ "ap.agent": "El agente {name}",
530
+ "ap.noAgent": "Algo en esta mรกquina (sin identidad de agente)",
531
+ "ap.noAgentWarn": "Si no le pediste esto a un agente, deniรฉgalo.",
532
+ "ap.signin.title": "Algo espera tu aprobaciรณn.",
533
+ "ap.signin": "Inicia sesiรณn para verlo: ejecuta {cmd} y abre el enlace.",
534
+ "ap.do.workspace_create": 'Crear el espacio "{name}"',
535
+ "ap.do.repo_add":
536
+ 'Aรฑadir el repositorio de {path} al espacio "{workspace}"',
537
+ "ap.do.repo_add.new":
538
+ 'Aรฑadir el repositorio de {path} a un espacio nuevo "{workspace}"',
539
+ "ap.do.service_try": "Iniciar {service}",
540
+ "ap.says": "El agente dice:",
541
+ "ap.review": "Revisar",
542
+ "ap.hide": "Ocultar detalles",
543
+ "ap.later": "Mรกs tarde",
544
+ "ap.deny": "Denegar",
545
+ "ap.allow": "Permitir",
546
+ "ap.waits": "Nada cambia hasta que lo permitas. Espera hasta las {time}.",
547
+ "ap.more": "{n} mรกs en espera",
548
+ "ap.failed": "Se ejecutรณ y fallรณ: {error}",
549
+ "ap.done": "Hecho: {what}",
491
550
  "ag.new.note":
492
551
  "Se inicia en la carpeta del espacio de trabajo. Sigue en ejecuciรณn si cierras el panel.",
493
552
  "ag.new.started": "{p} iniciado en {w}",
@@ -608,6 +667,7 @@
608
667
  "Su token fue revocado (web logout --all): detenlo y reanรบdalo, o inรญcialo de nuevo, para darle uno nuevo",
609
668
  "ag.ended":
610
669
  "Esta sesiรณn ha terminado. Su registro se queda hasta que se reanude o se elimine.",
670
+ "ag.switched": "Cambiado a {p}",
611
671
  "ag.exitedLine": "[la sesiรณn terminรณ{code}]",
612
672
  "ag.lost": "[desconectado: {why}]",
613
673
  "ag.reconnect": "Reconectar",
@@ -242,12 +242,18 @@
242
242
  "err.signin.title": "{name} ใซใ‚ตใ‚คใƒณใ‚คใƒณ",
243
243
  "err.signin.where":
244
244
  "ๅˆฅใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใ‹ใ‚‰ {name} ใ‚’้–‹ใ„ใฆใ„ใพใ™ใ€‚{name} ใฏ {host} ใงๅ‹•ใ„ใฆใ„ใ‚‹ใŸใ‚ใ€ใ‚ใชใŸๆœฌไบบใงใ‚ใ‚‹ใ“ใจใ‚’็ขบ่ชใ™ใ‚‹ๅฟ…่ฆใŒใ‚ใ‚Šใพใ™ใ€‚",
245
- "err.signin.githubNote":
246
- "ใ‚ใชใŸใฎ GitHub ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‚’ไฝฟใ„ใพใ™ใ€‚ใ“ใฎ {name} ใฎใ‚ชใƒผใƒŠใƒผใŒ่‡ชๅˆ†ใฎใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‚’ใƒชใƒณใ‚ฏใ—ใฆใ„ใ‚‹ๅฟ…่ฆใŒใ‚ใ‚Šใพใ™ใ€‚",
247
- "err.signin.githubNoOwner":
248
- "GitHub ใงใฎใ‚ตใ‚คใƒณใ‚คใƒณใซใฏใ‚ชใƒผใƒŠใƒผใŒๅฟ…่ฆใงใ™ใŒใ€ใ“ใฎ {name} ใซใฏใพใ ใ„ใพใ›ใ‚“ใ€‚{host} ใง(ใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใงใฏใ‚ใ‚Šใพใ›ใ‚“)ๆฌกใ‚’ๅฎŸ่กŒใ—ใฆใใ ใ•ใ„:",
245
+ "err.signin.ghStep1":
246
+ "{host} ใง {cmd} ใ‚’ๅฎŸ่กŒใ™ใ‚‹ใ‹ใ€ใใกใ‚‰ใง ่จญๅฎš โ€บ ใ‚ขใ‚ฏใ‚ปใ‚น ใ‚’ไฝฟใ„ใพใ™ใ€‚",
247
+ "err.signin.ghStep2":
248
+ "ใ‚ณใƒผใƒ‰ใŒ่กจ็คบใ•ใ‚Œใพใ™ใ€‚github.com/login/device ใงๅ…ฅๅŠ›ใ—ใฆๆ‰ฟ่ชใ—ใพใ™ใ€‚",
249
+ "err.signin.ghStep3":
250
+ "ใ“ใ“ใซๆˆปใ‚Šใ€ๅŒใ˜ GitHub ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใงใ€ŒGitHub ใงใ‚ตใ‚คใƒณใ‚คใƒณใ€ใ‚’ๆŠผใ—ใพใ™ใ€‚",
251
+ "err.signin.ghOwner": "ใ‚ชใƒผใƒŠใƒผ: @{login}",
252
+ "err.signin.ghOnly":
253
+ "ใ“ใฎๆ–นๆณ•ใงใ‚ตใ‚คใƒณใ‚คใƒณใงใใ‚‹ใฎใฏใ‚ชใƒผใƒŠใƒผใฎ GitHub ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ ใ‘ใงใ™ใ€‚ใใ‚Œไปฅๅค–ใฎไบบใฏใ€Œใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใ‚’ๆ‰ฟ่ชใ€ใ‚’ไฝฟใฃใฆใใ ใ•ใ„ใ€‚",
254
+ "err.signin.ghLearn": "่ฉณใ—ใ่ฆ‹ใ‚‹",
249
255
  "err.signin.githubRefused":
250
- "@{login} ใฏใ“ใฎ {name} ใฎใ‚ชใƒผใƒŠใƒผใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚ใ‚ชใƒผใƒŠใƒผใซ่ฟฝๅŠ ใ‚’ไพ้ ผใ™ใ‚‹ใ‹ใ€ใ€Œใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใ‚’ๆ‰ฟ่ชใ€ใ‚’ไฝฟใฃใฆใใ ใ•ใ„ใ€‚",
256
+ "@{login} ใฏใ“ใฎ {name} ใฎใ‚ชใƒผใƒŠใƒผใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚GitHub ใงใ‚ตใ‚คใƒณใ‚คใƒณใ‚’ไฝฟใˆใ‚‹ใฎใฏใ‚ชใƒผใƒŠใƒผใฎใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ ใ‘ใงใ™ใ€‚ไปฃใ‚ใ‚Šใซใ€Œใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใ‚’ๆ‰ฟ่ชใ€ใ‚’ไฝฟใฃใฆใใ ใ•ใ„ใ€‚",
251
257
  "err.signin.approveOr": "ใพใŸใฏใ€ใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใ‚’ๆ‰ฟ่ช",
252
258
  "err.signin.approve": "ใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใ‚’ๆ‰ฟ่ช",
253
259
  "err.signin.step1":
@@ -260,6 +266,8 @@
260
266
  "err.signin.noTerminal":
261
267
  "{host} ใฎใ‚ฟใƒผใƒŸใƒŠใƒซใŒใ‚ใ‚Šใพใ›ใ‚“ใ‹?ใ“ใฎใ‚ณใƒณใƒ”ใƒฅใƒผใ‚ฟใƒผใงๆฌกใ‚’ๅฎŸ่กŒใ—ใพใ™:",
262
268
  "err.signin.openTerminal": "ใ‚ฟใƒผใƒŸใƒŠใƒซใ‚’้–‹ใ",
269
+ "err.signin.sshUserNote":
270
+ "{host} ใฎใ‚ขใ‚ซใ‚ฆใƒณใƒˆ({user})ใจใใฎใƒ‘ใ‚นใƒฏใƒผใƒ‰ใ€ใพใŸใฏ SSH ใ‚ญใƒผใงใƒญใ‚ฐใ‚คใƒณใ—ใพใ™ใ€‚",
263
271
  "err.signin.sshNote":
264
272
  "ใ€Œใ‚ฟใƒผใƒŸใƒŠใƒซใ‚’้–‹ใใ€ใฏใ‚ฟใƒผใƒŸใƒŠใƒซใ‚ขใƒ—ใƒชใง ssh://{target} ใ‚’้–‹ใใพใ™ใ€‚{target} ใธใฎ SSH ใ‚ขใ‚ฏใ‚ปใ‚นใŒๅฟ…่ฆใงใ™ใ€‚",
265
273
  "err.signin.whyTitle": "ใชใœ?",
@@ -442,6 +450,11 @@
442
450
  "ag.new.name": "ๅๅ‰๏ผˆไปปๆ„๏ผ‰",
443
451
  "ag.new.as": "{id} ใจใ—ใฆ้–‹ๅง‹ใ—ใพใ™",
444
452
  "ag.new.openRunning": "ๅฎŸ่กŒไธญใฎใ‚‚ใฎใ‚’้–‹ใ",
453
+ "ag.new.isRunning": "{id} ใฏใ™ใงใซๅฎŸ่กŒไธญใงใ™",
454
+ "ag.new.openIt": "้–‹ใ",
455
+ "ag.new.isStopped": "{id} ใฏๅœๆญขใ—ใฆใ„ใพใ™",
456
+ "ag.new.resumeIt": "ๅ†้–‹ใ™ใ‚‹",
457
+ "ag.new.orNext": "ใพใŸใฏ้–‹ๅง‹ใง {id} ใ‚’ไฝœๆˆใ—ใพใ™",
445
458
  "ag.new.taskPh":
446
459
  "ไพ‹: ็ฉบใฎ็Šถๆ…‹ใฎใ‚นใƒˆใƒผใƒชใƒผใ‚’ๆ‹…ๅฝ“ใ€‚ใƒ‡ใ‚ถใ‚คใƒณใซๅˆใ‚ใ›ใ€ใƒ†ใ‚นใƒˆใ‚’่ฟฝๅŠ ใ™ใ‚‹ใ€‚",
447
460
  "ag.new.adv": "่ฉณ็ดฐ่จญๅฎš: ใƒขใƒ‡ใƒซใจๆจฉ้™",
@@ -466,12 +479,60 @@
466
479
  "ag.bring.agent": "ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆ {id}",
467
480
  "ag.bring.note":
468
481
  "ๅฎŸ่กŒใ—ใฆใ„ใŸใƒ•ใ‚ฉใƒซใƒ€ใƒผใง็ถšใ‘ใพใ™ใ€‚ใ“ใ“ใ‹ใ‚‰ใฏ Den ใฎใƒ„ใƒผใƒซใจใ‚ฌใƒผใƒ‰ใŒ้ฉ็”จใ•ใ‚Œใพใ™ใ€‚ใƒšใ‚คใƒณใ‚’้–‰ใ˜ใฆใ‚‚ๅฎŸ่กŒใฏ็ถšใใพใ™ใ€‚ๅ…ˆใซใปใ‹ใฎใ‚ฟใƒผใƒŸใƒŠใƒซใง้–‰ใ˜ใฆใใ ใ•ใ„ใ€‚Den ใŒ้–‹ใ„ใฆใ„ใ‚‹ใจๅˆคๆ–ญใงใใ‚‹ใฎใฏใ€็›ด่ฟ‘ 1 ๅˆ†ไปฅๅ†…ใซๆ›ธใ่พผใพใ‚ŒใŸไผš่ฉฑใ ใ‘ใงใ™ใ€‚",
482
+ "ag.keep.model": "ไผš่ฉฑใฎ {m} ใ‚’ใใฎใพใพไฝฟใ„ใพใ™ใ€‚",
483
+ "ag.keep.modelNew": "ใ“ใ‚Œใ‹ใ‚‰ใฏ {m} ใงๅ‹•ใใพใ™ใ€‚",
484
+ "ag.keep.from":
485
+ "ใ“ใ‚Œใ‹ใ‚‰ใฏ {p} ใจใ—ใฆๅƒใใพใ™: ใใฎๅฝนๅ‰ฒใ€ๆจฉ้™({mode})ใ€Den ใฎใƒ„ใƒผใƒซใจใ‚ฌใƒผใƒ‰ใ€‚ๅฑฅๆญดใฏใใฎใพใพๆฎ‹ใ‚Šใพใ™ใ€‚",
486
+ "ag.keep.lost":
487
+ "ไฝฟใˆใชใใชใ‚‹ใ‚‚ใฎ: {s}ใ€‚Den ใฏใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใ‚’ Den ใฎใƒ„ใƒผใƒซใ ใ‘ใงๅ‹•ใ‹ใ—ใพใ™ใ€‚",
488
+ "ag.keep.opt": "{m} ใฎใพใพ",
489
+ "ag.keep.optNone": "ไปŠใฎใƒขใƒ‡ใƒซใฎใพใพ",
490
+ "ag.keep.advNote":
491
+ "ใ“ใ“ใงๅˆฅใฎใƒขใƒ‡ใƒซใ‚’้ธใฐใชใ„้™ใ‚Šใ€ไปŠใฎใƒขใƒ‡ใƒซใฎใพใพใงใ™ใ€‚ๆจฉ้™ใฏใƒšใƒซใ‚ฝใƒŠใ‹ใ‚‰ๆฅใพใ™ใ€‚",
492
+ "ag.pc.item": "ใƒšใƒซใ‚ฝใƒŠใ‚’ๅค‰ๆ›ดโ€ฆ",
493
+ "ag.pc.title": "{id} ใฎใƒšใƒซใ‚ฝใƒŠใ‚’ๅค‰ๆ›ด",
494
+ "ag.pc.note":
495
+ "Den ใฏใ„ใฃใŸใ‚“ๅœๆญขใ—ใ€ๅŒใ˜ไผš่ฉฑใ‚’ๆ–ฐใ—ใ„ใƒšใƒซใ‚ฝใƒŠใงๅ†้–‹ใ—ใพใ™ใ€‚IDใ€ๅๅ‰ใ€ใƒ•ใ‚ฉใƒซใƒ€ใƒผใ€ใ‚ฟใƒ–ใฏใใฎใพใพใงใ™ใ€‚",
496
+ "ag.pc.go": "ใƒšใƒซใ‚ฝใƒŠใ‚’ๅค‰ๆ›ด",
497
+ "ag.pc.going": "ๅค‰ๆ›ดไธญโ€ฆ",
498
+ "ag.pc.current": "็พๅœจ",
499
+ "ag.pc.busy":
500
+ "ไฝœๆฅญไธญใงใ™ใ€‚ใ“ใฎใ‚ฟใƒผใƒณใฎๅพŒใซๅค‰ๆ›ดใ—ใพใ™ใ‹ใ€ใใ‚Œใจใ‚‚ไปŠใ™ใๅœๆญขใ—ใพใ™ใ‹?",
501
+ "ag.pc.after": "ใ“ใฎใ‚ฟใƒผใƒณใฎๅพŒ",
502
+ "ag.pc.now": "ไปŠใ™ใๅœๆญข",
503
+ "ag.pc.done": "{id} ใฏ {p} ใซใชใ‚Šใพใ—ใŸ",
504
+ "ag.pc.pending": "ใ“ใฎใ‚ฟใƒผใƒณใŒ็ต‚ใ‚ใ‚‹ใจ {id} ใฏ {p} ใซใชใ‚Šใพใ™",
505
+ "ag.more": "{id} ใฎใใฎไป–ใฎๆ“ไฝœ",
469
506
  "ag.bring.go": "ๅ–ใ‚Š่พผใ‚€",
470
507
  "ag.bring.going": "ๅ–ใ‚Š่พผใฟไธญโ€ฆ",
471
508
  "ag.bring.done": "{id} ใจใ—ใฆๅ–ใ‚Š่พผใฟใพใ—ใŸ",
472
509
  "ag.bring.now": "ใŸใฃใŸไปŠ",
473
510
  "ag.bring.min": "{n} ๅˆ†ๅ‰",
474
511
  "ag.bring.h": "{n} ๆ™‚้–“ๅ‰",
512
+ "ap.title": "{agent}ใŒๆฌกใฎใ“ใจใ‚’ๆฑ‚ใ‚ใฆใ„ใพใ™:",
513
+ "ap.agent": "ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆ {name} ",
514
+ "ap.noAgent": "ใ“ใฎใƒžใ‚ทใƒณไธŠใฎไฝ•ใ‹๏ผˆใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใฎ่ญ˜ๅˆฅใชใ—๏ผ‰",
515
+ "ap.noAgentWarn":
516
+ "ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใซใ“ใ‚Œใ‚’้ ผใ‚“ใงใ„ใชใ‘ใ‚Œใฐใ€ๆ‹’ๅฆใ—ใฆใใ ใ•ใ„ใ€‚",
517
+ "ap.signin.title": "ๆ‰ฟ่ชใ‚’ๅพ…ใฃใฆใ„ใ‚‹ใ‚‚ใฎใŒใ‚ใ‚Šใพใ™ใ€‚",
518
+ "ap.signin":
519
+ "ใ‚ตใ‚คใƒณใ‚คใƒณใ™ใ‚‹ใจ่กจ็คบใ•ใ‚Œใพใ™: {cmd} ใ‚’ๅฎŸ่กŒใ—ใฆใƒชใƒณใ‚ฏใ‚’้–‹ใ„ใฆใใ ใ•ใ„ใ€‚",
520
+ "ap.do.workspace_create": "ใƒฏใƒผใ‚ฏใ‚นใƒšใƒผใ‚นใ€Œ{name}ใ€ใ‚’ไฝœๆˆ",
521
+ "ap.do.repo_add":
522
+ "{path} ใฎใƒชใƒใ‚ธใƒˆใƒชใ‚’ใƒฏใƒผใ‚ฏใ‚นใƒšใƒผใ‚นใ€Œ{workspace}ใ€ใซ่ฟฝๅŠ ",
523
+ "ap.do.repo_add.new":
524
+ "{path} ใฎใƒชใƒใ‚ธใƒˆใƒชใ‚’ๆ–ฐใ—ใ„ใƒฏใƒผใ‚ฏใ‚นใƒšใƒผใ‚นใ€Œ{workspace}ใ€ใซ่ฟฝๅŠ ",
525
+ "ap.do.service_try": "{service} ใ‚’่ตทๅ‹•",
526
+ "ap.says": "ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใฎใ‚ณใƒกใƒณใƒˆ:",
527
+ "ap.review": "็ขบ่ช",
528
+ "ap.hide": "่ฉณ็ดฐใ‚’้š ใ™",
529
+ "ap.later": "ใ‚ใจใง",
530
+ "ap.deny": "ๆ‹’ๅฆ",
531
+ "ap.allow": "่จฑๅฏ",
532
+ "ap.waits": "่จฑๅฏใ™ใ‚‹ใพใงไฝ•ใ‚‚ๅค‰ใ‚ใ‚Šใพใ›ใ‚“ใ€‚{time} ใพใงๅพ…ใกใพใ™ใ€‚",
533
+ "ap.more": "ใปใ‹ใซ {n} ไปถใŒๅพ…ๆฉŸไธญ",
534
+ "ap.failed": "ๅฎŸ่กŒใ—ใพใ—ใŸใŒๅคฑๆ•—ใ—ใพใ—ใŸ: {error}",
535
+ "ap.done": "ๅฎŒไบ†: {what}",
475
536
  "ag.new.note":
476
537
  "ใƒฏใƒผใ‚ฏใ‚นใƒšใƒผใ‚นใฎใƒ•ใ‚ฉใƒซใƒ€ใƒผใง้–‹ๅง‹ใ—ใพใ™ใ€‚ใƒšใ‚คใƒณใ‚’้–‰ใ˜ใฆใ‚‚ๅฎŸ่กŒใฏ็ถšใใพใ™ใ€‚",
477
538
  "ag.new.started": "{w} ใง {p} ใ‚’้–‹ๅง‹ใ—ใพใ—ใŸ",
@@ -590,6 +651,7 @@
590
651
  "ag.revokedTip":
591
652
  "ใƒˆใƒผใ‚ฏใƒณใŒๅคฑๅŠนใ—ใพใ—ใŸ๏ผˆweb logout --all๏ผ‰ใ€‚ๆ–ฐใ—ใ„ใƒˆใƒผใ‚ฏใƒณใ‚’ๆธกใ™ใซใฏๅœๆญขใ—ใฆๅ†้–‹ใ™ใ‚‹ใ‹ใ€ใ‚‚ใ†ไธ€ๅบฆ้–‹ๅง‹ใ—ใฆใใ ใ•ใ„",
592
653
  "ag.ended": "ใ“ใฎใ‚ปใƒƒใ‚ทใƒงใƒณใฏ็ต‚ไบ†ใ—ใพใ—ใŸใ€‚่จ˜้Œฒใฏๅ†้–‹ใ‹ๅ‰Š้™คใพใงๆฎ‹ใ‚Šใพใ™ใ€‚",
654
+ "ag.switched": "{p} ใซๅˆ‡ใ‚Šๆ›ฟใˆใพใ—ใŸ",
593
655
  "ag.exitedLine": "[ใ‚ปใƒƒใ‚ทใƒงใƒณใŒ็ต‚ไบ†ใ—ใพใ—ใŸ{code}]",
594
656
  "ag.lost": "[ๅˆ‡ๆ–ญ: {why}]",
595
657
  "ag.reconnect": "ๅ†ๆŽฅ็ถš",
@@ -355,7 +355,7 @@
355
355
  white-space: nowrap;
356
356
  }
357
357
  .run-btn:hover:not(:disabled) {
358
- opacity: 0.85;
358
+ filter: brightness(0.92);
359
359
  }
360
360
  .run-btn:disabled {
361
361
  opacity: 0.45;
@@ -344,7 +344,7 @@
344
344
  cursor: pointer;
345
345
  }
346
346
  .new-ws-form button:hover {
347
- opacity: 0.85;
347
+ filter: brightness(0.92);
348
348
  }
349
349
  /* โ”€โ”€ Content โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
350
350
  .content {
@@ -552,7 +552,7 @@
552
552
  cursor: pointer;
553
553
  }
554
554
  .add-btn:hover {
555
- opacity: 0.85;
555
+ filter: brightness(0.92);
556
556
  }
557
557
  .form-msg {
558
558
  font-size: 11px;
@@ -688,8 +688,9 @@
688
688
  .add-svc-row.selected {
689
689
  background: color-mix(in srgb, var(--accent) 9%, transparent);
690
690
  }
691
+ /* Already in the workspace: readable (WCAG AA), not dimmed. */
691
692
  .add-svc-row.already {
692
- opacity: 0.4;
693
+ color: var(--muted);
693
694
  }
694
695
  .add-svc-row-info {
695
696
  flex: 1;
@@ -839,7 +840,7 @@
839
840
  cursor: pointer;
840
841
  }
841
842
  .add-svc-submit:hover {
842
- opacity: 0.85;
843
+ filter: brightness(0.92);
843
844
  }
844
845
  .add-svc-submit:disabled {
845
846
  opacity: 0.45;
@@ -181,6 +181,17 @@ dialog.pe-dlg::backdrop {
181
181
  .helpl.warn {
182
182
  color: var(--warn);
183
183
  }
184
+ /* AG-17.1c, AG-18: what a persona changes, under the list. */
185
+ .den-keep {
186
+ margin: 8px 0 0;
187
+ padding-left: 18px;
188
+ font-size: 12px;
189
+ line-height: 1.45;
190
+ color: var(--muted);
191
+ }
192
+ .den-keep li + li {
193
+ margin-top: 2px;
194
+ }
184
195
  .faint {
185
196
  color: var(--faint);
186
197
  }
@@ -561,3 +572,24 @@ dialog.pe-dlg::backdrop {
561
572
  .pr[aria-disabled="true"]:hover {
562
573
  background: none;
563
574
  }
575
+
576
+ /* ON-3's approval dialog (shell.js, every page): the Start dialog's frame,
577
+ the request in the body text's size and weight, and Review's lines in
578
+ the mono box the persona Details use. */
579
+ .den-ap-dlg .den-ap-what {
580
+ color: var(--text);
581
+ font: 600 14.5px/1.45 var(--sans);
582
+ overflow-wrap: anywhere;
583
+ }
584
+ .den-ap-dlg .den-ap-det {
585
+ margin: 0;
586
+ max-height: 260px;
587
+ overflow: auto;
588
+ padding: 10px 12px;
589
+ border: 1px solid var(--border);
590
+ border-radius: var(--radius-button, 6px);
591
+ background: var(--surface, var(--raised));
592
+ font: 12px/1.5 var(--mono);
593
+ white-space: pre-wrap;
594
+ overflow-wrap: anywhere;
595
+ }
@@ -398,7 +398,6 @@
398
398
  .loading-note .sub {
399
399
  display: block;
400
400
  margin-top: 4px;
401
- opacity: 0.75;
402
401
  font-size: 11px;
403
402
  }
404
403
  .loading-dots::after {
@@ -160,7 +160,9 @@ export function deviceLine(d, now = Date.now()) {
160
160
  ? `signed in with GitHub${d.login ? ` (@${d.login})` : ""}`
161
161
  : d.via === "code"
162
162
  ? "approved with a code"
163
- : "signed in with a one-time link";
163
+ : d.via === "claim"
164
+ ? "claimed with a setup link"
165
+ : "signed in with a one-time link";
164
166
  const name = d.name || "Unknown browser";
165
167
  const from = d.client || d.host;
166
168
  return {
@@ -340,10 +340,18 @@
340
340
  .set-radio:hover {
341
341
  background: var(--sunken);
342
342
  }
343
+ /* Not installed: the option stays readable (WCAG AA); only its
344
+ radio dims. */
343
345
  .set-radio.off {
344
- opacity: 0.5;
346
+ color: var(--muted);
345
347
  cursor: default;
346
348
  }
349
+ .set-radio.off:hover {
350
+ background: none;
351
+ }
352
+ .set-radio.off input {
353
+ opacity: 0.5;
354
+ }
347
355
  .set-radio input {
348
356
  accent-color: var(--accent);
349
357
  margin: 0;