vcorp-chrome 1.8.1 → 2.0.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 +20 -1
- package/docs//345/215/217/350/256/256.md +53 -0
- package/docs//345/257/271/346/257/224.md +55 -53
- package/docs//350/203/275/345/212/233/345/214/205/344/270/216eda-/344/272/244/346/216/245.md +288 -0
- package/extension/background.js +20 -2
- package/extension/cloud-policy.js +14 -0
- package/extension/cloud.js +229 -6
- package/extension/desktop-policy.js +400 -0
- package/extension/desktop-render.js +81 -0
- package/extension/device.js +144 -0
- package/extension/manifest.json +4 -1
- package/extension/panel.html +10 -0
- package/extension/panel.js +476 -427
- package/native-host/host.js +122 -0
- package/package.json +6 -2
- package/src/bridge.js +31 -2
- package/src/cli.js +35 -1
- package/src/lib/desktop.js +171 -0
- package/src/lib/native-install.js +139 -0
- package/src/lib/paths.js +20 -0
- package/src/lib/rpc.js +7 -2
- package/src/mcp-server.js +135 -10
package/extension/cloud.js
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
import * as policy from './cloud-policy.js';
|
|
20
20
|
import * as folders from './folders.js';
|
|
21
|
+
import { checkDesktop, checkObserved, recordOf, filterWindows, appsToAsk, APPS_MAX } from './desktop-policy.js';
|
|
22
|
+
import { renderEnvelope } from './desktop-render.js';
|
|
23
|
+
import { routeHere, stampFrom, capOf, cleanDevice, describeDevices } from './device.js';
|
|
21
24
|
|
|
22
25
|
const CFG = 'cloud'; // storage.local:配对信息和用户的选择
|
|
23
26
|
const TASKS = 'cloudTasks'; // storage.local:任务(批准状态、自己开的标签页)
|
|
@@ -240,10 +243,14 @@ export async function connect() {
|
|
|
240
243
|
try {
|
|
241
244
|
const sig = await sign(helloText(cfg.origin, cfg.deviceId, String(m.nonce || '')));
|
|
242
245
|
const mounted = await folders.names().catch(() => []);
|
|
246
|
+
// 名下的设备(目前只有同机的桌面)。签名只盖浏览器自己:桌面经这条已认证的连接中继,
|
|
247
|
+
// 服务器能见到的桌面只有这个浏览器报上来的
|
|
248
|
+
const child = await desktopChild();
|
|
243
249
|
sock.send(JSON.stringify({
|
|
244
250
|
type: 'hello', v: 1, deviceId: cfg.deviceId, sig,
|
|
245
251
|
version: chrome.runtime.getManifest().version, sites: policy.cleanAllowList(cfg.sites), auto: !!cfg.auto, autoConfirm: !!cfg.autoConfirm,
|
|
246
252
|
folders: mounted,
|
|
253
|
+
devices: child ? [{ ...child, parent: cfg.deviceId }] : [],
|
|
247
254
|
}));
|
|
248
255
|
} catch { try { sock.close(); } catch { /* 已经废了 */ } }
|
|
249
256
|
return;
|
|
@@ -348,7 +355,7 @@ async function taskFor(job, cfg, reason = '', { allowEmpty = false } = {}) {
|
|
|
348
355
|
await log({ ev: 'approved', job: jobId, msg: '自动放行' });
|
|
349
356
|
await pushLive();
|
|
350
357
|
}
|
|
351
|
-
if (t.state === 'nosites') throw err('NO_SITES', '先用 sites 申请要访问的网站(如 sites {"add":["zhipin.com"],"reason":"找兼职"}),或用 folders
|
|
358
|
+
if (t.state === 'nosites') throw err('NO_SITES', '先用 sites 申请要访问的网站(如 sites {"add":["zhipin.com"],"reason":"找兼职"}),或用 folders 申请本地文件夹、apps 申请桌面应用。用户批准后才能继续。');
|
|
352
359
|
if (t.state === 'denied') throw err('CLOUD_DENIED', '用户拒绝了这个任务。不要重试。');
|
|
353
360
|
if (t.state === 'approved') return t;
|
|
354
361
|
|
|
@@ -653,12 +660,212 @@ const updateTask = (jobId, fn) => serial(async () => {
|
|
|
653
660
|
return tasks[jobId];
|
|
654
661
|
});
|
|
655
662
|
|
|
663
|
+
// ---------- 桌面 ----------
|
|
664
|
+
//
|
|
665
|
+
// 同机的桌面是这个浏览器名下的子设备:命令经原生消息宿主 ai.vcorp.desktop 交给 agent-desktop。
|
|
666
|
+
// 规矩全在 desktop-policy.js(云端那一列),宿主那边再审一遍。快照户口记在任务上,任务结束一起删。
|
|
667
|
+
|
|
668
|
+
const HOST = 'ai.vcorp.desktop';
|
|
669
|
+
const DESK = 'cloudDesktop'; // storage.local:启用桌面能力时宿主报的设备
|
|
670
|
+
const DESK_PENDING = 'cloudDesktopPending'; // 为补上 nativeMessaging 绑定而重载,重载后接着启用
|
|
671
|
+
const DESK_SNAPS = 4; // 每个任务记几份快照的户口。户口按 ref 存名字,大应用一份上百 KB
|
|
672
|
+
|
|
673
|
+
const deskInfo = async () => (await chrome.storage.local.get(DESK))[DESK] || null;
|
|
674
|
+
|
|
675
|
+
function hostCall(msg) {
|
|
676
|
+
if (!chrome.runtime.sendNativeMessage) {
|
|
677
|
+
return Promise.reject(err('NO_DEVICE', '这个浏览器没有启用桌面能力。请用户在扩展侧栏「桌面应用」里点「启用桌面能力」。'));
|
|
678
|
+
}
|
|
679
|
+
return new Promise((resolve, reject) => {
|
|
680
|
+
chrome.runtime.sendNativeMessage(HOST, msg, (r) => {
|
|
681
|
+
const e = chrome.runtime.lastError;
|
|
682
|
+
if (e) reject(err('DEVICE_OFFLINE', `桌面宿主没有应答(${e.message})。请用户在终端运行 npx vcorp-chrome desktop install。`));
|
|
683
|
+
else if (!r || typeof r !== 'object') reject(err('DEVICE_OFFLINE', '桌面宿主的回包不对'));
|
|
684
|
+
else resolve(r);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// 名下的桌面设备:启用过、权限还在才算
|
|
690
|
+
async function desktopChild() {
|
|
691
|
+
const info = await deskInfo();
|
|
692
|
+
if (!info?.device || !chrome.runtime.sendNativeMessage) return null;
|
|
693
|
+
return info.device;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async function enableDesktop() {
|
|
697
|
+
// 运行时才授予的 nativeMessaging,Chrome 不给已经在跑的 service worker 补上绑定,重载后才有
|
|
698
|
+
if (!chrome.runtime.sendNativeMessage) {
|
|
699
|
+
if (!(await chrome.permissions.contains({ permissions: ['nativeMessaging'] }))) {
|
|
700
|
+
throw err('NO_DEVICE', '没有 nativeMessaging 权限,桌面能力保持关闭');
|
|
701
|
+
}
|
|
702
|
+
await chrome.storage.local.set({ [DESK_PENDING]: Date.now() });
|
|
703
|
+
await log({ ev: 'desktop_reload' });
|
|
704
|
+
chrome.runtime.reload();
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const r = await hostCall({ op: 'hello' });
|
|
708
|
+
const device = cleanDevice(r.device);
|
|
709
|
+
if (!device || device.kind !== 'desktop') throw err('DEVICE_OFFLINE', '桌面宿主报上来的设备不对');
|
|
710
|
+
await chrome.storage.local.set({ [DESK]: { device, ready: !!r.ready, at: Date.now() } });
|
|
711
|
+
await log({ ev: 'desktop_on', msg: device.id });
|
|
712
|
+
// 重连一次,服务器在 hello 里看到这台设备
|
|
713
|
+
disconnect(); retryDelay = 0; void connect();
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
async function disableDesktop() {
|
|
717
|
+
await chrome.storage.local.remove(DESK);
|
|
718
|
+
await log({ ev: 'desktop_off' });
|
|
719
|
+
disconnect(); retryDelay = 0; void connect();
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
async function devicesCmd(cfg) {
|
|
723
|
+
const child = await desktopChild();
|
|
724
|
+
const list = [
|
|
725
|
+
{ id: cfg.deviceId, kind: 'browser', name: deviceName(), online: true },
|
|
726
|
+
...(child ? [{ ...child, parent: cfg.deviceId }] : []),
|
|
727
|
+
].map(cleanDevice).filter(Boolean);
|
|
728
|
+
return { text: describeDevices(list), devices: list };
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function notifyApps(t, cfg) {
|
|
732
|
+
chrome.notifications?.create(`hc-cloud-apps:${t.jobId}`, {
|
|
733
|
+
type: 'basic', iconUrl: 'icons/icon128.png', priority: 2, requireInteraction: true,
|
|
734
|
+
title: `${policy.hostOf(cfg.origin)} 的任务想操作你电脑上的应用`,
|
|
735
|
+
message: `${t.title ? `「${t.title}」` : '任务'} → ${(t.appAsk || []).join('、')}${t.appAskWhy ? `\n用途:${t.appAskWhy}` : ''}`,
|
|
736
|
+
buttons: [{ title: '允许' }, { title: '拒绝' }],
|
|
737
|
+
}, () => void chrome.runtime.lastError);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// 和文件夹一样:自动允许管不到应用,每个任务逐个点头;批准只记在这个任务上
|
|
741
|
+
async function requestApps(task, p, cfg) {
|
|
742
|
+
if (!(await desktopChild())) throw err('NO_DEVICE', '这个浏览器没有启用桌面能力。请用户在扩展侧栏「桌面应用」里点「启用桌面能力」。');
|
|
743
|
+
const split = appsToAsk(p.add, { apps: task.apps || [], refused: task.appRefused || [] });
|
|
744
|
+
if (split.ask.length) {
|
|
745
|
+
const t = await updateTask(task.jobId, (x) => {
|
|
746
|
+
x.appAsk = split.ask;
|
|
747
|
+
x.appAskWhy = p.reason;
|
|
748
|
+
});
|
|
749
|
+
if (!t) throw err('CLOUD_DENIED', '这个任务已经结束');
|
|
750
|
+
notifyApps(t, cfg);
|
|
751
|
+
const allow = await waitFor(`apps:${task.jobId}`);
|
|
752
|
+
await updateTask(task.jobId, (x) => {
|
|
753
|
+
if (!Array.isArray(x.apps)) x.apps = [];
|
|
754
|
+
if (!Array.isArray(x.appRefused)) x.appRefused = [];
|
|
755
|
+
if (allow) for (const a of split.ask) if (!x.apps.includes(a) && x.apps.length < APPS_MAX) x.apps.push(a);
|
|
756
|
+
else if (allow === false) for (const a of split.ask) if (!x.appRefused.includes(a)) x.appRefused.push(a);
|
|
757
|
+
x.appAsk = [];
|
|
758
|
+
x.appAskWhy = '';
|
|
759
|
+
});
|
|
760
|
+
chrome.notifications?.clear(`hc-cloud-apps:${task.jobId}`, () => void chrome.runtime.lastError);
|
|
761
|
+
await log({ ev: allow ? 'apps_on' : allow === false ? 'apps_off' : 'apps_timeout', job: task.jobId, msg: split.ask.join(',') });
|
|
762
|
+
}
|
|
763
|
+
const now = (await getTasks())[task.jobId] || task;
|
|
764
|
+
const granted = now.apps || [];
|
|
765
|
+
const refused = now.appRefused || [];
|
|
766
|
+
const lines = [`本任务可以操作:${granted.join('、') || '(无)'}`];
|
|
767
|
+
const no = p.add.filter((a) => refused.includes(a));
|
|
768
|
+
const later = split.ask.filter((a) => !granted.includes(a) && !refused.includes(a));
|
|
769
|
+
if (no.length) lines.push(`用户不同意:${no.join('、')}。不要再申请。`);
|
|
770
|
+
if (later.length) lines.push(`用户 5 分钟内没有回应:${later.join('、')}。可以稍后再申请一次。`);
|
|
771
|
+
if (p.overflow.length) lines.push(`超出一个任务最多 ${APPS_MAX} 个应用的上限,这次没有询问:${p.overflow.join('、')}`);
|
|
772
|
+
if (p.bad.length) lines.push(`不能申请(浏览器、密码管理器、终端这类一律不碰,或名字不合法):${p.bad.join('、')}`);
|
|
773
|
+
return { text: lines.join('\n'), apps: granted, audit: `apps ${p.add.join(',') || '(list)'}` };
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function decideApps(jobId, allow) {
|
|
777
|
+
chrome.notifications?.clear(`hc-cloud-apps:${jobId}`, () => void chrome.runtime.lastError);
|
|
778
|
+
for (const fn of waiters.get(`apps:${jobId}`) || []) fn(!!allow);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// 发送、删除一类的桌面动作:和页面上的对外动作同一套免确认开关;否则弹通知等用户点头。
|
|
782
|
+
// 桌面没有标签页可以挂浮条,所以走通知和侧栏卡片。
|
|
783
|
+
async function confirmDesk(chk, task, cfg) {
|
|
784
|
+
const jobId = task.jobId;
|
|
785
|
+
const what = `${chk.cmd}「${String(chk.target?.name || '').slice(0, 40)}」(${chk.app})`;
|
|
786
|
+
if (cfg.autoConfirm || task.confirmAll) {
|
|
787
|
+
await log({ ev: 'ask_auto', job: jobId, msg: what });
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
await updateTask(jobId, (x) => { x.deskAsk = what; });
|
|
791
|
+
chrome.notifications?.create(`hc-cloud-desk:${jobId}`, {
|
|
792
|
+
type: 'basic', iconUrl: 'icons/icon128.png', priority: 2, requireInteraction: true,
|
|
793
|
+
title: `${policy.hostOf(cfg.origin)} 的任务要在你的电脑上做一件对外的事`,
|
|
794
|
+
message: what,
|
|
795
|
+
buttons: [{ title: '同意' }, { title: '不同意' }],
|
|
796
|
+
}, () => void chrome.runtime.lastError);
|
|
797
|
+
const allow = await waitFor(`desk:${jobId}`);
|
|
798
|
+
await updateTask(jobId, (x) => { x.deskAsk = ''; });
|
|
799
|
+
chrome.notifications?.clear(`hc-cloud-desk:${jobId}`, () => void chrome.runtime.lastError);
|
|
800
|
+
await log({ ev: allow ? 'desk_confirm' : 'desk_declined', job: jobId, msg: what });
|
|
801
|
+
if (!allow) {
|
|
802
|
+
throw err('CONFIRM_DECLINED', allow === null
|
|
803
|
+
? `用户 5 分钟内没有确认 ${what},没有执行。在报告里写明,不要重试。`
|
|
804
|
+
: `用户没有同意 ${what},没有执行。这是明确的「别做」,不要换个方式重试。`);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function decideDesk(jobId, allow) {
|
|
809
|
+
chrome.notifications?.clear(`hc-cloud-desk:${jobId}`, () => void chrome.runtime.lastError);
|
|
810
|
+
for (const fn of waiters.get(`desk:${jobId}`) || []) fn(!!allow);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const windowsMap = (list) => Object.fromEntries((Array.isArray(list) ? list : []).map((w) => [w.id, { app: w.app_name, title: w.title || '' }]));
|
|
814
|
+
|
|
815
|
+
async function desktopCmd(p, task, cfg) {
|
|
816
|
+
if (!(await desktopChild())) throw err('NO_DEVICE', '这个浏览器没有启用桌面能力。请用户在扩展侧栏「桌面应用」里点「启用桌面能力」。');
|
|
817
|
+
if (p.steps !== undefined) throw err('DESKTOP_DENIED', '云端的桌面命令一条一条发,不支持 steps');
|
|
818
|
+
const session = `cloud-${task.jobId}`;
|
|
819
|
+
const desk = { snaps: { ...(task.desk?.snaps || {}) }, windows: { ...(task.desk?.windows || {}) } };
|
|
820
|
+
if (p.window !== undefined && !desk.windows[String(p.window)]) {
|
|
821
|
+
const env = await hostCall({ op: 'run', argv: ['list-windows', `--session=${session}`] });
|
|
822
|
+
if (env.ok) desk.windows = windowsMap(env.data);
|
|
823
|
+
}
|
|
824
|
+
const chk = checkDesktop(p, {
|
|
825
|
+
channel: 'cloud', session, apps: task.apps || [],
|
|
826
|
+
lookup: (id) => desk.snaps[id] || null,
|
|
827
|
+
windows: (id) => desk.windows[id] || null,
|
|
828
|
+
});
|
|
829
|
+
if (chk.sensitive === 'pay') throw err('PAY_REFUSED', `「${chk.target?.name}」像是要花钱的按钮。桌面能力不代做付款,请用户亲手点。`);
|
|
830
|
+
if (chk.sensitive === 'sensitive') await confirmDesk(chk, task, cfg);
|
|
831
|
+
|
|
832
|
+
const env = await hostCall({ op: 'run', argv: chk.argv, timeoutMs: Math.min(Math.max((Number(p.timeout) || 0) + 10000, 30000), 90000) });
|
|
833
|
+
if (env.ok && (chk.cmd === 'snapshot' || chk.cmd === 'find')) {
|
|
834
|
+
const rec = recordOf(env.data);
|
|
835
|
+
if (rec && !rec.app) rec.app = chk.app;
|
|
836
|
+
if (chk.cmd === 'snapshot') checkObserved(rec);
|
|
837
|
+
if (rec) {
|
|
838
|
+
const prev = chk.cmd === 'find' ? desk.snaps[rec.id] : null;
|
|
839
|
+
delete desk.snaps[rec.id];
|
|
840
|
+
desk.snaps[rec.id] = prev ? { ...prev, refs: { ...prev.refs, ...rec.refs } } : rec;
|
|
841
|
+
const ids = Object.keys(desk.snaps);
|
|
842
|
+
while (ids.length > DESK_SNAPS) delete desk.snaps[ids.shift()];
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (env.ok && chk.cmd === 'list-windows') {
|
|
846
|
+
env.data = filterWindows(env.data, 'cloud');
|
|
847
|
+
desk.windows = windowsMap(env.data);
|
|
848
|
+
}
|
|
849
|
+
const held = await updateTask(task.jobId, (x) => { x.desk = desk; });
|
|
850
|
+
if (!held) throw err('CLOUD_DENIED', '这个任务已经结束');
|
|
851
|
+
const text = renderEnvelope(chk.cmd, env, { target: chk.target });
|
|
852
|
+
if (!env.ok) throw err(env.error?.code || 'INTERNAL', text);
|
|
853
|
+
return { text, audit: `${chk.cmd}${chk.app ? ` ${chk.app}` : ''}${chk.target?.name ? ` ${String(chk.target.name).slice(0, 40)}` : ''}` };
|
|
854
|
+
}
|
|
855
|
+
|
|
656
856
|
// ---------- 命令 ----------
|
|
657
857
|
|
|
858
|
+
// 结果盖上执行它的设备(from)。服务器据此知道回包来自浏览器还是它名下的桌面。
|
|
658
859
|
async function handle(m) {
|
|
659
860
|
const started = Date.now();
|
|
660
861
|
const jobId = String(m.job?.id || '');
|
|
862
|
+
const cfg = await getCfg();
|
|
863
|
+
const self = { id: cfg?.deviceId, kind: 'browser' };
|
|
864
|
+
let from = self;
|
|
661
865
|
try {
|
|
866
|
+
const child = await desktopChild();
|
|
867
|
+
const where = routeHere({ ...m, cap: m.cmd === 'desktop' ? 'desktop' : capOf(m) }, { self, children: child ? [child] : [] });
|
|
868
|
+
from = where.device;
|
|
662
869
|
const data = await exec(m);
|
|
663
870
|
let out = data;
|
|
664
871
|
const audit = out && typeof out === 'object' ? out.audit : '';
|
|
@@ -666,10 +873,10 @@ async function handle(m) {
|
|
|
666
873
|
if (JSON.stringify(out ?? null).length > policy.RESULT_MAX) {
|
|
667
874
|
out = { text: String(data?.text || '').slice(0, 200000) + '\n…(结果太大,已截断)' };
|
|
668
875
|
}
|
|
669
|
-
send({ type: 'res', id: m.id, ok: true, data: out });
|
|
876
|
+
send(stampFrom({ type: 'res', id: m.id, ok: true, data: out }, from));
|
|
670
877
|
await log({ job: jobId, cmd: m.cmd, ok: true, ms: Date.now() - started, ...(audit ? { msg: String(audit).slice(0, 160) } : {}) });
|
|
671
878
|
} catch (e) {
|
|
672
|
-
send({ type: 'res', id: m.id, ok: false, error: { code: e.code || 'INTERNAL', message: e.message || String(e) } });
|
|
879
|
+
send(stampFrom({ type: 'res', id: m.id, ok: false, error: { code: e.code || 'INTERNAL', message: e.message || String(e) } }, from));
|
|
673
880
|
await log({ job: jobId, cmd: m.cmd, ok: false, code: e.code || 'INTERNAL', msg: String(e.message || e).slice(0, 120) });
|
|
674
881
|
}
|
|
675
882
|
}
|
|
@@ -695,8 +902,9 @@ async function exec(m) {
|
|
|
695
902
|
if (recent.length >= PER_MINUTE) throw err('CLOUD_RATE', '命令太密(每分钟最多 120 条),放慢一点');
|
|
696
903
|
recent.push(now);
|
|
697
904
|
|
|
698
|
-
|
|
699
|
-
const
|
|
905
|
+
if (m.cmd === 'devices') return devicesCmd(cfg);
|
|
906
|
+
const reason = ['sites', 'folders', 'apps'].includes(m.cmd) ? String(m.params?.reason || '').slice(0, 200) : '';
|
|
907
|
+
const task = await taskFor(m.job, cfg, reason, { allowEmpty: m.cmd === 'folders' || m.cmd === 'apps' });
|
|
700
908
|
const jobId = task.jobId;
|
|
701
909
|
const updated = await updateTask(jobId, (t) => { t.count += 1; t.lastAt = Date.now(); });
|
|
702
910
|
if (!updated) throw err('CLOUD_DENIED', '这个任务已经结束');
|
|
@@ -706,6 +914,8 @@ async function exec(m) {
|
|
|
706
914
|
const p = policy.checkCommand(m.cmd, m.params, { hosts, origin: cfg.origin });
|
|
707
915
|
if (m.cmd === 'sites') return requestSites(updated, p, cfg);
|
|
708
916
|
if (m.cmd === 'folders') return requestFolders(updated, p, cfg);
|
|
917
|
+
if (m.cmd === 'apps') return requestApps(updated, p, cfg);
|
|
918
|
+
if (m.cmd === 'desktop') return desktopCmd(p, updated, cfg);
|
|
709
919
|
if (m.cmd === 'fs_list' || m.cmd === 'fs_read') return fsCmd(m.cmd, p, updated);
|
|
710
920
|
const sid = sidOf(jobId);
|
|
711
921
|
const label = labelOf(cfg.origin);
|
|
@@ -867,8 +1077,9 @@ async function panelState() {
|
|
|
867
1077
|
cfg: cfg && { origin: cfg.origin, account: cfg.account, sites: allowOf(cfg), auto: cfg.auto, autoConfirm: !!cfg.autoConfirm, paused: cfg.paused, pairedAt: cfg.pairedAt },
|
|
868
1078
|
status: status || { state: 'off' },
|
|
869
1079
|
claim: claimed && claimed.expiresAt > Date.now() ? { origin: claimed.origin, account: claimed.account } : null,
|
|
870
|
-
tasks: Object.values(tasks).sort((a, b) => b.lastAt - a.lastAt),
|
|
1080
|
+
tasks: Object.values(tasks).sort((a, b) => b.lastAt - a.lastAt).map(({ desk: _desk, ...t }) => t),
|
|
871
1081
|
folders: await folders.listFolders().catch(() => []),
|
|
1082
|
+
desktop: await deskInfo().then((d) => (d && chrome.runtime.sendNativeMessage ? d : null)),
|
|
872
1083
|
log: list.slice(-40).reverse(),
|
|
873
1084
|
defaultOrigin: policy.DEFAULT_ORIGIN,
|
|
874
1085
|
};
|
|
@@ -930,6 +1141,10 @@ async function onPanel(m) {
|
|
|
930
1141
|
case 'decide': await decide(String(m.jobId), !!m.allow); return panelState();
|
|
931
1142
|
case 'decideSites': decideSites(String(m.jobId), !!m.allow); return panelState();
|
|
932
1143
|
case 'decideFolders': decideFolders(String(m.jobId), !!m.allow); return panelState();
|
|
1144
|
+
case 'decideApps': decideApps(String(m.jobId), !!m.allow); return panelState();
|
|
1145
|
+
case 'decideDesk': decideDesk(String(m.jobId), !!m.allow); return panelState();
|
|
1146
|
+
case 'desktopEnable': await enableDesktop(); return panelState();
|
|
1147
|
+
case 'desktopDisable': await disableDesktop(); return panelState();
|
|
933
1148
|
case 'openMount': await openMount(m.folderId ? String(m.folderId) : ''); return panelState();
|
|
934
1149
|
case 'removeFolder': await folders.removeFolder(String(m.id || '')); await reportFolders(); return panelState();
|
|
935
1150
|
case 'foldersChanged': await reportFolders(); return panelState();
|
|
@@ -956,6 +1171,8 @@ export function initCloud(d) {
|
|
|
956
1171
|
|
|
957
1172
|
chrome.notifications?.onButtonClicked.addListener((id, idx) => {
|
|
958
1173
|
if (id.startsWith('hc-cloud-folders:')) decideFolders(id.slice('hc-cloud-folders:'.length), idx === 0);
|
|
1174
|
+
else if (id.startsWith('hc-cloud-apps:')) decideApps(id.slice('hc-cloud-apps:'.length), idx === 0);
|
|
1175
|
+
else if (id.startsWith('hc-cloud-desk:')) decideDesk(id.slice('hc-cloud-desk:'.length), idx === 0);
|
|
959
1176
|
else if (id.startsWith('hc-cloud-sites:')) decideSites(id.slice('hc-cloud-sites:'.length), idx === 0);
|
|
960
1177
|
else if (id.startsWith('hc-cloud:')) void decide(id.slice('hc-cloud:'.length), idx === 0);
|
|
961
1178
|
else if (id.startsWith('hc-folder:')) void openMount(id.slice('hc-folder:'.length));
|
|
@@ -1002,4 +1219,10 @@ export function initCloud(d) {
|
|
|
1002
1219
|
}).then(pushLive);
|
|
1003
1220
|
|
|
1004
1221
|
void ensureCloud();
|
|
1222
|
+
|
|
1223
|
+
void chrome.storage.local.get(DESK_PENDING).then(async (r) => {
|
|
1224
|
+
if (!r[DESK_PENDING]) return;
|
|
1225
|
+
await chrome.storage.local.remove(DESK_PENDING);
|
|
1226
|
+
await enableDesktop().catch((e) => log({ ev: 'desktop_fail', msg: e.message || String(e) }));
|
|
1227
|
+
});
|
|
1005
1228
|
}
|