@addai/node 0.27.0 → 0.28.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/assets/vault-extension/assets/inter.woff2 +0 -0
- package/assets/vault-extension/background.js +45 -0
- package/assets/vault-extension/content.js +62 -0
- package/assets/vault-extension/icons/icon-128.png +0 -0
- package/assets/vault-extension/icons/icon-16.png +0 -0
- package/assets/vault-extension/icons/icon-32.png +0 -0
- package/assets/vault-extension/icons/icon-48.png +0 -0
- package/assets/vault-extension/manifest.json +39 -0
- package/dist/capabilities.js +2 -1
- package/dist/command-runner.js +109 -2
- package/dist/desktop/docker.d.ts +4 -1
- package/dist/desktop/docker.js +7 -3
- package/dist/desktop/engine.d.ts +13 -0
- package/dist/desktop/engine.js +35 -4
- package/dist/desktop/install-engine.d.ts +33 -2
- package/dist/desktop/install-engine.js +138 -12
- package/dist/desktop/learn/browser.d.ts +41 -0
- package/dist/desktop/learn/browser.js +136 -0
- package/dist/desktop/learn/injected.mjs +173 -0
- package/dist/desktop/learn/recorder.mjs +151 -0
- package/dist/desktop/learn/session.d.ts +51 -0
- package/dist/desktop/learn/session.js +224 -0
- package/dist/desktop/provider.d.ts +4 -0
- package/dist/desktop/start-engine.d.ts +7 -4
- package/dist/desktop/start-engine.js +50 -6
- package/dist/desktop/vault-extension.d.ts +21 -0
- package/dist/desktop/vault-extension.js +112 -0
- package/dist/desktop/vault-seed.mjs +112 -0
- package/dist/desktop/vault-session.d.ts +41 -0
- package/dist/desktop/vault-session.js +164 -0
- package/dist/kimi-spawn.js +29 -10
- package/package.json +4 -3
- package/scripts/copy-assets.js +18 -0
- package/dist/tui.d.ts +0 -1
- package/dist/tui.js +0 -314
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
(function(){"use strict";const k={ITEMS_FOR_DOMAIN:"items:for-domain",ITEM_REVEAL:"item:reveal",ITEM_CREATE:"item:create",INFER_FORM:"infer:form"},$=80,E=a=>{if(typeof a!="string")return null;const e=a.replace(/\s+/g," ").trim();return e?e.length>$?e.slice(0,$):e:null},aa=a=>E(a.getAttribute("aria-label")),ea=a=>{if(!a.id)return null;const e=a.ownerDocument.querySelector(`label[for="${CSS.escape(a.id)}"]`);return e?E(e.textContent):null},ta=a=>{const e=a.closest("label");if(!e)return null;const t=e.cloneNode(!0);return t.querySelectorAll("input, textarea, select").forEach(r=>r.remove()),E(t.textContent)},ra=a=>E(a.getAttribute("placeholder")),na=a=>a&&(aa(a)||ea(a)||ta(a)||ra(a))||null,oa=new Set(["hidden"]),ia=a=>{var t,r;if(oa.has(a.type))return!0;const e=(r=(t=a.ownerDocument.defaultView)==null?void 0:t.getComputedStyle)==null?void 0:r.call(t,a);return e?e.display==="none"||e.visibility==="hidden":!1},da=a=>({_el:a,type:(a.type||a.tagName||"text").toLowerCase(),name:a.getAttribute("name")||null,autocomplete:a.getAttribute("autocomplete")||null,placeholder:a.getAttribute("placeholder")||null,label:na(a),ariaLabel:a.getAttribute("aria-label")||null,required:a.hasAttribute("required"),visible:!ia(a)}),sa=a=>{let e=2166136261;const t=a.slice().sort().join("|");for(let r=0;r<t.length;r++)e^=t.charCodeAt(r),e=e+((e<<1)+(e<<4)+(e<<7)+(e<<8)+(e<<24))>>>0;return e.toString(16).padStart(8,"0")},la=a=>sa(a.filter(e=>e.visible).map(e=>`${e.type}:${e.name||""}:${e.autocomplete||""}`)),D=(a,e)=>Array.from(a.querySelectorAll(e)).filter(t=>!t.disabled),R=(a,e)=>{const t=a.map(da),r=t.find(s=>s.type==="password"&&s.visible),i=t.find(s=>s.visible),n=r||i||t[0],d=(n==null?void 0:n._el)||null,o=t.map((s,c)=>({...s,id:`i_${c}`})),l=new Map(o.map((s,c)=>[s.id,t[c]._el]));return{formId:`form_${e}`,signature:la(o),inputs:o.map(({_el:s,...c})=>c),anchor:d,elementById:l}},ca=(a=document)=>{const e=[],t=new WeakSet;let r=0;const i=Array.from(a.querySelectorAll("form"));for(const d of i){const o=D(d,"input, select, textarea");o.length&&(o.forEach(l=>t.add(l)),e.push(R(o,r++)))}const n=D(a,'input[type="password"]').filter(d=>!t.has(d));for(const d of n){const o=d.parentElement;if(!o)continue;const l=Array.from(o.querySelectorAll("input, select, textarea")).filter(s=>!s.disabled&&!t.has(s));l.length&&(l.forEach(s=>t.add(s)),e.push(R(l,r++)))}return e},z="addai.forms",N=50;let f=null;const M=async()=>{if(f)return f;try{f=(await chrome.storage.local.get(z))[z]||{}}catch{f={}}return f},q=async a=>{f=a;try{await chrome.storage.local.set({[z]:a})}catch{}},pa=async(a,e)=>{const r=(await M())[a];if(!r)return null;const i=r.find(n=>n.signature===e);return i?i.mapping:null},ua=async(a,e,t,r)=>{const i=await M(),n=(i[a]||[]).filter(o=>o.signature!==e),d=Date.now();n.push({signature:e,mapping:t,fields:r,savedAt:d,lastUsedAt:d}),n.sort((o,l)=>l.lastUsedAt-o.lastUsedAt),n.length>N&&(n.length=N),await q({...i,[a]:n})},fa=async(a,e)=>{const t=await M(),r=t[a];if(!r)return;const i=r.findIndex(n=>n.signature===e);i<0||(r[i]={...r[i],lastUsedAt:Date.now()},await q({...t,[a]:r}))},ga=':host{--addai-brand: rgb(127, 86, 217);--addai-brand-strong: rgb(105, 65, 198);--addai-brand-soft: rgba(127, 86, 217, .12);--addai-brand-ring: rgba(127, 86, 217, .24);--addai-glass-fill: rgba(255, 255, 255, .66);--addai-glass-fill-strong: rgba(255, 255, 255, .92);--addai-glass-gloss: linear-gradient(180deg, rgba(255, 255, 255, .55) 0%, rgba(255, 255, 255, 0) 36%, rgba(255, 255, 255, 0) 70%, rgba(11, 15, 23, .04) 100%);--addai-glass-edge: rgba(11, 15, 23, .1);--addai-glass-edge-soft: rgba(11, 15, 23, .06);--addai-glass-inner: inset 0 1px 0 rgba(255, 255, 255, .7), inset 0 -1px 0 rgba(11, 15, 23, .04);--addai-glass-blur: blur(24px) saturate(180%);--addai-glass-blur-strong: blur(36px) saturate(190%);--addai-fg: rgb(16, 24, 40);--addai-fg-strong: rgb(16, 24, 40);--addai-muted: rgb(94, 100, 115);--addai-muted-strong: rgb(71, 79, 96);--addai-radius: 14px;--addai-radius-sm: 10px;--addai-radius-pill: 999px;--addai-shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 12px 28px rgba(16, 24, 40, .14), 0 2px 6px rgba(16, 24, 40, .06);--addai-shadow-lifted: 0 1px 2px rgba(16, 24, 40, .06), 0 24px 48px rgba(16, 24, 40, .18), 0 4px 12px rgba(16, 24, 40, .08);--addai-font: "AddaiInter", "Open Sans", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--addai-font-feature: "cv11", "ss01", "ss03";all:initial;font-family:var(--addai-font);font-feature-settings:var(--addai-font-feature);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--addai-fg)}@media(prefers-color-scheme:dark){:host{--addai-brand: rgb(176, 156, 255);--addai-brand-strong: rgb(159, 138, 245);--addai-brand-soft: rgba(176, 156, 255, .18);--addai-brand-ring: rgba(176, 156, 255, .32);--addai-glass-fill: rgba(22, 24, 31, .62);--addai-glass-fill-strong: rgba(22, 24, 31, .88);--addai-glass-gloss: linear-gradient(180deg, rgba(255, 255, 255, .1) 0%, rgba(255, 255, 255, 0) 30%, rgba(255, 255, 255, 0) 70%, rgba(0, 0, 0, .18) 100%);--addai-glass-edge: rgba(255, 255, 255, .1);--addai-glass-edge-soft: rgba(255, 255, 255, .06);--addai-glass-inner: inset 0 1px 0 rgba(255, 255, 255, .1), inset 0 -1px 0 rgba(0, 0, 0, .3);--addai-fg: rgb(248, 250, 252);--addai-fg-strong: rgb(255, 255, 255);--addai-muted: rgb(176, 184, 200);--addai-muted-strong: rgb(206, 213, 226);--addai-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 16px 36px rgba(0, 0, 0, .5), 0 2px 6px rgba(0, 0, 0, .3);--addai-shadow-lifted: 0 1px 2px rgba(0, 0, 0, .4), 0 28px 60px rgba(0, 0, 0, .55), 0 4px 12px rgba(0, 0, 0, .35)}}.addai-root,.addai-root *{box-sizing:border-box}.addai-glyph{position:fixed;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;color:var(--addai-brand);background:var(--addai-glass-fill-strong);-webkit-backdrop-filter:var(--addai-glass-blur);backdrop-filter:var(--addai-glass-blur);border:1px solid var(--addai-glass-edge);border-radius:8px;cursor:pointer;z-index:2147483646;box-shadow:0 1px 2px #1018281f,var(--addai-glass-inner);transition:transform .18s cubic-bezier(.32,.72,0,1),box-shadow .18s ease,background .18s ease;padding:0;isolation:isolate;overflow:hidden}.addai-glyph:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(180deg,#ffffff73,#fff0 60%);pointer-events:none;z-index:0}.addai-glyph>svg{position:relative;z-index:1;width:14px;height:14px}.addai-glyph:hover{transform:scale(1.06);background:var(--addai-glass-fill-strong);box-shadow:0 1px 2px #1018281f,0 0 0 3px var(--addai-brand-ring),var(--addai-glass-inner)}.addai-glyph:active{transform:scale(.96)}.addai-glyph:focus-visible{outline:none;box-shadow:0 1px 2px #1018281f,0 0 0 3px var(--addai-brand-ring),var(--addai-glass-inner)}.addai-panel{position:fixed;width:320px;max-height:380px;background:var(--addai-glass-fill);-webkit-backdrop-filter:var(--addai-glass-blur-strong);backdrop-filter:var(--addai-glass-blur-strong);border:1px solid var(--addai-glass-edge);border-radius:var(--addai-radius);box-shadow:var(--addai-shadow-lifted),var(--addai-glass-inner);overflow:hidden;z-index:2147483647;display:flex;flex-direction:column;font-family:var(--addai-font);color:var(--addai-fg);isolation:isolate;animation:addai-panel-in .2s cubic-bezier(.32,.72,0,1)}.addai-panel:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:var(--addai-glass-gloss);pointer-events:none;z-index:0}.addai-panel>*{position:relative;z-index:1}@keyframes addai-panel-in{0%{opacity:0;transform:translateY(-6px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.addai-panel header{display:flex;align-items:center;gap:8px;padding:12px 16px;font-size:12px;font-weight:500;letter-spacing:-.005em;color:var(--addai-muted-strong);border-bottom:1px solid var(--addai-glass-edge-soft)}.addai-panel header .addai-mark{width:14px;height:14px;color:var(--addai-brand);display:inline-flex;align-items:center;justify-content:center}.addai-panel header .addai-mark svg{width:14px;height:14px}.addai-list{overflow:auto;padding:6px}.addai-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:var(--addai-radius-sm);cursor:pointer;font-size:13px;line-height:1.35;position:relative;transition:background-color .12s ease}.addai-row:hover,.addai-row[aria-selected=true]{background:var(--addai-brand-soft)}.addai-row[aria-selected=true]:before{content:"";position:absolute;left:4px;top:10px;bottom:10px;width:3px;border-radius:999px;background:var(--addai-brand)}.addai-avatar{flex-shrink:0;width:28px;height:28px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;background:var(--addai-brand-soft);color:var(--addai-brand-strong);font-weight:600;font-size:12px;letter-spacing:-.01em;border:1px solid var(--addai-glass-edge-soft)}.addai-row .addai-rowtext{min-width:0;flex:1 1 auto}.addai-row .addai-username{font-weight:600;font-size:13.5px;color:var(--addai-fg-strong);letter-spacing:-.005em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.addai-row .addai-meta{font-size:12px;color:var(--addai-muted);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.addai-panel footer{display:flex;align-items:center;gap:8px;padding:11px 16px;font-size:12.5px;font-weight:500;border-top:1px solid var(--addai-glass-edge-soft);color:var(--addai-brand);cursor:pointer;transition:background-color .12s ease}.addai-panel footer:hover{background:var(--addai-brand-soft)}.addai-panel footer .addai-plus{width:14px;height:14px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--addai-brand-soft);color:var(--addai-brand);font-weight:700;font-size:11px;line-height:1}.addai-toast{position:fixed;bottom:20px;right:20px;width:340px;padding:16px 18px;background:var(--addai-glass-fill);-webkit-backdrop-filter:var(--addai-glass-blur-strong);backdrop-filter:var(--addai-glass-blur-strong);border:1px solid var(--addai-glass-edge);border-radius:var(--addai-radius);box-shadow:var(--addai-shadow-lifted),var(--addai-glass-inner);z-index:2147483647;font:13px/1.4 var(--addai-font);color:var(--addai-fg);isolation:isolate;animation:addai-panel-in .22s cubic-bezier(.32,.72,0,1)}.addai-toast:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:var(--addai-glass-gloss);pointer-events:none;z-index:0;border-radius:inherit}.addai-toast>*{position:relative;z-index:1}.addai-toast .addai-toast-head{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:500;letter-spacing:-.005em;color:var(--addai-muted-strong);margin-bottom:10px}.addai-toast .addai-toast-head .addai-mark{color:var(--addai-brand);display:inline-flex}.addai-toast .addai-toast-head .addai-mark svg{width:14px;height:14px}.addai-toast h4{margin:0 0 4px;font-size:14px;font-weight:600;letter-spacing:-.005em;color:var(--addai-fg-strong)}.addai-toast p{margin:0 0 14px;color:var(--addai-muted);font-size:12.5px}.addai-toast .addai-actions{display:flex;gap:8px;justify-content:flex-end}.addai-toast button{font:500 12.5px/1.2 var(--addai-font);padding:8px 14px;border-radius:var(--addai-radius-sm);border:1px solid var(--addai-glass-edge);background:transparent;color:var(--addai-fg);cursor:pointer;transition:background-color .12s ease,border-color .12s ease,transform .12s ease}.addai-toast button:hover{background:#1018280a}.addai-toast button:active{transform:scale(.97)}.addai-toast button.primary{background:var(--addai-brand);border-color:var(--addai-brand);color:#fff;box-shadow:0 1px 2px #10182829,inset 0 1px #ffffff2e}.addai-toast button.primary:hover{background:var(--addai-brand-strong);border-color:var(--addai-brand-strong)}.addai-toast button.primary:focus-visible{outline:none;box-shadow:0 0 0 3px var(--addai-brand-ring),0 1px 2px #10182829}@media(prefers-color-scheme:dark){.addai-toast button:hover{background:#ffffff0f}.addai-toast button.primary{color:#fff}}.addai-pill{position:fixed;top:16px;right:16px;display:inline-flex;align-items:center;gap:8px;padding:7px 14px;background:var(--addai-glass-fill);-webkit-backdrop-filter:var(--addai-glass-blur);backdrop-filter:var(--addai-glass-blur);border:1px solid var(--addai-glass-edge);border-radius:var(--addai-radius-pill);box-shadow:var(--addai-shadow),var(--addai-glass-inner);font:500 12.5px/1.2 var(--addai-font);color:var(--addai-fg-strong);z-index:2147483647;transform:translateY(-6px);opacity:0;transition:transform .22s cubic-bezier(.32,.72,0,1),opacity .22s ease;isolation:isolate}.addai-pill:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;background:linear-gradient(180deg,#fff6,#fff0 60%);pointer-events:none;z-index:0}.addai-pill>*{position:relative;z-index:1}.addai-pill[data-visible=true]{transform:translateY(0);opacity:1}.addai-pill .addai-spinner{width:12px;height:12px;border:2px solid var(--addai-brand-soft);border-top-color:var(--addai-brand);border-radius:50%;animation:addai-spin .7s linear infinite}.addai-pill .addai-check{width:14px;height:14px;color:var(--addai-brand)}@keyframes addai-spin{to{transform:rotate(360deg)}}',H='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15" r="4"/><path d="M10.85 12.15 19 4"/><path d="m18 5 2 2"/><path d="m15 8 2 2"/></svg>',O=`<span class="addai-mark" aria-hidden="true">${H}</span>`,ba=a=>{const e=String(a).trim();if(!e)return"?";const t=e[0];return/[a-z0-9]/i.test(t)?t.toLowerCase():"?"};let u=null,g=null,b=null;const ma=()=>{if(u)return g;u=document.createElement("div"),u.setAttribute("data-addai-vault",""),u.style.cssText="all: initial; position: relative; z-index: 2147483647;",document.documentElement.appendChild(u),g=u.attachShadow({mode:"closed"});const a=chrome.runtime.getURL("assets/inter.woff2"),e=document.createElement("style");e.textContent=`
|
|
2
|
+
@font-face {
|
|
3
|
+
font-family: 'AddaiInter';
|
|
4
|
+
font-style: normal;
|
|
5
|
+
font-weight: 100 900;
|
|
6
|
+
font-display: swap;
|
|
7
|
+
src: url('${a}') format('woff2-variations');
|
|
8
|
+
}
|
|
9
|
+
`,g.appendChild(e);const t=document.createElement("style");t.textContent=ga,g.appendChild(t);const r=document.createElement("div");return r.className="addai-root",g.appendChild(r),g},A=()=>ma().querySelector(".addai-root"),m=a=>String(a??"").replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e]),ha=(a,e)=>{const t=A(),r=document.createElement("button");r.type="button",r.className="addai-glyph",r.innerHTML=H,r.setAttribute("aria-label","Sign in with +Ai Vault"),r.addEventListener("mousedown",o=>o.preventDefault()),r.addEventListener("click",o=>{o.preventDefault(),o.stopPropagation(),e()}),t.appendChild(r);const i=()=>{const o=a.getBoundingClientRect();r.style.top=`${o.top+(o.height-20)/2}px`,r.style.left=`${o.right-26}px`,r.style.display=o.width===0||o.height===0?"none":"inline-flex"};i();const n=new ResizeObserver(i);n.observe(a);const d=()=>i();return window.addEventListener("scroll",d,!0),window.addEventListener("resize",d),()=>{r.remove(),n.disconnect(),window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d)}},xa=(a,e)=>{const t=e.getBoundingClientRect(),i=window.innerHeight-t.bottom<200;a.style.left=`${Math.max(8,t.left)}px`,a.style.top=i?`${t.top-a.offsetHeight-4}px`:`${t.bottom+4}px`},F=a=>{b&&(a.composedPath().includes(u)||h())},h=()=>{b&&(b.remove(),b=null,document.removeEventListener("mousedown",F,!0))},va=(a,e,t)=>{const r=Array.from(a.querySelectorAll(".addai-row"));let i=0;const n=o=>{r.forEach(l=>l.removeAttribute("aria-selected")),i=(o+r.length)%r.length,r[i].setAttribute("aria-selected","true"),r[i].scrollIntoView({block:"nearest"})},d=o=>{if(!b){document.removeEventListener("keydown",d,!0);return}o.key==="ArrowDown"?(o.preventDefault(),n(i+1)):o.key==="ArrowUp"?(o.preventDefault(),n(i-1)):o.key==="Enter"?(o.preventDefault(),t(e[i])):o.key==="Escape"&&(o.preventDefault(),h())};document.addEventListener("keydown",d,!0)},wa=({anchor:a,items:e,onPick:t,onSaveNew:r})=>{h();const i=A(),n=document.createElement("div");n.className="addai-panel",n.setAttribute("role","listbox"),n.innerHTML=`
|
|
10
|
+
<header>${O}<span>+Ai vault</span></header>
|
|
11
|
+
<div class="addai-list"></div>
|
|
12
|
+
<footer><span class="addai-plus" aria-hidden="true">+</span><span>Save new credential</span></footer>
|
|
13
|
+
`;const d=n.querySelector(".addai-list");return e.forEach((o,l)=>{var W,X,J,Q,Z;const s=document.createElement("div");s.className="addai-row",s.setAttribute("role","option"),s.dataset.idx=String(l),l===0&&s.setAttribute("aria-selected","true");const c=((W=o.data)==null?void 0:W.username)||((X=o.data)==null?void 0:X.email)||o.name||"(saved login)",C=[(J=o.data)!=null&&J.username||(Q=o.data)!=null&&Q.email?o.name:null,(Z=o.shared_from_workspace)!=null&&Z.name?`from ${o.shared_from_workspace.name}`:null,o.domain||null,o.folderName||null].filter(Boolean);s.innerHTML=`
|
|
14
|
+
<div class="addai-avatar" aria-hidden="true">${m(ba(c))}</div>
|
|
15
|
+
<div class="addai-rowtext">
|
|
16
|
+
<div class="addai-username">${m(c)}</div>
|
|
17
|
+
<div class="addai-meta">${m(C.join(" · "))}</div>
|
|
18
|
+
</div>
|
|
19
|
+
`,s.addEventListener("mousedown",Da=>Da.preventDefault()),s.addEventListener("click",()=>t(o)),d.appendChild(s)}),n.querySelector("footer").addEventListener("click",()=>r()),i.appendChild(n),xa(n,a),va(n,e,t),document.addEventListener("mousedown",F,!0),b=n,n},ya=({domain:a,username:e,onSave:t,onDismiss:r})=>{const i=A(),n=document.createElement("div");n.className="addai-toast",n.innerHTML=`
|
|
20
|
+
<div class="addai-toast-head">${O}<span>+Ai vault</span></div>
|
|
21
|
+
<h4>Save this credential?</h4>
|
|
22
|
+
<p>${m(a)} · ${m(e||"(no username)")}</p>
|
|
23
|
+
<div class="addai-actions">
|
|
24
|
+
<button data-act="dismiss">Not now</button>
|
|
25
|
+
<button data-act="save" class="primary">Save to vault</button>
|
|
26
|
+
</div>
|
|
27
|
+
`,n.querySelector('[data-act="save"]').addEventListener("click",()=>{n.remove(),t()}),n.querySelector('[data-act="dismiss"]').addEventListener("click",()=>{n.remove(),r()}),i.appendChild(n),setTimeout(()=>{n.isConnected&&n.remove()},3e4)},ka='<svg class="addai-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="5 12 10 17 19 7"/></svg>';let p=null,S=null;const Ea=()=>{const a=A();return p&&p.isConnected||(p=document.createElement("div"),p.className="addai-pill",p.setAttribute("data-visible","false"),p.setAttribute("role","status"),p.setAttribute("aria-live","polite"),a.appendChild(p)),p},x=(a,e={})=>{if(S&&(clearTimeout(S),S=null),a==="hidden"){if(!p)return;p.setAttribute("data-visible","false"),S=setTimeout(()=>{p==null||p.remove(),p=null},220);return}const t=Ea();if(a==="thinking")t.innerHTML='<span class="addai-spinner" aria-hidden="true"></span><span>Reading form…</span>';else if(a==="recognized"){const r=e.count??0;t.innerHTML=`${ka}<span>Recognized ${r} field${r===1?"":"s"}</span>`}t.offsetWidth,t.setAttribute("data-visible","true")},Aa=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"value").set,Sa=(a,e)=>{a&&(Aa.call(a,e??""),a.dispatchEvent(new Event("input",{bubbles:!0})),a.dispatchEvent(new Event("change",{bubbles:!0})))};let j=!1;async function La(a){var n,d;if(j)return;const e=location.hostname;if(!e)return;let t;try{t=await a("keepalive:for-host",{host:e})}catch{return}const r=t!=null&&t.ok?(n=t.data)==null?void 0:n.credential:null,i=t!=null&&t.ok?(d=t.data)==null?void 0:d.provider:null;!r&&!i||(j=!0,_a(a,e,r,i))}function _a(a,e,t,r){const i=document.createElement("div");i.setAttribute("data-addai-keepalive",""),document.documentElement.appendChild(i);const n=i.attachShadow({mode:"closed"}),d=[];t&&d.push({key:"refresh",label:`Refresh “${L(t.name)}”`,busy:"Capturing session…",msg:"keepalive:capture",done:o=>`✓ Refreshed · ${o.count} cookies`}),r&&d.push({key:"create",label:t?`+ New ${L(r.label)}`:`Save ${L(r.label)} to Vault`,busy:"Creating credential…",msg:"keepalive:create",done:o=>`✓ Created “${L(o.name)}”`}),n.innerHTML=`
|
|
28
|
+
<style>
|
|
29
|
+
.wrap { position: fixed; bottom: 16px; right: 16px; z-index: 2147483647;
|
|
30
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
|
31
|
+
.card { display: inline-flex; flex-direction: column; gap: 6px;
|
|
32
|
+
background: #1d1f29; color: #fff; border: 1px solid #2d2f3c; border-radius: 14px;
|
|
33
|
+
padding: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.3); }
|
|
34
|
+
.head { display: flex; align-items: center; gap: 8px; padding: 2px 4px 0; }
|
|
35
|
+
.head .dot { width: 7px; height: 7px; border-radius: 50%; background: #34d399; flex: none; }
|
|
36
|
+
.head .t { font-size: 11px; opacity: .7; }
|
|
37
|
+
.head .x { margin-left: auto; opacity: .45; font-size: 11px; cursor: pointer; }
|
|
38
|
+
.head .x:hover { opacity: .9; }
|
|
39
|
+
.btn { display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
|
|
40
|
+
background: #2a2d3a; color: #fff; border: 1px solid #353848; border-radius: 999px;
|
|
41
|
+
padding: 8px 14px; font-size: 12px; line-height: 1; user-select: none;
|
|
42
|
+
transition: transform .1s ease, background .15s ease; }
|
|
43
|
+
.btn:hover { transform: translateY(-1px); background: #323548; }
|
|
44
|
+
.btn[disabled] { cursor: default; opacity: .8; }
|
|
45
|
+
.btn .bdot { width: 8px; height: 8px; border-radius: 50%; background: #34d399; flex: none; }
|
|
46
|
+
.btn.err .bdot { background: #f87171; }
|
|
47
|
+
</style>
|
|
48
|
+
<div class="wrap">
|
|
49
|
+
<div class="card">
|
|
50
|
+
<div class="head">
|
|
51
|
+
<span class="dot"></span>
|
|
52
|
+
<span class="t">+Ai Vault</span>
|
|
53
|
+
<span class="x" data-x>✕</span>
|
|
54
|
+
</div>
|
|
55
|
+
${d.map((o,l)=>`
|
|
56
|
+
<div class="btn" role="button" tabindex="0" data-i="${l}">
|
|
57
|
+
<span class="bdot"></span>
|
|
58
|
+
<span class="label">${o.label}</span>
|
|
59
|
+
</div>`).join("")}
|
|
60
|
+
</div>
|
|
61
|
+
</div>`,n.querySelector("[data-x]").addEventListener("click",()=>i.remove()),n.querySelectorAll(".btn").forEach(o=>{const l=d[Number(o.dataset.i)],s=o.querySelector(".label");o.addEventListener("click",async()=>{if(o.hasAttribute("disabled"))return;o.setAttribute("disabled",""),s.textContent=l.busy;const c=await a(l.msg,{host:e});c!=null&&c.ok?(s.innerHTML=l.done(c.data),setTimeout(()=>i.remove(),2800)):(o.classList.add("err"),s.textContent=c!=null&&c.error?`Failed: ${c.error}`:"Failed",o.removeAttribute("disabled"))})})}function L(a){return String(a).replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e])}const v="addai.suppress",B=new WeakSet,P=new WeakSet,I=new Map;let w=!0;const y=(a,e)=>new Promise(t=>{if(!w){t(null);return}try{chrome.runtime.sendMessage({type:a,payload:e},r=>{if(chrome.runtime.lastError){/context invalidated/i.test(chrome.runtime.lastError.message||"")&&(w=!1,T.disconnect()),t(null);return}t(r)})}catch(r){/context invalidated/i.test((r==null?void 0:r.message)||"")&&(w=!1,T.disconnect()),t(null)}}),U=async(a,e)=>{if(!w)return e;try{return await a()}catch{return e}},Ca=async a=>U(async()=>{const r=((await chrome.storage.local.get(v))[v]||{})[a];return r&&r>Date.now()},!1),za=async a=>U(async()=>{const t=(await chrome.storage.local.get(v))[v]||{};t[a]=Date.now()+1440*60*1e3,await chrome.storage.local.set({[v]:t})},void 0),Ma={password:["username","password"],credential:["username","password"]},Ia=a=>a?Array.isArray(a)?a.map(e=>(e==null?void 0:e.key)||(e==null?void 0:e.name)).filter(Boolean):typeof a=="object"?Object.keys(a):[]:[],Ta=a=>{const e=a!=null&&a.data?Object.keys(a.data).filter(n=>n!=="url"):[],t=Ia(a==null?void 0:a.field_schema).filter(n=>n!=="url"),r=Array.isArray(a==null?void 0:a.custom_fields)?a.custom_fields.map(n=>(n==null?void 0:n.key)||(n==null?void 0:n.name)).filter(Boolean):[],i=Ma[a==null?void 0:a.item_type]||[];return Array.from(new Set([...e,...t,...r,...i]))},$a=(a,e)=>Object.values(a).filter(t=>typeof t=="string").every(t=>e.elementById.has(t)),Y=a=>Object.values(a).filter(e=>typeof e=="string").length,V=(a,e,t)=>{const r=async n=>{h();const d=await y(k.ITEM_REVEAL,{itemId:n.id});if(!(d!=null&&d.ok))return;const o=d.data;for(const[l,s]of Object.entries(e)){if(!s)continue;const c=a.elementById.get(s);if(!c)continue;const C=o[l];typeof C=="string"&&Sa(c,C)}},i=n=>{wa({anchor:n,items:t,onPick:r,onSaveNew:()=>h()})};for(const[,n]of Object.entries(e)){if(!n)continue;const d=a.elementById.get(n);!d||B.has(d)||(B.add(d),ha(d,()=>i(d)))}},K=a=>{var t;const e=(t=a.anchor)==null?void 0:t.closest("form");!e||P.has(e)||(P.add(e),e.addEventListener("submit",async()=>{const r=window.location.hostname;if(!r||await Ca(r))return;const i=Array.from(e.querySelectorAll('input[type="password"]'))[0],n=i==null?void 0:i.value;if(!n||n.length<4)return;const d=a.anchor&&a.anchor!==i?a.anchor:Array.from(e.querySelectorAll('input[type="email"], input[type="text"]'))[0],o=(d==null?void 0:d.value)||"";ya({domain:r,username:o,onSave:async()=>{await y(k.ITEM_CREATE,{name:r,url:window.location.origin,username:o,password:n})},onDismiss:()=>za(r)})},{capture:!0}))},_=async()=>{var n,d,o;if(!w)return;const a=window.location.hostname;if(!a)return;const e=ca(document);if(!e.length)return;const t=await y(k.ITEMS_FOR_DOMAIN,{domain:a});if(!(t!=null&&t.ok)||!t.data.items.length)return;const r=t.data.items,i=Array.from(new Set(r.flatMap(Ta)));if(i.length)for(const l of e){if(I.has(l.signature)){V(l,I.get(l.signature),r),K(l);continue}let s=await pa(a,l.signature);if(s&&!$a(s,l)&&(s=null),s)await fa(a,l.signature),x("recognized",{count:Y(s)});else{x("thinking");const c=await y(k.INFER_FORM,{fields:i,inputs:l.inputs,itemId:r[0].id,context:{host:a,title:document.title.slice(0,120),formAction:((o=(d=(n=l.anchor)==null?void 0:n.form)==null?void 0:d.getAttribute("action"))==null?void 0:o.slice(0,200))||null}});if(!(c!=null&&c.ok)){x("hidden");continue}s=c.data.mapping,await ua(a,l.signature,s,i),x("recognized",{count:Y(s)})}I.set(l.signature,s),V(l,s,r),K(l),setTimeout(()=>x("hidden"),2e3)}},T=new MutationObserver(()=>{clearTimeout(_.__t),_.__t=setTimeout(_,350)}),G=async()=>{await _(),T.observe(document.body,{childList:!0,subtree:!0}),La(y).catch(()=>{})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",G):G()})();
|
|
62
|
+
//# sourceMappingURL=content.js.map
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "+Ai Vault",
|
|
4
|
+
"short_name": "+Ai Vault",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Autofill credentials from your +Ai Vault.",
|
|
7
|
+
"icons": {
|
|
8
|
+
"16": "icons/icon-16.png",
|
|
9
|
+
"32": "icons/icon-32.png",
|
|
10
|
+
"48": "icons/icon-48.png",
|
|
11
|
+
"128": "icons/icon-128.png"
|
|
12
|
+
},
|
|
13
|
+
"permissions": ["cookies", "storage", "activeTab", "scripting", "tabs"],
|
|
14
|
+
"host_permissions": ["https://*.add.ai/*", "<all_urls>"],
|
|
15
|
+
"background": {
|
|
16
|
+
"service_worker": "background.js",
|
|
17
|
+
"type": "module"
|
|
18
|
+
},
|
|
19
|
+
"content_scripts": [
|
|
20
|
+
{
|
|
21
|
+
"matches": ["<all_urls>"],
|
|
22
|
+
"js": ["content.js"],
|
|
23
|
+
"run_at": "document_idle",
|
|
24
|
+
"all_frames": false
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"action": {
|
|
28
|
+
"default_icon": {
|
|
29
|
+
"16": "icons/icon-16.png",
|
|
30
|
+
"32": "icons/icon-32.png"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"web_accessible_resources": [
|
|
34
|
+
{
|
|
35
|
+
"resources": ["assets/*"],
|
|
36
|
+
"matches": ["<all_urls>"]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
package/dist/capabilities.js
CHANGED
|
@@ -393,7 +393,8 @@ async function probeFlows() {
|
|
|
393
393
|
let sandbox = {
|
|
394
394
|
kind: 'not-loaded',
|
|
395
395
|
isolated: false,
|
|
396
|
-
note: '
|
|
396
|
+
note: 'Loads on the first run this machine takes — until then there is '
|
|
397
|
+
+ 'nothing to report.',
|
|
397
398
|
};
|
|
398
399
|
if ((0, engine_loader_1.engineLoaded)()) {
|
|
399
400
|
try {
|
package/dist/command-runner.js
CHANGED
|
@@ -66,6 +66,10 @@ const manager_1 = require("./desktop/manager");
|
|
|
66
66
|
const creds_1 = require("./desktop/creds");
|
|
67
67
|
const install_engine_1 = require("./desktop/install-engine");
|
|
68
68
|
const engine_1 = require("./desktop/engine");
|
|
69
|
+
const session_1 = require("./desktop/learn/session");
|
|
70
|
+
const browser_1 = require("./desktop/learn/browser");
|
|
71
|
+
const vault_extension_1 = require("./desktop/vault-extension");
|
|
72
|
+
const vault_session_1 = require("./desktop/vault-session");
|
|
69
73
|
const COMMAND_TIMEOUT_MS = 10 * 60 * 1000;
|
|
70
74
|
/** Own version, read the same way index.ts does. Resolved here rather than
|
|
71
75
|
* imported from index.ts, which already imports this module. */
|
|
@@ -577,8 +581,70 @@ async function runSetAutostart(cmd) {
|
|
|
577
581
|
const DESKTOP_KINDS = [
|
|
578
582
|
'desktop_create', 'desktop_start', 'desktop_stop',
|
|
579
583
|
'desktop_delete', 'desktop_rebuild', 'desktop_sync_logins',
|
|
580
|
-
'desktop_exec',
|
|
584
|
+
'desktop_exec', 'desktop_record_start', 'desktop_record_stop',
|
|
581
585
|
];
|
|
586
|
+
/** A desktop's browser has to be started with a debugging port for +Ai Learn
|
|
587
|
+
* to see which control was clicked, and with the vault extension loaded for
|
|
588
|
+
* logins to be fillable — both are read only at launch. Applying the wrapper
|
|
589
|
+
* whenever a desktop comes up means the fleet acquires them without anyone
|
|
590
|
+
* doing anything, and a rebuild cannot lose them. */
|
|
591
|
+
async function armBrowserForLearning(provider, row, onLog) {
|
|
592
|
+
try {
|
|
593
|
+
const { confDir } = (0, manager_1.ensureDirs)(row.id);
|
|
594
|
+
// The extension is staged first: the wrapper's --load-extension has to
|
|
595
|
+
// point at something that is already on disk, or the browser starts once
|
|
596
|
+
// without it and nobody knows why.
|
|
597
|
+
const vault = (0, vault_extension_1.ensureVaultExtension)(confDir);
|
|
598
|
+
if (vault.available)
|
|
599
|
+
await signDesktopIn(row, confDir, onLog);
|
|
600
|
+
const res = await (0, browser_1.ensureBrowserWrapper)(async (cmd) => {
|
|
601
|
+
const r = await (0, creds_1.execInDesktopCapture)(provider, row, cmd, 20_000);
|
|
602
|
+
return { ok: r.exitCode === 0, out: r.stdout.toString('utf8') };
|
|
603
|
+
}, confDir, vault.available ? vault_extension_1.EXTENSION_DIR : null);
|
|
604
|
+
if (res.changed) {
|
|
605
|
+
onLog(vault.available
|
|
606
|
+
? 'browser set up so lessons can watch it, with the +Ai Vault loaded\n'
|
|
607
|
+
: 'browser set up so lessons can watch it\n');
|
|
608
|
+
}
|
|
609
|
+
if (res.note)
|
|
610
|
+
onLog(`${res.note}\n`);
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
// Never fatal. A desktop that works but cannot be taught is much better
|
|
614
|
+
// than a create that fails over an optional feature.
|
|
615
|
+
onLog(`could not prepare the browser for lessons: ${err.message}\n`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/** Leave a live add.ai session where the desktop's browser will find it.
|
|
619
|
+
*
|
|
620
|
+
* An installed vault with nobody signed in is an empty vault, and "empty"
|
|
621
|
+
* reads as broken. This is best-effort by design: a desktop whose entity has
|
|
622
|
+
* browser login switched off, or which two entities share with no default,
|
|
623
|
+
* gets a working browser and no session rather than a failed start. */
|
|
624
|
+
async function signDesktopIn(row, confDir, onLog) {
|
|
625
|
+
try {
|
|
626
|
+
const t = token();
|
|
627
|
+
if (!t)
|
|
628
|
+
return;
|
|
629
|
+
const res = await (0, vault_session_1.ensureVaultSession)(confDir, (desktopId, rotate) => (0, supabase_client_1.rpc)('runtime_desktop_login', { p_token: t, p_desktop_id: desktopId, p_rotate: rotate }), row.id);
|
|
630
|
+
if (res.session) {
|
|
631
|
+
onLog(res.rotated
|
|
632
|
+
? `signed the browser in as ${res.session.entity_name ?? 'the entity'}\n`
|
|
633
|
+
: 'the browser\u2019s vault session is still good\n');
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
// Said out loud, because every one of these is a thing somebody can fix.
|
|
637
|
+
const why = {
|
|
638
|
+
no_entity_assigned: 'no entity is assigned to this desktop',
|
|
639
|
+
shared_desktop_no_default: 'more than one entity works here and none is the default, so the vault stays signed out',
|
|
640
|
+
browser_login_disabled: 'browser login is switched off for this entity',
|
|
641
|
+
};
|
|
642
|
+
onLog(`the vault is installed but signed out: ${why[res.reason ?? ''] ?? res.reason}\n`);
|
|
643
|
+
}
|
|
644
|
+
catch (err) {
|
|
645
|
+
onLog(`could not sign the browser in: ${err.message}\n`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
582
648
|
/** Setup scripts run as a FILE, never as a `sh -c` string: a script with
|
|
583
649
|
* quotes or newlines must not need escaping to survive. */
|
|
584
650
|
async function runSetupScript(provider, row, onLog) {
|
|
@@ -641,6 +707,7 @@ async function runDesktopCommand(cmd) {
|
|
|
641
707
|
vnc_password: vncPassword,
|
|
642
708
|
});
|
|
643
709
|
await (0, creds_1.syncCredentials)(provider, built, onLog);
|
|
710
|
+
await armBrowserForLearning(provider, built, onLog);
|
|
644
711
|
if (row.setup_script) {
|
|
645
712
|
onLog('running setup script\n');
|
|
646
713
|
try {
|
|
@@ -666,6 +733,35 @@ async function runDesktopCommand(cmd) {
|
|
|
666
733
|
await (0, manager_1.setStatus)(row.id, { status: 'starting' });
|
|
667
734
|
await provider.start(row);
|
|
668
735
|
await (0, manager_1.setStatus)(row.id, { status: 'running', status_message: null });
|
|
736
|
+
await armBrowserForLearning(provider, row, onLog);
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
case 'desktop_record_start': {
|
|
740
|
+
const recordingId = String(cmd.input?.recording_id ?? '');
|
|
741
|
+
if (!recordingId)
|
|
742
|
+
throw new Error('no recording on command');
|
|
743
|
+
const t = token();
|
|
744
|
+
const res = await (0, session_1.startLesson)({
|
|
745
|
+
provider, row, recordingId, onLog,
|
|
746
|
+
send: async (events) => {
|
|
747
|
+
if (!t)
|
|
748
|
+
return null;
|
|
749
|
+
return await (0, supabase_client_1.rpc)('desktop_recording_page_events', { p_token: t, p_recording_id: recordingId, p_events: events });
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
onLog(res.browserTrack
|
|
753
|
+
? 'watching this desktop\'s browser\n'
|
|
754
|
+
: `${res.note ?? 'the browser could not be watched'}\n`);
|
|
755
|
+
// The command completes immediately: the recorder outlives it, and the
|
|
756
|
+
// stop command is what ends it. A command left running for the length
|
|
757
|
+
// of a lesson would look like a hang and would block the queue.
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
case 'desktop_record_stop': {
|
|
761
|
+
const recordingId = String(cmd.input?.recording_id ?? '');
|
|
762
|
+
if (recordingId)
|
|
763
|
+
await (0, session_1.stopLesson)(recordingId);
|
|
764
|
+
onLog('stopped watching\n');
|
|
669
765
|
break;
|
|
670
766
|
}
|
|
671
767
|
case 'desktop_stop':
|
|
@@ -829,7 +925,18 @@ async function runInstallEngine(cmd) {
|
|
|
829
925
|
}
|
|
830
926
|
return;
|
|
831
927
|
}
|
|
832
|
-
|
|
928
|
+
let engine = await (0, docker_1.getProvider)(true);
|
|
929
|
+
// The mac installer lays the app down and stops — nothing is running yet.
|
|
930
|
+
// Opening it is the rest of the same job, not a second errand for someone
|
|
931
|
+
// who pressed one button, so finish it here rather than reporting a
|
|
932
|
+
// half-done install as a failure.
|
|
933
|
+
if (!engine && process.platform === 'darwin') {
|
|
934
|
+
onLog('\nDocker Desktop installs closed. Opening it…\n');
|
|
935
|
+
if (await (0, start_engine_1.startEngine)(onLog, async () => null)) {
|
|
936
|
+
(0, docker_1.resetProviderCache)();
|
|
937
|
+
engine = await (0, docker_1.getProvider)(true);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
833
940
|
if (!engine) {
|
|
834
941
|
await update(cmd.id, 'failed', { log }, 'Installed, but no engine is answering yet. On macOS, Docker Desktop has to be opened once before its daemon runs.');
|
|
835
942
|
return;
|
package/dist/desktop/docker.d.ts
CHANGED
|
@@ -3,7 +3,10 @@ import { DesktopProvider, EngineInfo, CreateOpts } from './provider';
|
|
|
3
3
|
export declare class CliProvider implements DesktopProvider {
|
|
4
4
|
readonly id: 'docker' | 'podman';
|
|
5
5
|
private version;
|
|
6
|
-
|
|
6
|
+
private binary;
|
|
7
|
+
/** `binary` defaults to the bare name for callers (and tests) that have no
|
|
8
|
+
* resolved path — on a machine where PATH is enough, they are the same. */
|
|
9
|
+
constructor(id: 'docker' | 'podman', version: string, binary?: string);
|
|
7
10
|
private run;
|
|
8
11
|
probe(): Promise<EngineInfo | null>;
|
|
9
12
|
create(row: DesktopRow, opts: CreateOpts, onLog: (c: string) => void): Promise<string>;
|
package/dist/desktop/docker.js
CHANGED
|
@@ -12,15 +12,19 @@ const engine_1 = require("./engine");
|
|
|
12
12
|
class CliProvider {
|
|
13
13
|
id;
|
|
14
14
|
version;
|
|
15
|
-
|
|
15
|
+
binary;
|
|
16
|
+
/** `binary` defaults to the bare name for callers (and tests) that have no
|
|
17
|
+
* resolved path — on a machine where PATH is enough, they are the same. */
|
|
18
|
+
constructor(id, version, binary = id) {
|
|
16
19
|
this.id = id;
|
|
17
20
|
this.version = version;
|
|
21
|
+
this.binary = binary;
|
|
18
22
|
}
|
|
19
23
|
run(args, onLog, timeoutMs = 15 * 60_000) {
|
|
20
24
|
return new Promise(resolve => {
|
|
21
25
|
let stdout = '', stderr = '';
|
|
22
26
|
try {
|
|
23
|
-
const inv = (0, win_1.resolveCliInvocation)(this.
|
|
27
|
+
const inv = (0, win_1.resolveCliInvocation)(this.binary, args);
|
|
24
28
|
const child = (0, child_process_1.spawn)(inv.file, inv.args, {
|
|
25
29
|
env: process.env, windowsHide: true, timeout: timeoutMs,
|
|
26
30
|
});
|
|
@@ -96,7 +100,7 @@ async function getProvider(force = false) {
|
|
|
96
100
|
// An engine whose daemon is not answering is not a provider: every call
|
|
97
101
|
// through it would fail at the socket. Studio reports the reason from the
|
|
98
102
|
// capability probe instead.
|
|
99
|
-
cached = info?.running ? new CliProvider(info.id, info.version) : null;
|
|
103
|
+
cached = info?.running ? new CliProvider(info.id, info.version, info.binary ?? info.id) : null;
|
|
100
104
|
return cached;
|
|
101
105
|
}
|
|
102
106
|
function resetProviderCache() { cached = undefined; }
|
package/dist/desktop/engine.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { EngineInfo } from './provider';
|
|
2
2
|
export declare function parseEngineVersion(stdout: string): string | null;
|
|
3
|
+
/**
|
|
4
|
+
* Where an engine's CLI actually is.
|
|
5
|
+
*
|
|
6
|
+
* Docker Desktop puts `docker` in /usr/local/bin, and Homebrew's podman in
|
|
7
|
+
* its own prefix — neither is on the PATH of a daemon launchd started, which
|
|
8
|
+
* hands a job `PATH=/usr/bin:/bin:/usr/sbin:/sbin`. Probing the bare name
|
|
9
|
+
* there reports "no container engine" on a machine that has one, and the
|
|
10
|
+
* Desktops tab goes dark for a reason nobody can see. Look properly.
|
|
11
|
+
*
|
|
12
|
+
* Windows keeps its own resolution (`where`, then the npm shim candidates)
|
|
13
|
+
* inside findCliBinary, so this is one call for both.
|
|
14
|
+
*/
|
|
15
|
+
export declare function engineBinary(id: 'docker' | 'podman'): string | null;
|
|
3
16
|
/** Why an installed engine still cannot run anything. The three causes are
|
|
4
17
|
* worth telling apart, because the fix for each is different and none of
|
|
5
18
|
* them is "install Docker". */
|
package/dist/desktop/engine.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseEngineVersion = parseEngineVersion;
|
|
4
|
+
exports.engineBinary = engineBinary;
|
|
4
5
|
exports.explainUnreachable = explainUnreachable;
|
|
5
6
|
exports.detectEngine = detectEngine;
|
|
6
7
|
// Which container engine is on this machine? Docker first (the verified
|
|
@@ -13,6 +14,33 @@ function parseEngineVersion(stdout) {
|
|
|
13
14
|
const m = /version\s+v?(\d+\.\d+\.\d+)/i.exec(stdout);
|
|
14
15
|
return m ? m[1] : null;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Where an engine's CLI actually is.
|
|
19
|
+
*
|
|
20
|
+
* Docker Desktop puts `docker` in /usr/local/bin, and Homebrew's podman in
|
|
21
|
+
* its own prefix — neither is on the PATH of a daemon launchd started, which
|
|
22
|
+
* hands a job `PATH=/usr/bin:/bin:/usr/sbin:/sbin`. Probing the bare name
|
|
23
|
+
* there reports "no container engine" on a machine that has one, and the
|
|
24
|
+
* Desktops tab goes dark for a reason nobody can see. Look properly.
|
|
25
|
+
*
|
|
26
|
+
* Windows keeps its own resolution (`where`, then the npm shim candidates)
|
|
27
|
+
* inside findCliBinary, so this is one call for both.
|
|
28
|
+
*/
|
|
29
|
+
function engineBinary(id) {
|
|
30
|
+
return (0, win_1.findCliBinary)(id, {
|
|
31
|
+
posixCandidates: [
|
|
32
|
+
`/usr/local/bin/${id}`,
|
|
33
|
+
`/opt/homebrew/bin/${id}`,
|
|
34
|
+
`/usr/bin/${id}`,
|
|
35
|
+
// Docker Desktop ships its own copy and only symlinks it into
|
|
36
|
+
// /usr/local/bin when the user lets it — this one is always there.
|
|
37
|
+
...(id === 'docker' ? ['/Applications/Docker.app/Contents/Resources/bin/docker'] : []),
|
|
38
|
+
],
|
|
39
|
+
win32Candidates: id === 'docker'
|
|
40
|
+
? ['C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe']
|
|
41
|
+
: [],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
16
44
|
/** Run a command and hand back exit code + output. */
|
|
17
45
|
function run(binary, args) {
|
|
18
46
|
return new Promise(resolve => {
|
|
@@ -58,16 +86,19 @@ function explainUnreachable(output, platform) {
|
|
|
58
86
|
*/
|
|
59
87
|
async function detectEngine() {
|
|
60
88
|
for (const id of ['docker', 'podman']) {
|
|
61
|
-
const
|
|
89
|
+
const bin = engineBinary(id);
|
|
90
|
+
if (!bin)
|
|
91
|
+
continue;
|
|
92
|
+
const v = await run(bin, ['--version']);
|
|
62
93
|
if (!v.ok)
|
|
63
94
|
continue;
|
|
64
95
|
const version = parseEngineVersion(v.out);
|
|
65
96
|
if (!version)
|
|
66
97
|
continue;
|
|
67
|
-
const info = await run(
|
|
98
|
+
const info = await run(bin, ['info', '--format', '{{.ServerVersion}}']);
|
|
68
99
|
if (info.ok)
|
|
69
|
-
return { id, version, running: true };
|
|
70
|
-
return { id, version, running: false, reason: explainUnreachable(info.out, process.platform) };
|
|
100
|
+
return { id, version, running: true, binary: bin };
|
|
101
|
+
return { id, version, running: false, binary: bin, reason: explainUnreachable(info.out, process.platform) };
|
|
71
102
|
}
|
|
72
103
|
return null;
|
|
73
104
|
}
|
|
@@ -6,8 +6,39 @@ export interface InstallPlan {
|
|
|
6
6
|
manual: string;
|
|
7
7
|
needsRoot: boolean;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Where Homebrew actually lives.
|
|
11
|
+
*
|
|
12
|
+
* `brew` is not on the PATH of a daemon that did not come from a login shell —
|
|
13
|
+
* launchd hands a job `PATH=/usr/bin:/bin:/usr/sbin:/sbin`, and neither
|
|
14
|
+
* Homebrew prefix is in it. Spawning the bare name then fails ENOENT and the
|
|
15
|
+
* install reports "brew is not available here" on a machine that has brew,
|
|
16
|
+
* which is a lie the user cannot act on. Look where it really is instead.
|
|
17
|
+
*/
|
|
18
|
+
export declare function findBrew(platform?: NodeJS.Platform): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Install Docker Desktop from Docker's own disk image.
|
|
21
|
+
*
|
|
22
|
+
* The path for a Mac with no Homebrew — which is most Macs. This is the same
|
|
23
|
+
* download the `docker` cask fetches, driven by the installer Docker ships
|
|
24
|
+
* inside the app, so it is the vendor's supported unattended install rather
|
|
25
|
+
* than something we invented.
|
|
26
|
+
*
|
|
27
|
+
* Root is needed for one step: putting the app in /Applications and laying
|
|
28
|
+
* down its privileged helper. `--user` is what stops that cost being paid
|
|
29
|
+
* twice — without it, the first launch stops at an admin prompt on a machine
|
|
30
|
+
* nobody is sitting at.
|
|
31
|
+
*/
|
|
32
|
+
export declare function macDmgScript(user: string): string;
|
|
33
|
+
/** The current login name, for the `--user` the Docker installer wants. Never
|
|
34
|
+
* root: under `sudo` that would hand the app to the wrong account. */
|
|
35
|
+
export declare function installingUser(env?: NodeJS.ProcessEnv): string;
|
|
36
|
+
/**
|
|
37
|
+
* Pure, so the choice is testable on any machine — the two impure lookups
|
|
38
|
+
* (is brew here, who are we) are parameters with defaults rather than calls
|
|
39
|
+
* inside the body.
|
|
40
|
+
*/
|
|
41
|
+
export declare function installPlan(platform: NodeJS.Platform, isRoot: boolean, brew?: string | null, user?: string): InstallPlan;
|
|
11
42
|
/** Docker is there but this user cannot reach its socket. The fix is group
|
|
12
43
|
* membership, not another install — and it needs root, so it takes the same
|
|
13
44
|
* password round trip. */
|