livedesk 0.1.651 → 0.1.653
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/hub/src/console-direct.js +1 -0
- package/hub/src/console-direct.test.mjs +42 -0
- package/package.json +1 -1
- package/web/dist/app.html +12 -5
- package/web/dist/app.webmanifest +1 -1
- package/web/dist/assets/{AgentSettingsTab-CmoOFOcl.js → AgentSettingsTab-BBNQK3m_.js} +1 -1
- package/web/dist/assets/{AgentsPage-0p2Fa7hh.js → AgentsPage-DUMU7WPH.js} +1 -1
- package/web/dist/assets/{App-iw6HSNbk.js → App-WZAN0Iwh.js} +21 -21
- package/web/dist/assets/{CaptureGallery-DlsfaKXP.js → CaptureGallery-Dgucz-9s.js} +1 -1
- package/web/dist/assets/{DesktopUpdatePanel-D5sguo6U.js → DesktopUpdatePanel-BPD9IaXS.js} +1 -1
- package/web/dist/assets/{SettingsPage-BbO24wOs.js → SettingsPage-JYlSHnWI.js} +1 -1
- package/web/dist/assets/{SettingsTabs-DG1Q2KY3.js → SettingsTabs-D75pwHMD.js} +1 -1
- package/web/dist/assets/{ShareFilesPage-0PiU0oYj.js → ShareFilesPage-Bp5Dh9tQ.js} +1 -1
- package/web/dist/assets/{SupportPage-DcQ5yK4j.js → SupportPage-BwS_toBH.js} +1 -1
- package/web/dist/assets/{VuvoDeskApp-CeYs_j7-.js → VuvoDeskApp-BIrWZOFb.js} +1 -1
- package/web/dist/assets/{app-BSxy34W0.js → app-BJgxvk6P.js} +1 -1
- package/web/dist/assets/{main-D5ioXAGK.js → main-DAAb7wf6.js} +2 -2
- package/web/dist/assets/{styles-BriraxLg.js → styles-CO6aHjLW.js} +1 -1
- package/web/dist/assets/{styles-BbMJwyw_.css → styles-DDKP4MTD.css} +1 -1
- package/web/dist/index.html +11 -4
- package/web/dist/sw.js +1 -1
- package/web/dist/vuvodesk-build-evidence.json +52 -52
|
@@ -169,6 +169,7 @@ function allowedHttpRequest(method, pathname) {
|
|
|
169
169
|
|| pathname === '/api/auth/session'
|
|
170
170
|
|| pathname === '/api/remote/status'
|
|
171
171
|
|| pathname === '/api/hub/status'
|
|
172
|
+
|| pathname === '/api/remote/pwa-diagnostics'
|
|
172
173
|
|| pathname === '/api/update/status'
|
|
173
174
|
|| pathname === '/api/update/apply') {
|
|
174
175
|
return true;
|
|
@@ -627,6 +627,48 @@ test('reliable control channel bridges bounded HTTP over fragmented wire message
|
|
|
627
627
|
direct.close();
|
|
628
628
|
});
|
|
629
629
|
|
|
630
|
+
test('reliable control channel admits the authenticated PWA diagnostic route', async () => {
|
|
631
|
+
let fetchCalls = 0;
|
|
632
|
+
const diagnosticBody = JSON.stringify({ schema: 'livedesk.pwa-runtime-diagnostics.v1', events: [] });
|
|
633
|
+
const { direct, signal } = await connectedDirect({
|
|
634
|
+
fetchImpl: async (url, init) => {
|
|
635
|
+
fetchCalls += 1;
|
|
636
|
+
assert.equal(url, 'http://127.0.0.1:5179/api/remote/pwa-diagnostics');
|
|
637
|
+
assert.equal(init.method, 'POST');
|
|
638
|
+
assert.equal(init.headers['content-type'], 'application/json');
|
|
639
|
+
assert.equal(Buffer.from(init.body).toString('utf8'), diagnosticBody);
|
|
640
|
+
return new Response('{"ok":true,"accepted":true}', {
|
|
641
|
+
status: 202,
|
|
642
|
+
statusText: 'Accepted',
|
|
643
|
+
headers: { 'Content-Type': 'application/json' }
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
const peer = offer(signal);
|
|
648
|
+
const control = new FakeDataChannel('livedesk-control-v1');
|
|
649
|
+
peer.emitDataChannel(control);
|
|
650
|
+
control.open();
|
|
651
|
+
|
|
652
|
+
sendWire(control, {
|
|
653
|
+
type: 'http-request',
|
|
654
|
+
requestId: REQUEST_ID,
|
|
655
|
+
method: 'POST',
|
|
656
|
+
path: '/api/remote/pwa-diagnostics',
|
|
657
|
+
headers: { 'content-type': 'application/json' },
|
|
658
|
+
bodyBase64: Buffer.from(diagnosticBody).toString('base64')
|
|
659
|
+
});
|
|
660
|
+
await settle();
|
|
661
|
+
const response = decodeWireMessages(control.sent)
|
|
662
|
+
.map(message => JSON.parse(message.data))
|
|
663
|
+
.find(message => message.type === 'http-response');
|
|
664
|
+
assert.equal(fetchCalls, 1, 'The PWA diagnostic request must reach the local Hub API.');
|
|
665
|
+
assert.equal(response.requestId, REQUEST_ID);
|
|
666
|
+
assert.equal(response.status, 202);
|
|
667
|
+
assert.equal(Buffer.from(response.bodyBase64, 'base64').toString('utf8'), '{"ok":true,"accepted":true}');
|
|
668
|
+
assert.equal(direct.inspect().pendingHttpRequests, 0);
|
|
669
|
+
direct.close();
|
|
670
|
+
});
|
|
671
|
+
|
|
630
672
|
test('Team Operator can use Wall and Control but cannot mutate the Hub owner runtime', async () => {
|
|
631
673
|
const teamWorkspaceAccess = {
|
|
632
674
|
workspaceId: TEAM_WORKSPACE_ID,
|
package/package.json
CHANGED
package/web/dist/app.html
CHANGED
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
.vuvodesk-static-boot [hidden] { display: none; }
|
|
53
53
|
@keyframes vuvodesk-static-boot-spin { to { transform: rotate(360deg); } }
|
|
54
54
|
</style>
|
|
55
|
-
<script type="module" crossorigin src="/assets/app-
|
|
55
|
+
<script type="module" crossorigin src="/assets/app-BJgxvk6P.js" data-vuvodesk-entry></script>
|
|
56
56
|
<link rel="modulepreload" crossorigin href="/assets/icons-CacEbnZu.js">
|
|
57
57
|
<link rel="modulepreload" crossorigin href="/assets/react-Cfedt3N5.js">
|
|
58
|
-
<link rel="modulepreload" crossorigin href="/assets/styles-
|
|
58
|
+
<link rel="modulepreload" crossorigin href="/assets/styles-CO6aHjLW.js">
|
|
59
59
|
<link rel="modulepreload" crossorigin href="/assets/supabase-C7qjtLN3.js">
|
|
60
60
|
<link rel="modulepreload" crossorigin href="/assets/frame-lifecycle-esOdP-EY.js">
|
|
61
|
-
<link rel="modulepreload" crossorigin href="/assets/App-
|
|
62
|
-
<link rel="stylesheet" crossorigin href="/assets/styles-
|
|
61
|
+
<link rel="modulepreload" crossorigin href="/assets/App-WZAN0Iwh.js">
|
|
62
|
+
<link rel="stylesheet" crossorigin href="/assets/styles-DDKP4MTD.css">
|
|
63
63
|
<link rel="stylesheet" crossorigin href="/assets/App-DFLBNvIi.css">
|
|
64
64
|
</head>
|
|
65
65
|
<body>
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
const title = document.querySelector('[data-vuvodesk-static-boot-title]');
|
|
78
78
|
const detail = document.querySelector('[data-vuvodesk-static-boot-detail]');
|
|
79
79
|
const retry = document.querySelector('[data-vuvodesk-static-boot-retry]');
|
|
80
|
+
let recoveryStarted = false;
|
|
80
81
|
const revealFailure = () => {
|
|
81
82
|
if (!boot || !boot.isConnected) return;
|
|
82
83
|
boot.setAttribute('aria-busy', 'false');
|
|
@@ -95,7 +96,12 @@
|
|
|
95
96
|
}
|
|
96
97
|
};
|
|
97
98
|
const recoverEntryFailure = () => {
|
|
98
|
-
if (!boot || !boot.isConnected) return;
|
|
99
|
+
if (recoveryStarted || !boot || !boot.isConnected) return;
|
|
100
|
+
recoveryStarted = true;
|
|
101
|
+
if (window.__vuvodeskStaticBootTimer !== undefined) {
|
|
102
|
+
window.clearTimeout(window.__vuvodeskStaticBootTimer);
|
|
103
|
+
delete window.__vuvodeskStaticBootTimer;
|
|
104
|
+
}
|
|
99
105
|
const url = new URL(window.location.href);
|
|
100
106
|
const alreadyRetried = url.searchParams.has('startup-retry')
|
|
101
107
|
|| url.searchParams.has('startup-refresh');
|
|
@@ -110,6 +116,7 @@
|
|
|
110
116
|
window.stop();
|
|
111
117
|
window.location.replace(url.toString());
|
|
112
118
|
};
|
|
119
|
+
window.__vuvodeskStaticBootTimer = window.setTimeout(recoverEntryFailure, 15_000);
|
|
113
120
|
window.addEventListener('error', event => {
|
|
114
121
|
if (
|
|
115
122
|
event.target instanceof HTMLScriptElement
|
package/web/dist/app.webmanifest
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{h as x,j as t}from"./styles-
|
|
1
|
+
import{h as x,j as t}from"./styles-CO6aHjLW.js";import{a as c,a5 as A,k as q,a6 as I,a7 as z}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const E={enabled:!0};function w(a){return{enabled:a?.enabled===!0}}async function L(a,p,f){let l=null;for(let s=0;s<2;s+=1){const g=await x("/api/settings",{signal:f},p);if(g.settings.agent?.enabled===a)return;try{await x("/api/settings",{method:"PATCH",signal:f,body:JSON.stringify({revision:g.revision,agent:{enabled:a}})},p);return}catch(i){if(l=i,s!==0||!String(i instanceof Error?i.message:i).startsWith("409 "))throw i}}throw l}function F({hubUrl:a,showToast:p}){const[f,l]=c.useState(E),[s,g]=c.useState(null),[i,m]=c.useState(""),[j,v]=c.useState(""),[y,d]=c.useState(""),b=c.useRef(0),u=c.useRef(null);c.useEffect(()=>{let e=!0;u.current?.abort();const n=new AbortController;u.current=n;const h=++b.current;return g(null),l(E),m(""),d(""),v(""),x("/api/settings/agent",{signal:n.signal},a).then(o=>{!e||n.signal.aborted||h!==b.current||(g(o.settings),l(w(o.settings)))}).catch(o=>{e&&!n.signal.aborted&&h===b.current&&d(o instanceof Error?o.message:String(o))}),()=>{e=!1,n.abort(),u.current===n&&(u.current=null)}},[a]);const k=(e,n)=>l(h=>({...h,[e]:n})),C=e=>{g(e),l(w(e))},S=()=>({generation:b.current,controller:u.current}),r=e=>e.generation===b.current&&e.controller!==null&&e.controller===u.current&&!e.controller.signal.aborted,R=async()=>{if(!s){d("Agent settings are not available yet.");return}const e=S();if(r(e)){m("save"),d(""),v("");try{if(await L(f.enabled,a,e.controller.signal),!r(e))return;const n=await x("/api/settings/agent",{signal:e.controller.signal},a);if(!r(e))return;C(n.settings),v("Codex Agent settings saved.")}catch(n){r(e)&&d(n instanceof Error?n.message:String(n))}finally{r(e)&&m("")}}},D=async()=>{const e=S();if(r(e)){m("test"),d(""),v("");try{const n=await x("/api/settings/agent/test",{method:"POST",signal:e.controller.signal,body:JSON.stringify({})},a);if(!r(e))return;const h=await x("/api/settings/agent",{signal:e.controller.signal},a);if(!r(e))return;C(h.settings);const o=Number.isFinite(n.connection?.latencyMs)?Math.max(0,Math.round(n.connection.latencyMs)):null;p(`Codex Agent connection verified${o!==null?` in ${o} ms.`:"."}`,"success")}catch(n){r(e)&&d(n instanceof Error?n.message:String(n))}finally{r(e)&&m("")}}},T=s?.codexInstallation==="installed"&&s.codexAuth==="signed-in",N=s!==null;return t.jsxs("div",{className:"settings-tab-content agent-settings-content",children:[t.jsxs("section",{className:"settings-section-card agent-enable-card",children:[t.jsxs("label",{className:"settings-toggle agent-enable-toggle",children:[t.jsx("input",{type:"checkbox",checked:f.enabled,onChange:e=>k("enabled",e.target.checked),disabled:!N||i!==""}),t.jsx("span",{children:"Enable Codex Agent"})]}),t.jsxs("p",{className:"settings-help",children:[t.jsx(A,{size:12})," Enabled by default. Agent commands use only the Codex account signed in on this Hub."]})]}),y&&t.jsx("div",{className:"agent-settings-alert error",role:"alert",children:y}),j&&t.jsx("div",{className:"agent-settings-alert success",role:"status",children:j}),t.jsxs("section",{className:"settings-section-card",children:[t.jsxs("div",{className:"settings-section-heading",children:[t.jsxs("div",{children:[t.jsx("span",{children:"Connection"}),t.jsx("h3",{children:"Codex SDK / CLI"})]}),t.jsx(q,{size:20})]}),t.jsxs("div",{className:"agent-connection-row",children:[t.jsx("span",{className:`agent-connection-dot ${T?"connected":s?.codexAuth==="not-signed-in"?"unavailable":""}`}),t.jsx("span",{children:s?.codexInstallation==="not-installed"?"Codex SDK not installed":s?.codexAuth==="signed-in"?"Codex CLI signed in":s?.codexAuth==="not-signed-in"?"Codex CLI sign-in required":"Codex status not tested"}),t.jsxs("button",{className:"settings-reset",onClick:()=>{D()},disabled:i!=="",type:"button",children:[t.jsx(I,{size:15})," ",i==="test"?"Testing":"Test connection"]})]}),t.jsxs("p",{className:"settings-help",children:[t.jsx(A,{size:12})," The Hub uses the installed Codex SDK / CLI to plan approved commands and run them through VuvoDesk Agent tools on selected computers. VuvoDesk never asks for separate credentials."]})]}),t.jsx("div",{className:"settings-action-row agent-settings-actions",children:t.jsxs("button",{className:"text-button primary",onClick:()=>{R()},disabled:!N||i!=="",type:"button",children:[t.jsx(z,{size:15})," ",i==="save"?"Saving":"Save Agent settings"]})})]})}export{F as AgentSettingsTab};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{j as t,e as ht,h as F}from"./styles-
|
|
1
|
+
import{j as t,e as ht,h as F}from"./styles-CO6aHjLW.js";import{a as d,m as yt,n as Ce,o as vt,l as ee,X as xe,p as wt,q as kt,s as Je,B as bt,t as Ct,u as Se,S as H,v as xt,w as St,x as _e,b as jt,y as At,k as Ze,z as Rt,N as Nt,F as Pt,H as It,I as Tt,J as Et,U as ue,c as Xe}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const Ot=d.forwardRef(function({value:s,onChange:n,onSubmit:r,onOpenSuggestedTasks:i,suggestedOpen:f=!1,suggestedTaskCount:c,disabled:v=!1,placeholder:S="Ask VuvoDesk to work across your machines...",compact:g=!1},x){return t.jsxs("form",{className:"agents-composer",onSubmit:w=>{w.preventDefault(),r()},children:[t.jsx("button",{className:`agents-suggested-trigger ${f?"active":""}`,type:"button","aria-label":c===void 0?"Open Suggested tasks":`Open Suggested tasks (${c} available)`,"aria-expanded":f,onClick:i,disabled:v,children:t.jsx(yt,{size:17})}),t.jsx("textarea",{ref:x,value:s,onChange:w=>n(w.target.value.slice(0,4e3)),placeholder:S,"aria-label":"Ask VuvoDesk",rows:g?1:2,disabled:v}),t.jsxs("button",{className:"agents-run-button",type:"submit",disabled:v||!s.trim(),children:[v?t.jsx(Ce,{className:"spin",size:16}):t.jsx(vt,{size:16}),v?"Running":"Run"]})]})}),$t=["read","processControl","serviceControl","applicationControl","fileRead","fileWrite","fileDelete","shell","script","softwareInstall","network","systemPower","systemConfiguration","userAccount"];function de(e){return e.deviceName?.trim()||e.hostname?.trim()||e.deviceId}function pe(e){return e.connected!==!0?!1:e.synthetic===!0?!0:!e.channels||e.channels.control===!0}function Dt(e){return e==="running"||e==="completed"||e==="failed"||e==="cancelled"?e:"queued"}function _t(e){switch(e){case"process.list":return"Process list";case"system.health":return"System health";case"gpu.status":return"GPU status";case"disk.status":return"Disk status";case"service.status":return"Service status";case"diagnostics.collect":return"Diagnostics";case"process.control":return"Process control";case"service.control":return"Service control";case"application.launch":return"Launch application";case"application.close":return"Close application";case"file.read":return"Read file";case"file.write":return"Write file";case"file.delete":return"Delete file";case"file.list":return"List directory";case"command.run":return"Run command";case"script.run":return"Run script";case"software.install":return"Install software";case"network.status":return"Network status";case"system.power":return"Power action";case"system.configure":return"System configuration";case"logs.collect":return"Collect logs"}}const zt=64*1024,ze="[Earlier log output hidden by PWA display limit.]",Qe=new TextEncoder,Mt=new TextDecoder("utf-8",{fatal:!0}),Me=16*1024,Lt=200,Le="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|authorization|api[_-]?key|private[_-]?key|connection[_-]?string|credential|password|passwd|secret|cookie|set-cookie)",Ft="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|token|authorization|api[_-]?key|private[_-]?key|connection[_-]?string|credential|password|passwd|secret|cookie|set-cookie)",Bt="(?:access[_-]?(?:token|key)|refresh[_-]?token|id[_-]?token|pair[_-]?(?:token|key)|token|authorization|api[_-]?key|credential|password|secret|cookie)",Ut=/\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{8,}|(?:AKIA|ASIA)[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{35}|npm_[A-Za-z0-9]{8,}|xox[a-z]-[A-Za-z0-9-]{8,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,})\b/g;function qt(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:null}function Fe(e){return Qe.encode(e).byteLength}function Be(e,s){const n=Qe.encode(e);if(n.byteLength<=s)return{value:e,truncated:!1};let r=n.byteLength-s;for(;r<n.byteLength&&(n[r]&192)===128;)r+=1;let i=Mt.decode(n.subarray(r));const f=i.indexOf(`
|
|
2
2
|
`);return f>=0&&f<i.length-1&&(i=i.slice(f+1)),{value:i,truncated:!0}}function et(e){return e.replace(/-----BEGIN [^-\r\n]*PRIVATE KEY-----[\s\S]*?-----END [^-\r\n]*PRIVATE KEY-----/gi,"[REDACTED PRIVATE KEY]").replace(/\b(?:proxy-)?authorization\s*[:=]\s*[^\r\n]*|\b(?:set-)?cookie\s*[:=]\s*[^\r\n]*/gi,"credential-header=[REDACTED]").replace(/\bBearer\s+(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,"Bearer [REDACTED]").replace(new RegExp(`("${Le}"\\s*:\\s*")[^"\\r\\n]*(")`,"gi"),"$1[REDACTED]$2").replace(new RegExp(`('${Le}'\\s*:\\s*')[^'\\r\\n]*(')`,"gi"),"$1[REDACTED]$2").replace(new RegExp(`((?:[A-Za-z0-9.-]+[_-])?${Ft}\\s*[:=]\\s*)(?!\\[REDACTED\\])[^\\s,;&]+`,"gi"),"$1[REDACTED]").replace(new RegExp(`([?&]${Bt}=)[^&\\s]+`,"gi"),"$1[REDACTED]").replace(/\b(?:eyJ|[A-Za-z0-9_-]{8,}\.)[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,"[REDACTED TOKEN]").replace(Ut,"[REDACTED KEY]")}function Gt(e){return typeof e!="string"?"source unknown":et(e).replace(/[\u0000-\u001f\u007f]/g," ").trim().slice(0,64)||"source unknown"}function Ue(e){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=0?Math.floor(e):null}function Vt(e){if(typeof e!="string"||e.length===0)return{output:"",displayTruncated:!1};const s=Be(e.replace(/\r\n?/g,`
|
|
3
3
|
`),zt);let n=et(s.value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g," ").trim(),r=s.truncated;const i=n?n.split(`
|
|
4
4
|
`):[];if(i.length>Lt-1&&(n=i.slice(-199).join(`
|