@monster-orange/den 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +348 -0
- package/README.md +12 -11
- package/docs/distributions.md +438 -91
- package/docs/reference.md +8 -3
- package/package.json +1 -1
- package/src/shared/agents/catalog-runtime.ts +178 -0
- package/src/shared/agents/catalog.ts +343 -0
- package/src/shared/agents/den-claude.ts +14 -18
- package/src/shared/agents/runtime-search.ts +142 -15
- package/src/shared/agents/runtime.ts +18 -1
- package/src/shared/agents/runtimes.ts +23 -1
- package/src/shared/auth/sudo-vault.ts +131 -11
- package/src/shared/cli-command.ts +186 -0
- package/src/shared/confirm-code.ts +34 -0
- package/src/shared/den-config.ts +64 -3
- package/src/shared/dev-metrics/emit.ts +31 -1
- package/src/shared/edge/config.ts +39 -0
- package/src/shared/edge/render.ts +92 -18
- package/src/shared/edge/runtime.ts +57 -1
- package/src/shared/edge/status.ts +134 -0
- package/src/shared/edge/types.ts +11 -1
- package/src/shared/environment-manager/health.ts +82 -8
- package/src/shared/environment-manager/workspaces.ts +54 -0
- package/src/shared/github-app.ts +94 -0
- package/src/shared/github-device.ts +286 -0
- package/src/shared/health/not-checked.ts +42 -0
- package/src/shared/health/rollup.ts +4 -0
- package/src/shared/process/spawn-streaming.ts +8 -3
- package/src/shared/secrets/onepassword.ts +27 -4
- package/src/shared/sessions/client.ts +5 -1
- package/src/shared/sessions/host.ts +29 -1
- package/src/shared/sessions/protocol.ts +6 -1
- package/src/shared/sessions/types.ts +6 -0
- package/src/shared/stacks/status.ts +24 -0
- package/src/shared/strategies/registry.ts +5 -0
- package/src/shared/types/index.ts +6 -0
- package/src/shared/web-owner.ts +91 -0
- package/src/shared/web-session.ts +135 -1
- package/src/ui/cli/agent-ops.ts +210 -19
- package/src/ui/cli/agents-command.ts +50 -21
- package/src/ui/cli/edge-command.ts +16 -3
- package/src/ui/cli/flag-parser.ts +1 -1
- package/src/ui/cli/health-display.ts +4 -1
- package/src/ui/cli/web-command.ts +512 -33
- package/src/ui/mcp/scope.ts +23 -3
- package/src/ui/mcp/server.ts +31 -7
- package/src/ui/mcp/tools.ts +327 -20
- package/src/ui/mcp/views.ts +450 -0
- package/src/ui/server/agent-routes.ts +118 -28
- package/src/ui/server/device-flows.ts +274 -0
- package/src/ui/server/edge-exposure.ts +16 -4
- package/src/ui/server/github-routes.ts +321 -0
- package/src/ui/server/http-errors.ts +10 -0
- package/src/ui/server/index.ts +540 -33
- package/src/ui/server/login-waits.ts +203 -0
- package/src/ui/server/pair-requests.ts +237 -0
- package/src/ui/server/pair-routes.ts +196 -0
- package/src/ui/server/public/agent-start.js +46 -7
- package/src/ui/server/public/agents-page.js +232 -97
- package/src/ui/server/public/agents-section.js +3 -5
- package/src/ui/server/public/agents.html +12 -28
- package/src/ui/server/public/error-page.js +452 -40
- package/src/ui/server/public/error.html +59 -0
- package/src/ui/server/public/graph-model.js +2 -0
- package/src/ui/server/public/header.js +2 -3
- package/src/ui/server/public/health-rollup.js +20 -8
- package/src/ui/server/public/i18n/es.js +89 -21
- package/src/ui/server/public/i18n/ja.js +88 -21
- package/src/ui/server/public/index.html +17 -4
- package/src/ui/server/public/local-dev.html +44 -21
- package/src/ui/server/public/qr.js +317 -0
- package/src/ui/server/public/services.html +21 -5
- package/src/ui/server/public/settings-page.js +10 -2
- package/src/ui/server/public/settings-registry.js +617 -1
- package/src/ui/server/public/settings.html +90 -1
- package/src/ui/server/public/shell.js +151 -33
- package/src/ui/server/public/topbar.js +170 -206
- package/src/ui/server/public/workspace-picker.js +1 -1
- package/src/ui/server/readonly-routes.ts +29 -5
- package/src/ui/server/request-guard.ts +103 -36
- package/src/ui/server/route-classes.ts +116 -4
- package/src/ui/server/session-routes.ts +9 -1
- package/src/ui/server/sudo-store.ts +170 -6
- package/src/ui/server/ui-assets.ts +2 -0
- package/src/ui/server/workspace-routes.ts +10 -3
|
@@ -95,7 +95,9 @@
|
|
|
95
95
|
top: 0;
|
|
96
96
|
z-index: 4;
|
|
97
97
|
background: var(--bg);
|
|
98
|
-
box-shadow:
|
|
98
|
+
box-shadow:
|
|
99
|
+
0 -20px 0 0 var(--bg),
|
|
100
|
+
0 12px 0 0 var(--bg);
|
|
99
101
|
}
|
|
100
102
|
.ph .act {
|
|
101
103
|
margin-left: auto;
|
|
@@ -352,6 +354,93 @@
|
|
|
352
354
|
border-radius: 999px;
|
|
353
355
|
padding: 0 6px;
|
|
354
356
|
}
|
|
357
|
+
/* Settings › Agents › Runtimes (AG-0.5): one row per runtime. */
|
|
358
|
+
.set-custom {
|
|
359
|
+
flex: 1 1 100%;
|
|
360
|
+
min-width: 0;
|
|
361
|
+
}
|
|
362
|
+
.rt-scope {
|
|
363
|
+
display: flex;
|
|
364
|
+
align-items: center;
|
|
365
|
+
gap: 8px;
|
|
366
|
+
flex-wrap: wrap;
|
|
367
|
+
font-size: 13px;
|
|
368
|
+
color: var(--muted);
|
|
369
|
+
margin-bottom: 6px;
|
|
370
|
+
}
|
|
371
|
+
.rt-list {
|
|
372
|
+
display: grid;
|
|
373
|
+
border: 1px solid var(--border);
|
|
374
|
+
border-radius: var(--radius-button);
|
|
375
|
+
}
|
|
376
|
+
.rt {
|
|
377
|
+
display: grid;
|
|
378
|
+
grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr) minmax(
|
|
379
|
+
0,
|
|
380
|
+
2fr
|
|
381
|
+
) auto;
|
|
382
|
+
align-items: center;
|
|
383
|
+
gap: 4px 12px;
|
|
384
|
+
padding: 8px 10px;
|
|
385
|
+
font-size: 13px;
|
|
386
|
+
}
|
|
387
|
+
.rt + .rt {
|
|
388
|
+
border-top: 1px solid var(--border);
|
|
389
|
+
}
|
|
390
|
+
.rt-v {
|
|
391
|
+
color: var(--muted);
|
|
392
|
+
}
|
|
393
|
+
.rt-n .set-tag {
|
|
394
|
+
white-space: nowrap;
|
|
395
|
+
}
|
|
396
|
+
.rt-ok {
|
|
397
|
+
color: var(--ok);
|
|
398
|
+
}
|
|
399
|
+
.rt-bad {
|
|
400
|
+
color: var(--muted);
|
|
401
|
+
overflow-wrap: anywhere;
|
|
402
|
+
}
|
|
403
|
+
.rt-w {
|
|
404
|
+
min-width: 0;
|
|
405
|
+
overflow-wrap: anywhere;
|
|
406
|
+
}
|
|
407
|
+
.rt-p {
|
|
408
|
+
font-size: 12px;
|
|
409
|
+
}
|
|
410
|
+
.rt-f {
|
|
411
|
+
grid-column: 1 / -1;
|
|
412
|
+
display: flex;
|
|
413
|
+
flex-wrap: wrap;
|
|
414
|
+
align-items: center;
|
|
415
|
+
gap: 6px;
|
|
416
|
+
}
|
|
417
|
+
.rt-f[hidden],
|
|
418
|
+
.rt-err[hidden] {
|
|
419
|
+
display: none;
|
|
420
|
+
}
|
|
421
|
+
.rt-fl {
|
|
422
|
+
flex-basis: 100%;
|
|
423
|
+
color: var(--muted);
|
|
424
|
+
font-size: 12.5px;
|
|
425
|
+
}
|
|
426
|
+
.rt-f input {
|
|
427
|
+
flex: 1 1 260px;
|
|
428
|
+
min-width: 0;
|
|
429
|
+
}
|
|
430
|
+
.rt-err {
|
|
431
|
+
flex-basis: 100%;
|
|
432
|
+
color: var(--bad);
|
|
433
|
+
font-size: 12.5px;
|
|
434
|
+
overflow-wrap: anywhere;
|
|
435
|
+
}
|
|
436
|
+
@media (max-width: 640px) {
|
|
437
|
+
.rt {
|
|
438
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
439
|
+
}
|
|
440
|
+
.rt-w {
|
|
441
|
+
grid-column: 1 / -1;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
355
444
|
.set-langs {
|
|
356
445
|
display: grid;
|
|
357
446
|
gap: 4px;
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
* each part an .ib button with a .pop menu (an ARIA menu: ↑/↓,
|
|
23
23
|
* Home, End, Enter; Escape and a click outside close it) ·
|
|
24
24
|
* the ⌘K search field (R5) · the status chip (R6) · the Runs
|
|
25
|
-
* chip (R7) · the
|
|
25
|
+
* chip (R7) · the sudo chip (R8, only while asked) · What's new · ◐ ▾ (R10).
|
|
26
26
|
* shell.js draws them; topbar.js (through header.js) drives the
|
|
27
27
|
* chips, the command palette (R17), the status drawer (R15) and
|
|
28
|
-
* the
|
|
28
|
+
* the admin-password prompt; appearance.js the user menu (dev.20).
|
|
29
29
|
* rail Pinned, then the NAV registry by GROUPS (Develop, Verify,
|
|
30
30
|
* Ship, a distribution's own), then Settings and the collapse
|
|
31
31
|
* toggle. One tab stop (the current page); ↑/↓, Home and End
|
|
@@ -132,7 +132,9 @@
|
|
|
132
132
|
"org.title": "Org: {title}",
|
|
133
133
|
"org.titleId": "Org: {title} ({id})",
|
|
134
134
|
"ws.all": "All active",
|
|
135
|
-
"ws.focus": "
|
|
135
|
+
"ws.focus": "CLI default",
|
|
136
|
+
"ws.focusTip":
|
|
137
|
+
"CLI commands run here when you don't name a workspace. Picking a workspace in the top bar only changes what pages show.",
|
|
136
138
|
"ws.none": "No active workspaces",
|
|
137
139
|
"ws.manage": "Manage workspaces…",
|
|
138
140
|
"ws.allN": "All active ({n})",
|
|
@@ -142,12 +144,12 @@
|
|
|
142
144
|
"ws.viewTitle":
|
|
143
145
|
"View: what every page shows ({ws}). A view filter only: it starts, stops and routes nothing.",
|
|
144
146
|
"ws.viewTitleFocus":
|
|
145
|
-
"View: what every page shows ({ws}). A view filter only: it starts, stops and routes nothing, and
|
|
146
|
-
"ws.setFocus": "
|
|
147
|
+
"View: what every page shows ({ws}). A view filter only: it starts, stops and routes nothing, and the CLI default ({focus}) stays the target for commands.",
|
|
148
|
+
"ws.setFocus": "Make {ws} the CLI default",
|
|
147
149
|
"ws.setFocusTip":
|
|
148
|
-
"
|
|
150
|
+
"CLI commands run in it when you don't name a workspace (writes workspaces.yml)",
|
|
149
151
|
"ws.viewFailed": "Could not change the view: {msg}",
|
|
150
|
-
"ws.focusFailed": "Could not
|
|
152
|
+
"ws.focusFailed": "Could not change the CLI default: {msg}",
|
|
151
153
|
// A count's singular is its .one (0.3.0-dev.40: en had none, so the org
|
|
152
154
|
// menu said "1 workspaces").
|
|
153
155
|
"org.n": "{n} workspaces",
|
|
@@ -251,24 +253,20 @@
|
|
|
251
253
|
"dock.err": "failed",
|
|
252
254
|
"dock.last": "Last run: {s} ↗",
|
|
253
255
|
"dock.resize": "Drag to resize",
|
|
254
|
-
// The
|
|
255
|
-
"lock.
|
|
256
|
-
"lock.
|
|
257
|
-
"lock.
|
|
258
|
-
"lock.nopw": "Passwordless sudo (NOPASSWD): no admin password needed",
|
|
259
|
-
"lock.session": "Unlocked for this session",
|
|
260
|
-
"lock.sessionDesc":
|
|
261
|
-
"Unlocked for this session: start, stop and restart work on every page.",
|
|
256
|
+
// The admin password, asked only when a command needs sudo (R8, LK-0).
|
|
257
|
+
"lock.waiting": "Admin password needed",
|
|
258
|
+
"lock.needs": "{cmd} needs your admin password",
|
|
259
|
+
"lock.cmd": "A command",
|
|
262
260
|
"lock.cfg":
|
|
263
261
|
"Saving the password is off: set secrets.provider and secrets.refs.sudo in {file}.",
|
|
264
262
|
"lock.update": "Update stored password",
|
|
265
263
|
"lock.saveTo": "Save to {s}",
|
|
266
264
|
"lock.pw": "Admin password",
|
|
267
|
-
"lock.go": "
|
|
265
|
+
"lock.go": "Continue",
|
|
268
266
|
"lock.checking": "Checking…",
|
|
269
|
-
"lock.note": "Den
|
|
270
|
-
"lock.
|
|
271
|
-
"lock.saveFailed": "
|
|
267
|
+
"lock.note": "Den uses it only for sudo. Agents can't enter it.",
|
|
268
|
+
"lock.saved": "Admin password kept for this session",
|
|
269
|
+
"lock.saveFailed": "Kept for this session, but saving to {s} failed: {err}",
|
|
272
270
|
"lock.wrong": "Incorrect password — try again",
|
|
273
271
|
"lock.reqFailed": "Request failed — is the server running?",
|
|
274
272
|
// What's new (R9) and the stale-server pill.
|
|
@@ -358,19 +356,71 @@
|
|
|
358
356
|
"This server answers only on its own addresses, so a page on another name can't reach it.",
|
|
359
357
|
"err.421.open": "Open on localhost",
|
|
360
358
|
"err.refused.title": "Request refused",
|
|
361
|
-
"err.signin.title": "Sign in to use {name}",
|
|
362
359
|
"err.signin.terminal": "Sign in to use the terminal",
|
|
363
360
|
"err.signin.agents": "Sign in to start agents",
|
|
364
361
|
"err.signin.sub": "This browser isn't signed in",
|
|
365
362
|
"err.signin.lead": "Run {cmd} and open the link.",
|
|
366
363
|
"err.signin.expiredTitle": "Sign-in link expired",
|
|
367
|
-
"err.signin.expired":
|
|
368
|
-
"That sign-in link expired or was used. Run {cmd} for a new one.",
|
|
369
364
|
"err.signin.local":
|
|
370
365
|
"A sign-in belongs to one address. This page is on {host}: open the same page on localhost, where this browser is signed in.",
|
|
371
|
-
"err.signin.why":
|
|
372
|
-
"There is no password: the link comes from a file only your user account can read.",
|
|
373
366
|
"err.signin.copy": "Copy command",
|
|
367
|
+
"err.signin.title": "Sign in to {name}",
|
|
368
|
+
"err.signin.where":
|
|
369
|
+
"You're opening {name} from another computer. {name} runs on {host}, so it needs to know this is you.",
|
|
370
|
+
"err.signin.githubNote":
|
|
371
|
+
"Uses your GitHub account. The owner of this {name} has to have linked theirs.",
|
|
372
|
+
"err.signin.githubNoOwner":
|
|
373
|
+
"Sign in with GitHub needs an owner, and this {name} has none yet. On {host} (not this computer), run:",
|
|
374
|
+
"err.signin.githubRefused":
|
|
375
|
+
"@{login} isn't the owner of this {name}. Ask the owner to add you, or use 'Approve this computer'.",
|
|
376
|
+
"err.signin.approveOr": "Or approve this computer",
|
|
377
|
+
"err.signin.approve": "Approve this computer",
|
|
378
|
+
"err.signin.step1": "On {host} (not this computer), run:",
|
|
379
|
+
"err.signin.step1Or":
|
|
380
|
+
"Or open {name} on {host} itself and go to Settings › Access › Waiting devices.",
|
|
381
|
+
"err.signin.step2":
|
|
382
|
+
"This page signs you in by itself once approved. The code expires in 10 minutes.",
|
|
383
|
+
"err.signin.waiting": "Waiting for approval…",
|
|
384
|
+
"err.signin.noTerminal":
|
|
385
|
+
"Don't have a terminal on {host}? From this computer, run:",
|
|
386
|
+
"err.signin.openTerminal": "Open Terminal",
|
|
387
|
+
"err.signin.sshNote":
|
|
388
|
+
"Open Terminal opens ssh://{target} in your terminal app. It needs SSH access to {target}.",
|
|
389
|
+
"err.signin.whyTitle": "Why?",
|
|
390
|
+
"err.signin.whyText":
|
|
391
|
+
"{name} can start terminals and agents on {host}, so it only lets in people who prove they're the owner. You only do this once per computer: you stay signed in.",
|
|
392
|
+
"err.signin.other": "Other ways (the one-time link with {cmd})",
|
|
393
|
+
"err.signin.otherStep":
|
|
394
|
+
"On {host} (not this computer), run this, then open the link it prints in this browser:",
|
|
395
|
+
"err.signin.codeExpired": "This code expired.",
|
|
396
|
+
"err.signin.codeDenied": "This computer was denied on {host}.",
|
|
397
|
+
"err.signin.codeFailed": "{name} didn't give this page a code. Try again.",
|
|
398
|
+
"err.signin.newCode": "Get a new code",
|
|
399
|
+
"err.signin.youreIn": "You're in.",
|
|
400
|
+
"err.signin.expiredLead":
|
|
401
|
+
"That sign-in link expired or was used. Sign in again below.",
|
|
402
|
+
"err.signin.confirmLead":
|
|
403
|
+
"Enter the code that web login printed on {host}:",
|
|
404
|
+
"err.signin.confirmGo": "Sign in",
|
|
405
|
+
"err.signin.confirmWrong":
|
|
406
|
+
"That code isn't right. Check what web login printed and try again.",
|
|
407
|
+
"err.signin.confirmBusy": "Too many tries. Wait a minute and try again.",
|
|
408
|
+
"err.signin.confirmGone":
|
|
409
|
+
"This link no longer works. On {host}, run web login again for a new one.",
|
|
410
|
+
"err.signin.confirmWhy":
|
|
411
|
+
"The link alone isn't enough: it needs the code shown on {host} too, so a link someone else sees can't sign them in.",
|
|
412
|
+
"err.signin.github": "Sign in with GitHub",
|
|
413
|
+
"err.signin.githubEnter": "Enter this code at {url}:",
|
|
414
|
+
"err.signin.githubOpen": "Open GitHub",
|
|
415
|
+
"err.signin.githubCopy": "Copy code",
|
|
416
|
+
"err.signin.githubWaiting": "Waiting for you to approve it on GitHub…",
|
|
417
|
+
"err.signin.githubDone": "Signed in. Loading…",
|
|
418
|
+
"err.signin.githubExpired": "The code expired before it was approved.",
|
|
419
|
+
"err.signin.githubDenied": "The request was cancelled on GitHub.",
|
|
420
|
+
"err.signin.githubBusy":
|
|
421
|
+
"Too many sign-ins are in progress. Try again in a few minutes.",
|
|
422
|
+
"err.signin.githubFailed": "GitHub couldn't be reached. Try again.",
|
|
423
|
+
"err.signin.githubAgain": "Start again",
|
|
374
424
|
"err.422.title": "Config is invalid",
|
|
375
425
|
"err.422.sub": "The distribution config fails validation",
|
|
376
426
|
"err.422.lead":
|
|
@@ -402,11 +452,21 @@
|
|
|
402
452
|
"set.graph.density": "Cards",
|
|
403
453
|
"set.graph.panelWidth": "Details panel",
|
|
404
454
|
"set.workspace.editor": "IDE for this workspace",
|
|
455
|
+
"set.agents.runtimes": "Runtimes",
|
|
405
456
|
"set.cli.local_dev": "Local environment",
|
|
406
457
|
"set.cli.cli_tabs": "Terminal tabs",
|
|
407
458
|
"set.cli.log_window_lines": "Log pane lines",
|
|
408
459
|
"set.cli.web_port": "Your web port",
|
|
409
460
|
"set.cli.signin": "Browser sign-in",
|
|
461
|
+
"set.cli.sudo": "Admin password",
|
|
462
|
+
"set.access.owner": "Owner",
|
|
463
|
+
"set.access.devices": "Linked devices",
|
|
464
|
+
"ident.local": "Local",
|
|
465
|
+
"ident.device": "Signed in · this device",
|
|
466
|
+
"ident.toast": "This device is signed in to {name} on {host}",
|
|
467
|
+
"set.access.waiting": "Waiting devices",
|
|
468
|
+
"set.access.machine_name": "Machine name",
|
|
469
|
+
"set.access.pair": "Pair a device",
|
|
410
470
|
"set.distribution.brand": "Product name",
|
|
411
471
|
"set.distribution.github_org": "GitHub org",
|
|
412
472
|
"set.distribution.orgs": "Org labels",
|
|
@@ -495,6 +555,11 @@
|
|
|
495
555
|
"ag.rt.fix":
|
|
496
556
|
"Install it, or set {key} to its path in {file}, or run {cmd}.",
|
|
497
557
|
"ag.rt.unknown": "No runtime {id} here.",
|
|
558
|
+
"ag.rt.denTools": "Den tools: coming",
|
|
559
|
+
"ag.rt.ownCli":
|
|
560
|
+
"It runs as its own CLI in the workspace: Den's tools and permission modes are coming for it.",
|
|
561
|
+
"ag.rt.notInstalled": "not installed",
|
|
562
|
+
"ag.rt.needsArgv": "the persona has no argv template",
|
|
498
563
|
};
|
|
499
564
|
// The languages the UI has a catalog for, each named in itself (a name is
|
|
500
565
|
// never translated). The server's list is ui-language.ts's UI_LANGUAGES.
|
|
@@ -581,6 +646,9 @@
|
|
|
581
646
|
agents:
|
|
582
647
|
'<rect x="3" y="5" width="18" height="14" rx="2"/><path d="M7 10l3 2-3 2M12 15h5"/>',
|
|
583
648
|
edge: '<path d="M12 3l8 4v5c0 4.5-3.4 8.3-8 9-4.6-.7-8-4.5-8-9V7z"/><path d="M8 12h8M12 8v8"/>',
|
|
649
|
+
// SEC-2a: who this browser is (the rail footer's identity chip).
|
|
650
|
+
person:
|
|
651
|
+
'<circle cx="12" cy="8" r="3.5"/><path d="M5 20c.8-3.6 3.6-5.5 7-5.5s6.2 1.9 7 5.5"/>',
|
|
584
652
|
};
|
|
585
653
|
function icon(name) {
|
|
586
654
|
var d = ICONS[name] || (/^</.test(name || "") ? name : ICONS.page);
|
|
@@ -871,6 +939,9 @@
|
|
|
871
939
|
// Whether the edge is configured (the Edge page's when): null until
|
|
872
940
|
// known, the last answer cached so the rail doesn't jump.
|
|
873
941
|
edge: null,
|
|
942
|
+
// SEC-2a: who this browser is (GET /api/session `identity`): local,
|
|
943
|
+
// a signed-in device, or a GitHub login. Null until known.
|
|
944
|
+
identity: null,
|
|
874
945
|
};
|
|
875
946
|
try {
|
|
876
947
|
var cached = JSON.parse(localStorage.getItem(LS) || "{}");
|
|
@@ -1347,9 +1418,9 @@
|
|
|
1347
1418
|
|
|
1348
1419
|
// ── the frame ──────────────────────────────────────────────────────────
|
|
1349
1420
|
// The top bar after the scope, in the prototype's order (dev.20): the ⌘K
|
|
1350
|
-
// search field (R5), then .chips: status (R6), Runs (R7), the
|
|
1421
|
+
// search field (R5), then .chips: status (R6), Runs (R7), the sudo ask (R8),
|
|
1351
1422
|
// What's new (R9) and ◐ ▾ (R10, appearance.js fills #den-user). The
|
|
1352
|
-
// chips start neutral ("Checking…", "Runs"
|
|
1423
|
+
// chips start neutral ("Checking…", "Runs"; the sudo chip hidden) and topbar.js
|
|
1353
1424
|
// paints them: a cold load never shows a partial red.
|
|
1354
1425
|
var chips =
|
|
1355
1426
|
'<button type="button" class="den-search" id="den-search" aria-haspopup="dialog" aria-keyshortcuts="Meta+K Control+K" aria-label="' +
|
|
@@ -1366,10 +1437,10 @@
|
|
|
1366
1437
|
'<span class="den-dot off" aria-hidden="true"></span><span class="den-n den-lbl-l">' +
|
|
1367
1438
|
esc(t("runs.title")) +
|
|
1368
1439
|
"</span></button>" +
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
'
|
|
1372
|
-
esc(t("lock.
|
|
1440
|
+
// LK-0: shown only while a run waits for the admin password (topbar.js).
|
|
1441
|
+
'<button type="button" class="den-chip" id="den-lock" aria-haspopup="dialog" aria-expanded="false" hidden>' +
|
|
1442
|
+
'<span class="den-lock-ic" aria-hidden="true">🔑</span><span class="den-lbl-l">' +
|
|
1443
|
+
esc(t("lock.waiting")) +
|
|
1373
1444
|
"</span></button>" +
|
|
1374
1445
|
'<button class="den-ib v-pill" id="version-pill" onclick="openChangelog()" title="' +
|
|
1375
1446
|
esc(t("v.title")) +
|
|
@@ -1777,6 +1848,7 @@
|
|
|
1777
1848
|
},
|
|
1778
1849
|
page === SETTINGS.id,
|
|
1779
1850
|
);
|
|
1851
|
+
html += identityChip();
|
|
1780
1852
|
html +=
|
|
1781
1853
|
'<button class="den-ni den-collapse-btn" type="button" data-act="collapse" aria-expanded="' +
|
|
1782
1854
|
(state.collapsed ? "false" : "true") +
|
|
@@ -1790,6 +1862,49 @@
|
|
|
1790
1862
|
return html;
|
|
1791
1863
|
}
|
|
1792
1864
|
|
|
1865
|
+
/**
|
|
1866
|
+
* SEC-2a: the rail footer's identity chip: "Local" (this machine, no
|
|
1867
|
+
* sign-in), "Signed in · this device" (a one-time link or a code) or
|
|
1868
|
+
* "@login" (GitHub). A rail item like Settings (the same .den-ni
|
|
1869
|
+
* metrics), opening Settings › Access.
|
|
1870
|
+
*/
|
|
1871
|
+
function identityLabel(id) {
|
|
1872
|
+
if (!id) return "";
|
|
1873
|
+
if (id.kind === "local") return t("ident.local");
|
|
1874
|
+
if (id.kind === "github" && id.login) return "@" + id.login;
|
|
1875
|
+
if (id.kind === "github" || id.kind === "device") return t("ident.device");
|
|
1876
|
+
return "";
|
|
1877
|
+
}
|
|
1878
|
+
function identityChip() {
|
|
1879
|
+
var label = identityLabel(state.identity);
|
|
1880
|
+
if (!label) return "";
|
|
1881
|
+
return item(
|
|
1882
|
+
{
|
|
1883
|
+
id: "identity",
|
|
1884
|
+
href: "/settings#access",
|
|
1885
|
+
title: label,
|
|
1886
|
+
icon: "person",
|
|
1887
|
+
},
|
|
1888
|
+
false,
|
|
1889
|
+
"",
|
|
1890
|
+
' id="den-ident" data-ident="' + esc(state.identity.kind) + '"',
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1893
|
+
/** What GET /api/session said about this browser: the chip, and a toast once after a sign-in. */
|
|
1894
|
+
function applyIdentity(b) {
|
|
1895
|
+
if (!b || !b.identity) return;
|
|
1896
|
+
var was = state.identity;
|
|
1897
|
+
if (!was || was.kind !== b.identity.kind || was.login !== b.identity.login)
|
|
1898
|
+
setState({ identity: b.identity });
|
|
1899
|
+
if (b.justSignedIn)
|
|
1900
|
+
toast(
|
|
1901
|
+
t("ident.toast", {
|
|
1902
|
+
name: b.product || "__DEN_TITLE__",
|
|
1903
|
+
host: b.machine || "",
|
|
1904
|
+
}),
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1793
1908
|
// One tab stop for the rail: the current page, else the first item.
|
|
1794
1909
|
function rove() {
|
|
1795
1910
|
var links = Array.prototype.slice.call(nav.querySelectorAll(".den-ni"));
|
|
@@ -3522,6 +3637,7 @@
|
|
|
3522
3637
|
function (b) {
|
|
3523
3638
|
csrf.pending = null;
|
|
3524
3639
|
csrf.token = b && typeof b.csrf === "string" ? b.csrf : null;
|
|
3640
|
+
applyIdentity(b);
|
|
3525
3641
|
return csrf.token;
|
|
3526
3642
|
},
|
|
3527
3643
|
function () {
|
|
@@ -3853,7 +3969,7 @@
|
|
|
3853
3969
|
" max-height: calc(100vh - 72px); overflow-y: auto; box-sizing: border-box;",
|
|
3854
3970
|
" font: 14px/1.45 var(--sans); color: var(--text); }",
|
|
3855
3971
|
".den-pop:focus { outline: none; }",
|
|
3856
|
-
// LK-1: the
|
|
3972
|
+
// LK-1: the admin-password prompt is a <dialog> in the top layer (a popover, or
|
|
3857
3973
|
// a modal over another modal): placed by its top/left, no UA box.
|
|
3858
3974
|
"dialog.den-pop { margin: 0; inset: auto; max-width: none; }",
|
|
3859
3975
|
"dialog.den-pop::backdrop { background: transparent; }",
|
|
@@ -3909,6 +4025,8 @@
|
|
|
3909
4025
|
".den-chip .den-n { font-variant-numeric: tabular-nums; }",
|
|
3910
4026
|
".den-chip .den-dot, .den-drawer .den-dot, .den-pal .den-dot { margin: 0; }",
|
|
3911
4027
|
".den-chip[aria-expanded='true'] { border-color: var(--border-strong); }",
|
|
4028
|
+
".den-chip[hidden] { display: none; }",
|
|
4029
|
+
"#den-lock { border-color: color-mix(in srgb, var(--warn) 55%, var(--border)); }",
|
|
3912
4030
|
".den-dot.run { background: var(--accent); animation: den-pulse 1.2s infinite; }",
|
|
3913
4031
|
".den-dot.pending { background: var(--faint); animation: den-pulse 1.2s infinite; }",
|
|
3914
4032
|
"@keyframes den-pulse { 50% { opacity: .35; } }",
|
|
@@ -3973,7 +4091,7 @@
|
|
|
3973
4091
|
".den-pal li.den-pal-empty { cursor: default; color: var(--muted); }",
|
|
3974
4092
|
".den-pal .den-foot { display: flex; gap: 14px; padding: 8px 14px; border-top: 1px solid var(--border);",
|
|
3975
4093
|
" color: var(--faint); font-size: 12px; flex-wrap: wrap; }",
|
|
3976
|
-
// The
|
|
4094
|
+
// The admin-password prompt (.pop form, .pop input, .note: R8, LK-0)
|
|
3977
4095
|
".den-pop form { display: grid; gap: 8px; padding: 6px 8px 8px; width: 260px; box-sizing: content-box; margin: 0; }",
|
|
3978
4096
|
".den-pop input:not([type='checkbox']) { border: 1px solid var(--border); background: var(--bg); color: var(--text);",
|
|
3979
4097
|
" border-radius: var(--radius-button); padding: 6px 8px; font: inherit; font-size: 13.3333px; line-height: normal; }",
|
|
@@ -3982,7 +4100,7 @@
|
|
|
3982
4100
|
".den-pop .den-lock-save { display: flex; gap: 6px; align-items: center; color: var(--muted); font-size: 12.5px; }",
|
|
3983
4101
|
".den-pop .den-lock-save[hidden], .den-pop .den-lock-err[hidden], .den-mslot[hidden] { display: none; }",
|
|
3984
4102
|
".den-pop .den-lock-err { color: var(--bad); font-size: 12.5px; }",
|
|
3985
|
-
".den-pop
|
|
4103
|
+
".den-pop #den-lock-t { overflow-wrap: anywhere; }",
|
|
3986
4104
|
// What's new (.ib.whatsnew + .badge-dot)
|
|
3987
4105
|
"#den-shell .v-pill { font: inherit; padding: 4px 8px; border: 1px solid transparent;",
|
|
3988
4106
|
" border-radius: var(--radius-button); color: var(--muted); flex-shrink: 0; }",
|