sideby 0.12.1 → 0.13.1
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 +5 -2
- package/dist/src/cli/commands.js +165 -20
- package/dist/src/cli/json-schemas.js +100 -2
- package/dist/src/cli/main.js +72 -4
- 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/skill-check.d.ts +13 -0
- package/dist/src/core/skill-check.js +29 -1
- package/dist/src/core/update.d.ts +55 -3
- package/dist/src/core/update.js +124 -4
- 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 +9 -1
- package/dist/src/panel/handler.js +54 -6
- package/dist/src/panel/i18n.d.ts +74 -4
- package/dist/src/panel/i18n.js +160 -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 +587 -183
- package/dist/src/panel/page-styles.js +61 -18
- 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/alias.json +62 -0
- package/schemas/app-install.json +62 -0
- package/schemas/app-uninstall.json +62 -0
- package/schemas/browser-open.json +109 -0
- package/schemas/browser-switch.json +104 -0
- package/schemas/browser.json +161 -0
- package/schemas/config.json +11 -0
- package/schemas/desktop-remove.json +62 -0
- package/schemas/desktop.json +62 -0
- package/schemas/doctor.json +62 -0
- package/schemas/error.json +62 -0
- package/schemas/families.json +68 -0
- package/schemas/handoff-disable.json +62 -0
- package/schemas/handoff-enable.json +62 -0
- package/schemas/handoff-ready.json +62 -0
- package/schemas/handoff-setup.json +62 -0
- package/schemas/handoff-status.json +62 -0
- package/schemas/handoff-teardown.json +62 -0
- package/schemas/list.json +68 -0
- package/schemas/model.json +62 -0
- package/schemas/new.json +62 -0
- package/schemas/next.json +62 -0
- package/schemas/plugins.json +62 -0
- package/schemas/quota-setup.json +62 -0
- package/schemas/quota-teardown.json +62 -0
- package/schemas/quota.json +62 -0
- package/schemas/remote-control.json +66 -0
- package/schemas/tune-change.json +62 -0
- package/schemas/tune.json +62 -0
- package/schemas/update.json +111 -1
- package/skills/sideby/SKILL.md +8 -4
- package/skills/sideby/references/diagnose.md +1 -1
- package/skills/sideby/references/setup.md +1 -1
- package/skills/sideby/references/update.md +16 -0
|
@@ -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';
|
|
@@ -91,8 +92,10 @@ export interface PanelUpdate {
|
|
|
91
92
|
command?: string;
|
|
92
93
|
canRun: boolean;
|
|
93
94
|
releaseNotes?: string;
|
|
94
|
-
/** The command that
|
|
95
|
+
/** The command that installs the sideby skill by hand. */
|
|
95
96
|
skill: string;
|
|
97
|
+
/** The installed sideby skill, which an Update brings along (spec §3.23); absent when none is installed. */
|
|
98
|
+
skillUpdate?: import('../core/update.ts').SkillUpdatePlan;
|
|
96
99
|
}
|
|
97
100
|
export interface PanelState {
|
|
98
101
|
schemaVersion: 1;
|
|
@@ -132,6 +135,11 @@ export interface PanelState {
|
|
|
132
135
|
appProfiles?: AppProfileStatus[];
|
|
133
136
|
/** Update Check result and Auto Update setting (spec §3.23); absent with `updates: false`. */
|
|
134
137
|
update?: PanelUpdate;
|
|
138
|
+
/**
|
|
139
|
+
* Browser Sign-ins (spec §3.24): which Chrome browser user signed in to which Account, read on this computer only.
|
|
140
|
+
* Absent without Claude, Codex or Grok subscription Accounts. Nothing of the browser is read while `enabled` is false.
|
|
141
|
+
*/
|
|
142
|
+
browser?: BrowserReport;
|
|
135
143
|
}
|
|
136
144
|
/** The `handoff` map of the Panel state, from the statuses and Quota already read for the page. */
|
|
137
145
|
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';
|
|
@@ -60,6 +61,9 @@ async function updateState(rt, readOnly) {
|
|
|
60
61
|
});
|
|
61
62
|
const method = await update.installMethod(rt.env);
|
|
62
63
|
const plan = cache.latest ? update.shownPlan(update.planFrom(method, current, cache.latest, rt.env)) : null;
|
|
64
|
+
const skillUpdate = await update
|
|
65
|
+
.skillUpdatePlan(rt.paths.home, plan?.available ? plan.latest : current)
|
|
66
|
+
.catch(() => null);
|
|
63
67
|
return {
|
|
64
68
|
current,
|
|
65
69
|
...(cache.latest ? { latest: cache.latest } : {}),
|
|
@@ -71,6 +75,7 @@ async function updateState(rt, readOnly) {
|
|
|
71
75
|
...(plan ? { command: plan.command, releaseNotes: plan.releaseNotes } : {}),
|
|
72
76
|
canRun: Boolean(plan?.canRun),
|
|
73
77
|
skill: SKILL_INSTALL,
|
|
78
|
+
...(skillUpdate ? { skillUpdate } : {}),
|
|
74
79
|
};
|
|
75
80
|
}
|
|
76
81
|
/** The `handoff` map of the Panel state, from the statuses and Quota already read for the page. */
|
|
@@ -265,6 +270,8 @@ async function buildState(rt, opts) {
|
|
|
265
270
|
};
|
|
266
271
|
});
|
|
267
272
|
const handoff = handoffByFamily(rt, list, ctx.now);
|
|
273
|
+
const browser = await browserReport(rt, accounts).catch(() => undefined);
|
|
274
|
+
const showBrowser = browser && (browser.families.matched.length || browser.families.openOnly.length);
|
|
268
275
|
return {
|
|
269
276
|
schemaVersion: 1,
|
|
270
277
|
version: opts.version,
|
|
@@ -273,7 +280,8 @@ async function buildState(rt, opts) {
|
|
|
273
280
|
generatedAt: ctx.now.toISOString(),
|
|
274
281
|
families: families.map((f) => {
|
|
275
282
|
const logo = rt.families.get(f.id)?.logo;
|
|
276
|
-
|
|
283
|
+
const g = f.brokenBin ? { ...f, brokenBin: tildify(f.brokenBin, ctx.home) } : f;
|
|
284
|
+
return logo ? { ...g, logo } : g;
|
|
277
285
|
}),
|
|
278
286
|
accounts: list,
|
|
279
287
|
pluginErrors: rt.pluginErrors.map((e) => ({ where: e.where, message: e.message })),
|
|
@@ -294,6 +302,7 @@ async function buildState(rt, opts) {
|
|
|
294
302
|
.map((f) => f.id),
|
|
295
303
|
},
|
|
296
304
|
...(upd ? { update: upd } : {}),
|
|
305
|
+
...(showBrowser ? { browser } : {}),
|
|
297
306
|
};
|
|
298
307
|
}
|
|
299
308
|
/** The `change` of a /api/handoff/configure body, with every field checked; unknown fields are refused. */
|
|
@@ -351,6 +360,7 @@ export function createPanelHandler(opts) {
|
|
|
351
360
|
const readOnly = Boolean(opts.readOnly);
|
|
352
361
|
const version = opts.version ?? PANEL_VERSION;
|
|
353
362
|
const build = buildId();
|
|
363
|
+
const local = localBuild();
|
|
354
364
|
const theme = resolveTheme(opts.theme);
|
|
355
365
|
const lang = resolveLang(opts.lang);
|
|
356
366
|
const updates = opts.updates !== false;
|
|
@@ -450,8 +460,8 @@ export function createPanelHandler(opts) {
|
|
|
450
460
|
return { ref: found.ref, undone: r.ok, message: r.message };
|
|
451
461
|
},
|
|
452
462
|
},
|
|
453
|
-
// Remote Control
|
|
454
|
-
// settings file that
|
|
463
|
+
// Remote Control at startup (spec §3.22, ADR-0017): writes `remoteControlAtStartup` (false for off, true for on)
|
|
464
|
+
// into each Claude settings file that does not hold it yet; the page shows the files first, from the state.
|
|
455
465
|
'/api/remote-control/off': {
|
|
456
466
|
async run() {
|
|
457
467
|
const rt = await getRuntime();
|
|
@@ -460,6 +470,14 @@ export function createPanelHandler(opts) {
|
|
|
460
470
|
return { ...r, results: r.results.map((x) => ({ ...x, file: tildify(x.file, home) })) };
|
|
461
471
|
},
|
|
462
472
|
},
|
|
473
|
+
'/api/remote-control/on': {
|
|
474
|
+
async run() {
|
|
475
|
+
const rt = await getRuntime();
|
|
476
|
+
const r = await rt.remoteControlOn();
|
|
477
|
+
const home = rt.paths.home;
|
|
478
|
+
return { ...r, results: r.results.map((x) => ({ ...x, file: tildify(x.file, home) })) };
|
|
479
|
+
},
|
|
480
|
+
},
|
|
463
481
|
'/api/remote-control/undo': {
|
|
464
482
|
async run() {
|
|
465
483
|
const rt = await getRuntime();
|
|
@@ -524,6 +542,29 @@ export function createPanelHandler(opts) {
|
|
|
524
542
|
};
|
|
525
543
|
},
|
|
526
544
|
},
|
|
545
|
+
// Browser Sign-ins on or off (spec §3.24): config `browser.signIns`, shown first like every config change.
|
|
546
|
+
'/api/browser/settings': {
|
|
547
|
+
writes: (body) => body.confirm === true,
|
|
548
|
+
async run(body) {
|
|
549
|
+
if (typeof body.enabled !== 'boolean')
|
|
550
|
+
throw new HttpError(400, '"enabled" must be true or false');
|
|
551
|
+
const rt = await getRuntime();
|
|
552
|
+
const r = await setBrowserSignIns(rt, body.enabled, { apply: optBool(body, 'confirm') });
|
|
553
|
+
return { ...r, file: tildify(r.file, rt.paths.home) };
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
// Opens the Account's website in one Chrome browser user (spec §3.24). It starts a program, so a read-only Panel
|
|
557
|
+
// refuses it (writes defaults to true).
|
|
558
|
+
'/api/browser/open': {
|
|
559
|
+
async run(body) {
|
|
560
|
+
const account = optString(body, 'account');
|
|
561
|
+
if (!account)
|
|
562
|
+
throw new HttpError(400, '"account" is required');
|
|
563
|
+
const rt = await getRuntime();
|
|
564
|
+
const profile = optString(body, 'profile');
|
|
565
|
+
return openInBrowser(rt, await rt.resolve(account), profile);
|
|
566
|
+
},
|
|
567
|
+
},
|
|
527
568
|
// Opens a Grok Bot window, creating it when the name is new, or brings it to the front (ADR-0019).
|
|
528
569
|
'/api/app-profile/open': {
|
|
529
570
|
async run(body) {
|
|
@@ -600,7 +641,14 @@ export function createPanelHandler(opts) {
|
|
|
600
641
|
});
|
|
601
642
|
if (!r.ok)
|
|
602
643
|
throw new UserError(r.message);
|
|
603
|
-
return {
|
|
644
|
+
return {
|
|
645
|
+
...shown,
|
|
646
|
+
updated: true,
|
|
647
|
+
message: r.message,
|
|
648
|
+
restartedPanel: r.restartedPanel,
|
|
649
|
+
refreshedApp: r.refreshedApp,
|
|
650
|
+
...(r.skillUpdated === undefined ? {} : { skillUpdated: r.skillUpdated }),
|
|
651
|
+
};
|
|
604
652
|
},
|
|
605
653
|
},
|
|
606
654
|
// The Auto Update switch: config `update.auto`, shown as a diff first like every config change.
|
|
@@ -671,7 +719,7 @@ export function createPanelHandler(opts) {
|
|
|
671
719
|
});
|
|
672
720
|
res.end(method === 'HEAD'
|
|
673
721
|
? undefined
|
|
674
|
-
: renderPage({ basePath, token, nonce, version, build, readOnly, theme, lang, updates }));
|
|
722
|
+
: renderPage({ basePath, token, nonce, version, build, local, readOnly, theme, lang, updates }));
|
|
675
723
|
return true;
|
|
676
724
|
}
|
|
677
725
|
// 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;
|
|
@@ -20,12 +20,21 @@ declare const en: {
|
|
|
20
20
|
'upd.after': string;
|
|
21
21
|
'upd.cannot': string;
|
|
22
22
|
'upd.skill': string;
|
|
23
|
+
'upd.skillToo': string;
|
|
24
|
+
'upd.skillDone': string;
|
|
25
|
+
'upd.skillManaged': string;
|
|
23
26
|
'upd.checksOff': string;
|
|
24
27
|
'upd.latest': string;
|
|
25
28
|
'upd.latestNow': string;
|
|
26
29
|
'upd.check': string;
|
|
27
30
|
'upd.checking': string;
|
|
28
31
|
'upd.verTitle': string;
|
|
32
|
+
'upd.local': string;
|
|
33
|
+
'upd.localDirty': string;
|
|
34
|
+
'upd.open': string;
|
|
35
|
+
'upd.autoIsOn': string;
|
|
36
|
+
'upd.autoIsOff': string;
|
|
37
|
+
'upd.settings': string;
|
|
29
38
|
'upd.checked': string;
|
|
30
39
|
'upd.notYet': string;
|
|
31
40
|
'upd.auto': string;
|
|
@@ -42,9 +51,6 @@ declare const en: {
|
|
|
42
51
|
'upd.reloading': string;
|
|
43
52
|
'upd.restartHint': string;
|
|
44
53
|
'acct.title': string;
|
|
45
|
-
'theme.system': string;
|
|
46
|
-
'theme.light': string;
|
|
47
|
-
'theme.dark': string;
|
|
48
54
|
'err.noAnswer': string;
|
|
49
55
|
'err.session': string;
|
|
50
56
|
'err.expired': string;
|
|
@@ -124,6 +130,7 @@ declare const en: {
|
|
|
124
130
|
'why.health-fail': string;
|
|
125
131
|
'why.signed-out': string;
|
|
126
132
|
'why.not-installed': string;
|
|
133
|
+
'why.broken': string;
|
|
127
134
|
'why.quota-high': string;
|
|
128
135
|
'why.health-warn': string;
|
|
129
136
|
'tb.search': string;
|
|
@@ -138,6 +145,7 @@ declare const en: {
|
|
|
138
145
|
'tb.used': string;
|
|
139
146
|
'tb.left': string;
|
|
140
147
|
'tb.view': string;
|
|
148
|
+
'tb.display': string;
|
|
141
149
|
'tb.list': string;
|
|
142
150
|
'tb.cards': string;
|
|
143
151
|
'tb.hideEmails': string;
|
|
@@ -155,6 +163,7 @@ declare const en: {
|
|
|
155
163
|
'group.collapse': string;
|
|
156
164
|
'row.collapse': string;
|
|
157
165
|
'row.expand': string;
|
|
166
|
+
'row.runs': string;
|
|
158
167
|
'group.toggle': string;
|
|
159
168
|
'group.attention.one': string;
|
|
160
169
|
'group.attention.other': string;
|
|
@@ -176,6 +185,10 @@ declare const en: {
|
|
|
176
185
|
'acct.installHost': string;
|
|
177
186
|
'acct.nearLimit': string;
|
|
178
187
|
'acct.installIt': string;
|
|
188
|
+
'acct.installHostCmd': string;
|
|
189
|
+
'acct.brokenHost': string;
|
|
190
|
+
'acct.installDocs': string;
|
|
191
|
+
'acct.broken': string;
|
|
179
192
|
'quota.pctUsed': string;
|
|
180
193
|
'quota.pctLeft': string;
|
|
181
194
|
'quota.meterUsed': string;
|
|
@@ -186,6 +199,13 @@ declare const en: {
|
|
|
186
199
|
'quota.dataFrom': string;
|
|
187
200
|
'quota.asOf': string;
|
|
188
201
|
'quota.sharedFrom': string;
|
|
202
|
+
'quota.readFrom': string;
|
|
203
|
+
'login.count': string;
|
|
204
|
+
'login.label': string;
|
|
205
|
+
'login.hint': string;
|
|
206
|
+
'login.sharedAbove': string;
|
|
207
|
+
'login.collapse': string;
|
|
208
|
+
'login.expand': string;
|
|
189
209
|
'quota.noWindows': string;
|
|
190
210
|
'quota.off': string;
|
|
191
211
|
'quota.turnOnFamily': string;
|
|
@@ -280,6 +300,32 @@ declare const en: {
|
|
|
280
300
|
'desk.started': string;
|
|
281
301
|
'desk.focused': string;
|
|
282
302
|
'desk.failed': string;
|
|
303
|
+
'br.open': string;
|
|
304
|
+
'br.signedIn': string;
|
|
305
|
+
'br.signedInMore': string;
|
|
306
|
+
'br.hint': string;
|
|
307
|
+
'br.title': string;
|
|
308
|
+
'br.offIntro': string;
|
|
309
|
+
'br.reads': string;
|
|
310
|
+
'br.read1': string;
|
|
311
|
+
'br.read2': string;
|
|
312
|
+
'br.read3': string;
|
|
313
|
+
'br.never': string;
|
|
314
|
+
'br.enable': string;
|
|
315
|
+
'br.enabled': string;
|
|
316
|
+
'br.disable': string;
|
|
317
|
+
'br.disabled': string;
|
|
318
|
+
'br.local': string;
|
|
319
|
+
'br.found': string;
|
|
320
|
+
'br.none': string;
|
|
321
|
+
'br.noMatch': string;
|
|
322
|
+
'br.others': string;
|
|
323
|
+
'br.all': string;
|
|
324
|
+
'br.noChrome': string;
|
|
325
|
+
'br.openIt': string;
|
|
326
|
+
'br.opened': string;
|
|
327
|
+
'br.failed': string;
|
|
328
|
+
'br.changed': string;
|
|
283
329
|
'app.apart': string;
|
|
284
330
|
'app.apartTip': string;
|
|
285
331
|
'app.mainName': string;
|
|
@@ -435,6 +481,7 @@ declare const en: {
|
|
|
435
481
|
'nf.accounts.other': string;
|
|
436
482
|
'nf.runOnce': string;
|
|
437
483
|
'nf.notInstalled': string;
|
|
484
|
+
'nf.broken': string;
|
|
438
485
|
'nf.name': string;
|
|
439
486
|
'nf.namePh': string;
|
|
440
487
|
'nf.api': string;
|
|
@@ -535,6 +582,29 @@ declare const en: {
|
|
|
535
582
|
'rc.confirm': string;
|
|
536
583
|
'rc.done': string;
|
|
537
584
|
'rc.failed': string;
|
|
585
|
+
'set.button': string;
|
|
586
|
+
'set.lang': string;
|
|
587
|
+
'set.theme': string;
|
|
588
|
+
'set.theme.system': string;
|
|
589
|
+
'set.theme.light': string;
|
|
590
|
+
'set.theme.dark': string;
|
|
591
|
+
'set.rcOn': string;
|
|
592
|
+
'rc.button': string;
|
|
593
|
+
'rc.state.on': string;
|
|
594
|
+
'rc.state.default': string;
|
|
595
|
+
'rc.state.off': string;
|
|
596
|
+
'rc.state.missing': string;
|
|
597
|
+
'rc.state.blocked': string;
|
|
598
|
+
'rc.dialogTitle': string;
|
|
599
|
+
'rc.explain': string;
|
|
600
|
+
'rc.allOff': string;
|
|
601
|
+
'rc.undo': string;
|
|
602
|
+
'rc.switch': string;
|
|
603
|
+
'rc.confirmOn': string;
|
|
604
|
+
'rc.onWarn': string;
|
|
605
|
+
'rc.doneOn': string;
|
|
606
|
+
'rc.failedOn': string;
|
|
607
|
+
'rc.readOnlyBoth': string;
|
|
538
608
|
'rc.undone': string;
|
|
539
609
|
'banner.readOnly': string;
|
|
540
610
|
'banner.plugins.one': string;
|