sideby 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/build.json +1 -1
- package/dist/src/cli/commands.d.ts +4 -2
- package/dist/src/cli/commands.js +117 -17
- package/dist/src/cli/json-schemas.js +52 -1
- package/dist/src/cli/main.js +34 -3
- package/dist/src/core/brand-mark.d.ts +5 -7
- package/dist/src/core/brand-mark.js +18 -5
- package/dist/src/core/browser-profiles.d.ts +71 -0
- package/dist/src/core/browser-profiles.js +361 -0
- package/dist/src/core/config.d.ts +15 -0
- package/dist/src/core/config.js +13 -1
- package/dist/src/core/create.js +2 -1
- package/dist/src/core/desktop-app.d.ts +3 -1
- package/dist/src/core/desktop-app.js +29 -8
- package/dist/src/core/launch.d.ts +7 -0
- package/dist/src/core/launch.js +22 -2
- package/dist/src/core/leveldb-keys.d.ts +14 -0
- package/dist/src/core/leveldb-keys.js +291 -0
- package/dist/src/core/remote-control.d.ts +17 -6
- package/dist/src/core/remote-control.js +40 -17
- package/dist/src/core/update.d.ts +10 -3
- package/dist/src/core/update.js +38 -3
- package/dist/src/core/version.d.ts +11 -0
- package/dist/src/core/version.js +26 -0
- package/dist/src/desktop/app.d.ts +2 -0
- package/dist/src/desktop/app.js +39 -3
- package/dist/src/families/browser.d.ts +20 -0
- package/dist/src/families/browser.js +23 -0
- package/dist/src/families/claude/browser.d.ts +3 -0
- package/dist/src/families/claude/browser.js +14 -0
- package/dist/src/families/claude/desktop.js +1 -0
- package/dist/src/families/codex/browser.d.ts +7 -0
- package/dist/src/families/codex/browser.js +26 -0
- package/dist/src/families/desktop.d.ts +5 -1
- package/dist/src/families/pi/index.js +2 -0
- package/dist/src/panel/handler.d.ts +6 -0
- package/dist/src/panel/handler.js +49 -6
- package/dist/src/panel/i18n.d.ts +75 -4
- package/dist/src/panel/i18n.js +162 -20
- package/dist/src/panel/page-logic.d.ts +23 -0
- package/dist/src/panel/page-logic.js +44 -0
- package/dist/src/panel/page-script.js +609 -180
- package/dist/src/panel/page-styles.js +72 -17
- package/dist/src/panel/page.d.ts +5 -0
- package/dist/src/panel/page.js +10 -9
- package/dist/src/runtime.d.ts +6 -1
- package/dist/src/runtime.js +22 -11
- package/dist/src/types.d.ts +2 -0
- package/package.json +5 -3
- package/schemas/browser-open.json +47 -0
- package/schemas/browser-switch.json +42 -0
- package/schemas/browser.json +99 -0
- package/schemas/config.json +11 -0
- package/schemas/families.json +6 -0
- package/schemas/list.json +6 -0
- package/schemas/remote-control.json +4 -0
- package/schemas/update.json +4 -0
- package/skills/sideby/SKILL.md +5 -3
- package/skills/sideby/references/setup.md +1 -1
package/dist/src/desktop/app.js
CHANGED
|
@@ -39,7 +39,7 @@ export function launcherScript(s) {
|
|
|
39
39
|
: `command -v notify-send >/dev/null 2>&1 && notify-send "sideby could not start" "Run sideby app install again in a terminal. Details: $LOG"`;
|
|
40
40
|
const lines = [
|
|
41
41
|
'#!/bin/sh',
|
|
42
|
-
`# ${APP_MARKER}: written by \`sideby app install
|
|
42
|
+
`# ${APP_MARKER}: written by \`sideby app install\` (\`sideby update\` runs it again). Run it yourself after upgrading Node.`,
|
|
43
43
|
`NODE=${shq(s.node)}`,
|
|
44
44
|
`ENTRY=${shq(s.entry)}`,
|
|
45
45
|
`URL=${shq(s.url ?? '')}`,
|
|
@@ -185,6 +185,38 @@ async function ownership(entry) {
|
|
|
185
185
|
}
|
|
186
186
|
return markerTest(entry)(data) ? { state: 'ours' } : foreign(entry, NOT_OURS);
|
|
187
187
|
}
|
|
188
|
+
/** The launcher script inside what install writes: the bundle's executable on macOS, the launcher file on Linux. */
|
|
189
|
+
function launcherPath(l, platform) {
|
|
190
|
+
return platform === 'darwin' ? join(l.primary, 'Contents', 'MacOS', APP_NAME) : l.linux.launcher;
|
|
191
|
+
}
|
|
192
|
+
/** The `URL='…'` an earlier install wrote into the launcher, when it is a valid http(s) address; never follows a link. */
|
|
193
|
+
async function recordedUrl(launcher) {
|
|
194
|
+
let text;
|
|
195
|
+
try {
|
|
196
|
+
text = (await readNoFollow(launcher)).toString('utf8');
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
if (text.split('\n', 2)[1]?.startsWith(`# ${APP_MARKER}: `) !== true)
|
|
202
|
+
return undefined;
|
|
203
|
+
const m = /^URL='((?:[^']|'\\'')*)'$/m.exec(text);
|
|
204
|
+
const value = m?.[1]?.replace(/'\\''/g, "'");
|
|
205
|
+
if (!value)
|
|
206
|
+
return undefined;
|
|
207
|
+
try {
|
|
208
|
+
return checkAppUrl(value);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** Whether a sideby desktop app is installed for this home: its main file exists and carries APP_MARKER. */
|
|
215
|
+
export async function appInstalled(ctx) {
|
|
216
|
+
if (ctx.platform !== 'darwin' && ctx.platform !== 'linux')
|
|
217
|
+
return false;
|
|
218
|
+
return (await ownership(layout(ctx, ctx.platform).primary)).state === 'ours';
|
|
219
|
+
}
|
|
188
220
|
/** Refuses to replace anything at `entry` that `sideby app install` did not write; true when it is ours. */
|
|
189
221
|
async function assertOwned(entry, verb) {
|
|
190
222
|
const o = await ownership(entry);
|
|
@@ -199,12 +231,14 @@ export async function installApp(ctx, opts) {
|
|
|
199
231
|
const run = ctx.run ?? defaultRunner;
|
|
200
232
|
if (!isAbsolute(opts.node) || !isAbsolute(opts.entry))
|
|
201
233
|
throw new UserError('node and the sideby entry must be absolute paths');
|
|
202
|
-
const url = opts.url === undefined ? undefined : checkAppUrl(opts.url);
|
|
203
234
|
const l = layout(ctx, platform);
|
|
204
235
|
let updated = false;
|
|
205
236
|
for (const e of l.entries)
|
|
206
237
|
if (await assertOwned(e, 'install'))
|
|
207
238
|
updated = true;
|
|
239
|
+
// Without --url, a reinstall (by hand or after `sideby update`) keeps the address an earlier --url recorded.
|
|
240
|
+
const kept = opts.url === undefined && updated ? await recordedUrl(launcherPath(l, platform)) : undefined;
|
|
241
|
+
const url = opts.url === undefined ? kept : checkAppUrl(opts.url);
|
|
208
242
|
const xdg = {};
|
|
209
243
|
for (const k of ['XDG_CONFIG_HOME', 'XDG_STATE_HOME']) {
|
|
210
244
|
const v = ctx.env[k];
|
|
@@ -274,8 +308,10 @@ export async function installApp(ctx, opts) {
|
|
|
274
308
|
}
|
|
275
309
|
if (url)
|
|
276
310
|
hints.push(`The app opens ${url}.`);
|
|
311
|
+
if (kept)
|
|
312
|
+
hints.push('Kept the address from the earlier install; to start the panel instead, run `sideby app uninstall`, then `sideby app install`.');
|
|
277
313
|
else
|
|
278
|
-
hints.push('The app starts the panel in the background and opens it; stop the panel with `sideby ui --stop`.', '
|
|
314
|
+
hints.push('The app starts the panel in the background and opens it; stop the panel with `sideby ui --stop`.', '`sideby update` refreshes the app; run `sideby app install` again after upgrading Node or installing sideby another way.');
|
|
279
315
|
if (/[/\\]_npx[/\\]/.test(opts.entry))
|
|
280
316
|
hints.push('sideby runs from the npx cache, which npm may clear; install it with `npm i -g sideby` and run this again.');
|
|
281
317
|
return {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Account } from '../types.ts';
|
|
2
|
+
export interface WebsiteDef {
|
|
3
|
+
/** The Family id. */
|
|
4
|
+
family: string;
|
|
5
|
+
/** Where "Open in browser" goes. */
|
|
6
|
+
url: string;
|
|
7
|
+
/**
|
|
8
|
+
* The Local Storage origin whose key names may hold `signInId`, and the reader of that id from the Host's login
|
|
9
|
+
* file. The id is compared in memory only; it is never shown, returned, logged or cached. Missing when the website
|
|
10
|
+
* keeps no id the Host also has (grok.com), so the Account can only be opened in a Browser Profile the user picks.
|
|
11
|
+
*/
|
|
12
|
+
match?: {
|
|
13
|
+
origin: string;
|
|
14
|
+
signInId(account: Account): Promise<string | undefined>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** The website of a built-in Family id, if it has one. */
|
|
18
|
+
export declare function websiteDef(familyId: string): WebsiteDef | undefined;
|
|
19
|
+
/** Every website that can be matched, for the origins to read. */
|
|
20
|
+
export declare function matchedOrigins(): string[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { claudeSignInId } from './claude/browser.js';
|
|
2
|
+
import { codexSignInId } from './codex/browser.js';
|
|
3
|
+
const WEBSITES = new Map([
|
|
4
|
+
{
|
|
5
|
+
family: 'claude',
|
|
6
|
+
url: 'https://claude.ai',
|
|
7
|
+
match: { origin: 'https://claude.ai', signInId: claudeSignInId },
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
family: 'codex',
|
|
11
|
+
url: 'https://chatgpt.com',
|
|
12
|
+
match: { origin: 'https://chatgpt.com', signInId: codexSignInId },
|
|
13
|
+
},
|
|
14
|
+
{ family: 'grok', url: 'https://grok.com' },
|
|
15
|
+
].map((w) => [w.family, w]));
|
|
16
|
+
/** The website of a built-in Family id, if it has one. */
|
|
17
|
+
export function websiteDef(familyId) {
|
|
18
|
+
return WEBSITES.get(familyId);
|
|
19
|
+
}
|
|
20
|
+
/** Every website that can be matched, for the origins to read. */
|
|
21
|
+
export function matchedOrigins() {
|
|
22
|
+
return [...WEBSITES.values()].flatMap((w) => (w.match ? [w.match.origin] : []));
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// The Claude account id that claude.ai's Local Storage key names also carry (spec §3.24, ADR-0023).
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { claudeJsonOf } from './index.js';
|
|
4
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
|
+
/** `oauthAccount.accountUuid` of `.claude.json`, only for matching; every other field is dropped at once. */
|
|
6
|
+
export async function claudeSignInId(account) {
|
|
7
|
+
try {
|
|
8
|
+
const id = JSON.parse(await readFile(claudeJsonOf(account), 'utf8'))?.oauthAccount?.accountUuid;
|
|
9
|
+
return typeof id === 'string' && UUID.test(id) ? id.toLowerCase() : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Account } from '../../types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* `chatgpt_user_id` (else `user_id`, as Codex itself falls back: codex-rs/login/src/token_data.rs) under the auth
|
|
4
|
+
* claim of `tokens.id_token`, only for matching. The payload is decoded as for the email (core/identity.ts); the
|
|
5
|
+
* token and every other claim are dropped at once. `tokens.account_id` is the workspace and is not used.
|
|
6
|
+
*/
|
|
7
|
+
export declare function codexSignInId(account: Account): Promise<string | undefined>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// The ChatGPT user id that chatgpt.com's Local Storage key names also carry (spec §3.24, ADR-0023).
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const USER_ID = /^user-[A-Za-z0-9]{8,64}$/;
|
|
5
|
+
const AUTH_CLAIM = 'https://api.openai.com/auth';
|
|
6
|
+
/**
|
|
7
|
+
* `chatgpt_user_id` (else `user_id`, as Codex itself falls back: codex-rs/login/src/token_data.rs) under the auth
|
|
8
|
+
* claim of `tokens.id_token`, only for matching. The payload is decoded as for the email (core/identity.ts); the
|
|
9
|
+
* token and every other claim are dropped at once. `tokens.account_id` is the workspace and is not used.
|
|
10
|
+
*/
|
|
11
|
+
export async function codexSignInId(account) {
|
|
12
|
+
try {
|
|
13
|
+
const token = JSON.parse(await readFile(join(account.dir, 'auth.json'), 'utf8'))?.tokens?.id_token;
|
|
14
|
+
if (typeof token !== 'string')
|
|
15
|
+
return undefined;
|
|
16
|
+
const payload = token.split('.')[1];
|
|
17
|
+
if (!payload)
|
|
18
|
+
return undefined;
|
|
19
|
+
const auth = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'))?.[AUTH_CLAIM];
|
|
20
|
+
const id = auth?.chatgpt_user_id ?? auth?.user_id;
|
|
21
|
+
return typeof id === 'string' && USER_ID.test(id) ? id : undefined;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -17,10 +17,14 @@ export interface DesktopAppDef {
|
|
|
17
17
|
clearVars: readonly string[];
|
|
18
18
|
/** The first sign-in happens inside the app, through a URL callback another running instance may catch. */
|
|
19
19
|
signInInApp: boolean;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Files of the App Data shared from the Main Account's default App Data as links (`mainPath` relative to HOME).
|
|
22
|
+
* With `key`, a real file the app made in place of the link is fine while that JSON key matches the main one.
|
|
23
|
+
*/
|
|
21
24
|
sharedFiles: readonly {
|
|
22
25
|
path: string;
|
|
23
26
|
mainPath: string;
|
|
27
|
+
key?: string;
|
|
24
28
|
}[];
|
|
25
29
|
}
|
|
26
30
|
/** The Desktop App of a built-in Family id, if it has one. */
|
|
@@ -28,6 +28,8 @@ export const piFamily = {
|
|
|
28
28
|
logo: BUILTIN_LOGOS.pi,
|
|
29
29
|
bin: 'pi',
|
|
30
30
|
installUrl: 'https://github.com/earendil-works/pi',
|
|
31
|
+
// The managed install stages and smoke-tests each release before switching to it; npm upgrades in place.
|
|
32
|
+
installCommand: 'curl -fsSL https://pi.dev/install.sh | sh',
|
|
31
33
|
selectVar: 'PI_CODING_AGENT_DIR',
|
|
32
34
|
secretFileMeansApi: false,
|
|
33
35
|
layout: { main: '.pi/agent', account: '.pi-<name>/agent' },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
import { type AppProfileStatus } from '../core/app-profiles.ts';
|
|
3
|
+
import { type BrowserReport } from '../core/browser-profiles.ts';
|
|
3
4
|
import { type HandoffSettings } from '../core/config.ts';
|
|
4
5
|
import { type AccountModel } from '../core/model-choice.ts';
|
|
5
6
|
import type { RemoteControlGroup } from '../core/remote-control.ts';
|
|
@@ -132,6 +133,11 @@ export interface PanelState {
|
|
|
132
133
|
appProfiles?: AppProfileStatus[];
|
|
133
134
|
/** Update Check result and Auto Update setting (spec §3.23); absent with `updates: false`. */
|
|
134
135
|
update?: PanelUpdate;
|
|
136
|
+
/**
|
|
137
|
+
* Browser Sign-ins (spec §3.24): which Chrome browser user signed in to which Account, read on this computer only.
|
|
138
|
+
* Absent without Claude, Codex or Grok subscription Accounts. Nothing of the browser is read while `enabled` is false.
|
|
139
|
+
*/
|
|
140
|
+
browser?: BrowserReport;
|
|
135
141
|
}
|
|
136
142
|
/** The `handoff` map of the Panel state, from the statuses and Quota already read for the page. */
|
|
137
143
|
export declare function handoffByFamily(rt: Runtime, accounts: readonly PanelAccount[], now: Date): Record<string, string>;
|
|
@@ -3,6 +3,7 @@ import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
|
3
3
|
import { aliasesByAccount } from '../core/accounts.js';
|
|
4
4
|
import { listAppProfiles, openAppProfile, removeAppProfile, } from '../core/app-profiles.js';
|
|
5
5
|
import { FAVICON_SVG, ICON_SVG } from '../core/brand-mark.js';
|
|
6
|
+
import { browserReport, openInBrowser, setBrowserSignIns, } from '../core/browser-profiles.js';
|
|
6
7
|
import { aliasArgs, aliasRefs, editConfigUpdate } from '../core/config.js';
|
|
7
8
|
import { openDesktopApp, withDesktopApps } from '../core/desktop-app.js';
|
|
8
9
|
import { UserError } from '../core/errors.js';
|
|
@@ -15,7 +16,7 @@ import { tildify } from '../core/paths.js';
|
|
|
15
16
|
import { SKILL_INSTALL } from '../core/skill-check.js';
|
|
16
17
|
import { MIN_CALLS } from '../core/tuning.js';
|
|
17
18
|
import * as update from '../core/update.js';
|
|
18
|
-
import { buildId, packageVersion } from '../core/version.js';
|
|
19
|
+
import { buildId, localBuild, packageVersion } from '../core/version.js';
|
|
19
20
|
import { HOOK_FAMILIES } from '../handoff/hook.js';
|
|
20
21
|
import { renderPage } from './page.js';
|
|
21
22
|
import { resolveTheme } from './theme.js';
|
|
@@ -265,6 +266,8 @@ async function buildState(rt, opts) {
|
|
|
265
266
|
};
|
|
266
267
|
});
|
|
267
268
|
const handoff = handoffByFamily(rt, list, ctx.now);
|
|
269
|
+
const browser = await browserReport(rt, accounts).catch(() => undefined);
|
|
270
|
+
const showBrowser = browser && (browser.families.matched.length || browser.families.openOnly.length);
|
|
268
271
|
return {
|
|
269
272
|
schemaVersion: 1,
|
|
270
273
|
version: opts.version,
|
|
@@ -273,7 +276,8 @@ async function buildState(rt, opts) {
|
|
|
273
276
|
generatedAt: ctx.now.toISOString(),
|
|
274
277
|
families: families.map((f) => {
|
|
275
278
|
const logo = rt.families.get(f.id)?.logo;
|
|
276
|
-
|
|
279
|
+
const g = f.brokenBin ? { ...f, brokenBin: tildify(f.brokenBin, ctx.home) } : f;
|
|
280
|
+
return logo ? { ...g, logo } : g;
|
|
277
281
|
}),
|
|
278
282
|
accounts: list,
|
|
279
283
|
pluginErrors: rt.pluginErrors.map((e) => ({ where: e.where, message: e.message })),
|
|
@@ -294,6 +298,7 @@ async function buildState(rt, opts) {
|
|
|
294
298
|
.map((f) => f.id),
|
|
295
299
|
},
|
|
296
300
|
...(upd ? { update: upd } : {}),
|
|
301
|
+
...(showBrowser ? { browser } : {}),
|
|
297
302
|
};
|
|
298
303
|
}
|
|
299
304
|
/** The `change` of a /api/handoff/configure body, with every field checked; unknown fields are refused. */
|
|
@@ -351,6 +356,7 @@ export function createPanelHandler(opts) {
|
|
|
351
356
|
const readOnly = Boolean(opts.readOnly);
|
|
352
357
|
const version = opts.version ?? PANEL_VERSION;
|
|
353
358
|
const build = buildId();
|
|
359
|
+
const local = localBuild();
|
|
354
360
|
const theme = resolveTheme(opts.theme);
|
|
355
361
|
const lang = resolveLang(opts.lang);
|
|
356
362
|
const updates = opts.updates !== false;
|
|
@@ -450,8 +456,8 @@ export function createPanelHandler(opts) {
|
|
|
450
456
|
return { ref: found.ref, undone: r.ok, message: r.message };
|
|
451
457
|
},
|
|
452
458
|
},
|
|
453
|
-
// Remote Control
|
|
454
|
-
// settings file that
|
|
459
|
+
// Remote Control at startup (spec §3.22, ADR-0017): writes `remoteControlAtStartup` (false for off, true for on)
|
|
460
|
+
// into each Claude settings file that does not hold it yet; the page shows the files first, from the state.
|
|
455
461
|
'/api/remote-control/off': {
|
|
456
462
|
async run() {
|
|
457
463
|
const rt = await getRuntime();
|
|
@@ -460,6 +466,14 @@ export function createPanelHandler(opts) {
|
|
|
460
466
|
return { ...r, results: r.results.map((x) => ({ ...x, file: tildify(x.file, home) })) };
|
|
461
467
|
},
|
|
462
468
|
},
|
|
469
|
+
'/api/remote-control/on': {
|
|
470
|
+
async run() {
|
|
471
|
+
const rt = await getRuntime();
|
|
472
|
+
const r = await rt.remoteControlOn();
|
|
473
|
+
const home = rt.paths.home;
|
|
474
|
+
return { ...r, results: r.results.map((x) => ({ ...x, file: tildify(x.file, home) })) };
|
|
475
|
+
},
|
|
476
|
+
},
|
|
463
477
|
'/api/remote-control/undo': {
|
|
464
478
|
async run() {
|
|
465
479
|
const rt = await getRuntime();
|
|
@@ -524,6 +538,29 @@ export function createPanelHandler(opts) {
|
|
|
524
538
|
};
|
|
525
539
|
},
|
|
526
540
|
},
|
|
541
|
+
// Browser Sign-ins on or off (spec §3.24): config `browser.signIns`, shown first like every config change.
|
|
542
|
+
'/api/browser/settings': {
|
|
543
|
+
writes: (body) => body.confirm === true,
|
|
544
|
+
async run(body) {
|
|
545
|
+
if (typeof body.enabled !== 'boolean')
|
|
546
|
+
throw new HttpError(400, '"enabled" must be true or false');
|
|
547
|
+
const rt = await getRuntime();
|
|
548
|
+
const r = await setBrowserSignIns(rt, body.enabled, { apply: optBool(body, 'confirm') });
|
|
549
|
+
return { ...r, file: tildify(r.file, rt.paths.home) };
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
// Opens the Account's website in one Chrome browser user (spec §3.24). It starts a program, so a read-only Panel
|
|
553
|
+
// refuses it (writes defaults to true).
|
|
554
|
+
'/api/browser/open': {
|
|
555
|
+
async run(body) {
|
|
556
|
+
const account = optString(body, 'account');
|
|
557
|
+
if (!account)
|
|
558
|
+
throw new HttpError(400, '"account" is required');
|
|
559
|
+
const rt = await getRuntime();
|
|
560
|
+
const profile = optString(body, 'profile');
|
|
561
|
+
return openInBrowser(rt, await rt.resolve(account), profile);
|
|
562
|
+
},
|
|
563
|
+
},
|
|
527
564
|
// Opens a Grok Bot window, creating it when the name is new, or brings it to the front (ADR-0019).
|
|
528
565
|
'/api/app-profile/open': {
|
|
529
566
|
async run(body) {
|
|
@@ -600,7 +637,13 @@ export function createPanelHandler(opts) {
|
|
|
600
637
|
});
|
|
601
638
|
if (!r.ok)
|
|
602
639
|
throw new UserError(r.message);
|
|
603
|
-
return {
|
|
640
|
+
return {
|
|
641
|
+
...shown,
|
|
642
|
+
updated: true,
|
|
643
|
+
message: r.message,
|
|
644
|
+
restartedPanel: r.restartedPanel,
|
|
645
|
+
refreshedApp: r.refreshedApp,
|
|
646
|
+
};
|
|
604
647
|
},
|
|
605
648
|
},
|
|
606
649
|
// The Auto Update switch: config `update.auto`, shown as a diff first like every config change.
|
|
@@ -671,7 +714,7 @@ export function createPanelHandler(opts) {
|
|
|
671
714
|
});
|
|
672
715
|
res.end(method === 'HEAD'
|
|
673
716
|
? undefined
|
|
674
|
-
: renderPage({ basePath, token, nonce, version, build, readOnly, theme, lang, updates }));
|
|
717
|
+
: renderPage({ basePath, token, nonce, version, build, local, readOnly, theme, lang, updates }));
|
|
675
718
|
return true;
|
|
676
719
|
}
|
|
677
720
|
// The mark, for a Portal's tab and for this page's favicon (ADR-0006 amendment).
|
package/dist/src/panel/i18n.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ declare const en: {
|
|
|
6
6
|
'top.refreshing': string;
|
|
7
7
|
'top.updated': string;
|
|
8
8
|
'top.updatedTitle': string;
|
|
9
|
-
'top.
|
|
9
|
+
'top.stale': string;
|
|
10
10
|
'foot.text': string;
|
|
11
11
|
'foot.textChecks': string;
|
|
12
12
|
'upd.label': string;
|
|
@@ -22,6 +22,16 @@ declare const en: {
|
|
|
22
22
|
'upd.skill': string;
|
|
23
23
|
'upd.checksOff': string;
|
|
24
24
|
'upd.latest': string;
|
|
25
|
+
'upd.latestNow': string;
|
|
26
|
+
'upd.check': string;
|
|
27
|
+
'upd.checking': string;
|
|
28
|
+
'upd.verTitle': string;
|
|
29
|
+
'upd.local': string;
|
|
30
|
+
'upd.localDirty': string;
|
|
31
|
+
'upd.open': string;
|
|
32
|
+
'upd.autoIsOn': string;
|
|
33
|
+
'upd.autoIsOff': string;
|
|
34
|
+
'upd.settings': string;
|
|
25
35
|
'upd.checked': string;
|
|
26
36
|
'upd.notYet': string;
|
|
27
37
|
'upd.auto': string;
|
|
@@ -38,9 +48,6 @@ declare const en: {
|
|
|
38
48
|
'upd.reloading': string;
|
|
39
49
|
'upd.restartHint': string;
|
|
40
50
|
'acct.title': string;
|
|
41
|
-
'theme.system': string;
|
|
42
|
-
'theme.light': string;
|
|
43
|
-
'theme.dark': string;
|
|
44
51
|
'err.noAnswer': string;
|
|
45
52
|
'err.session': string;
|
|
46
53
|
'err.expired': string;
|
|
@@ -120,6 +127,7 @@ declare const en: {
|
|
|
120
127
|
'why.health-fail': string;
|
|
121
128
|
'why.signed-out': string;
|
|
122
129
|
'why.not-installed': string;
|
|
130
|
+
'why.broken': string;
|
|
123
131
|
'why.quota-high': string;
|
|
124
132
|
'why.health-warn': string;
|
|
125
133
|
'tb.search': string;
|
|
@@ -134,6 +142,7 @@ declare const en: {
|
|
|
134
142
|
'tb.used': string;
|
|
135
143
|
'tb.left': string;
|
|
136
144
|
'tb.view': string;
|
|
145
|
+
'tb.display': string;
|
|
137
146
|
'tb.list': string;
|
|
138
147
|
'tb.cards': string;
|
|
139
148
|
'tb.hideEmails': string;
|
|
@@ -151,6 +160,7 @@ declare const en: {
|
|
|
151
160
|
'group.collapse': string;
|
|
152
161
|
'row.collapse': string;
|
|
153
162
|
'row.expand': string;
|
|
163
|
+
'row.runs': string;
|
|
154
164
|
'group.toggle': string;
|
|
155
165
|
'group.attention.one': string;
|
|
156
166
|
'group.attention.other': string;
|
|
@@ -172,6 +182,10 @@ declare const en: {
|
|
|
172
182
|
'acct.installHost': string;
|
|
173
183
|
'acct.nearLimit': string;
|
|
174
184
|
'acct.installIt': string;
|
|
185
|
+
'acct.installHostCmd': string;
|
|
186
|
+
'acct.brokenHost': string;
|
|
187
|
+
'acct.installDocs': string;
|
|
188
|
+
'acct.broken': string;
|
|
175
189
|
'quota.pctUsed': string;
|
|
176
190
|
'quota.pctLeft': string;
|
|
177
191
|
'quota.meterUsed': string;
|
|
@@ -182,6 +196,13 @@ declare const en: {
|
|
|
182
196
|
'quota.dataFrom': string;
|
|
183
197
|
'quota.asOf': string;
|
|
184
198
|
'quota.sharedFrom': string;
|
|
199
|
+
'quota.readFrom': string;
|
|
200
|
+
'login.count': string;
|
|
201
|
+
'login.label': string;
|
|
202
|
+
'login.hint': string;
|
|
203
|
+
'login.sharedAbove': string;
|
|
204
|
+
'login.collapse': string;
|
|
205
|
+
'login.expand': string;
|
|
185
206
|
'quota.noWindows': string;
|
|
186
207
|
'quota.off': string;
|
|
187
208
|
'quota.turnOnFamily': string;
|
|
@@ -276,6 +297,32 @@ declare const en: {
|
|
|
276
297
|
'desk.started': string;
|
|
277
298
|
'desk.focused': string;
|
|
278
299
|
'desk.failed': string;
|
|
300
|
+
'br.open': string;
|
|
301
|
+
'br.signedIn': string;
|
|
302
|
+
'br.signedInMore': string;
|
|
303
|
+
'br.hint': string;
|
|
304
|
+
'br.title': string;
|
|
305
|
+
'br.offIntro': string;
|
|
306
|
+
'br.reads': string;
|
|
307
|
+
'br.read1': string;
|
|
308
|
+
'br.read2': string;
|
|
309
|
+
'br.read3': string;
|
|
310
|
+
'br.never': string;
|
|
311
|
+
'br.enable': string;
|
|
312
|
+
'br.enabled': string;
|
|
313
|
+
'br.disable': string;
|
|
314
|
+
'br.disabled': string;
|
|
315
|
+
'br.local': string;
|
|
316
|
+
'br.found': string;
|
|
317
|
+
'br.none': string;
|
|
318
|
+
'br.noMatch': string;
|
|
319
|
+
'br.others': string;
|
|
320
|
+
'br.all': string;
|
|
321
|
+
'br.noChrome': string;
|
|
322
|
+
'br.openIt': string;
|
|
323
|
+
'br.opened': string;
|
|
324
|
+
'br.failed': string;
|
|
325
|
+
'br.changed': string;
|
|
279
326
|
'app.apart': string;
|
|
280
327
|
'app.apartTip': string;
|
|
281
328
|
'app.mainName': string;
|
|
@@ -431,6 +478,7 @@ declare const en: {
|
|
|
431
478
|
'nf.accounts.other': string;
|
|
432
479
|
'nf.runOnce': string;
|
|
433
480
|
'nf.notInstalled': string;
|
|
481
|
+
'nf.broken': string;
|
|
434
482
|
'nf.name': string;
|
|
435
483
|
'nf.namePh': string;
|
|
436
484
|
'nf.api': string;
|
|
@@ -531,6 +579,29 @@ declare const en: {
|
|
|
531
579
|
'rc.confirm': string;
|
|
532
580
|
'rc.done': string;
|
|
533
581
|
'rc.failed': string;
|
|
582
|
+
'set.button': string;
|
|
583
|
+
'set.lang': string;
|
|
584
|
+
'set.theme': string;
|
|
585
|
+
'set.theme.system': string;
|
|
586
|
+
'set.theme.light': string;
|
|
587
|
+
'set.theme.dark': string;
|
|
588
|
+
'set.rcOn': string;
|
|
589
|
+
'rc.button': string;
|
|
590
|
+
'rc.state.on': string;
|
|
591
|
+
'rc.state.default': string;
|
|
592
|
+
'rc.state.off': string;
|
|
593
|
+
'rc.state.missing': string;
|
|
594
|
+
'rc.state.blocked': string;
|
|
595
|
+
'rc.dialogTitle': string;
|
|
596
|
+
'rc.explain': string;
|
|
597
|
+
'rc.allOff': string;
|
|
598
|
+
'rc.undo': string;
|
|
599
|
+
'rc.switch': string;
|
|
600
|
+
'rc.confirmOn': string;
|
|
601
|
+
'rc.onWarn': string;
|
|
602
|
+
'rc.doneOn': string;
|
|
603
|
+
'rc.failedOn': string;
|
|
604
|
+
'rc.readOnlyBoth': string;
|
|
534
605
|
'rc.undone': string;
|
|
535
606
|
'banner.readOnly': string;
|
|
536
607
|
'banner.plugins.one': string;
|