thinkpool-pair 0.7.449 → 0.7.451

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/launcher.mjs CHANGED
@@ -1 +1 @@
1
- import e from"node:os";import n from"node:fs";import t from"node:path";import{execSync as r}from"node:child_process";import{detectProvider as o,fetchModels as i,ANTHROPIC_COMPATIBLE as a,gatewayHint as s}from"./byok-detect.mjs";import{probeHermesRuntime as l}from"./hermes-probe.mjs";import{probeOpenCodeRuntime as c}from"./opencode-probe.mjs";import{darwinServiceRunning as p,serviceRuntimeVersion as d}from"./service.mjs";import{loadKeepAwakePreference as u}from"./keep-awake.mjs";const m=e.homedir(),h=t.join(m,".thinkpool-pair"),k=(()=>{try{return JSON.parse(n.readFileSync(new URL("./package.json",import.meta.url),"utf8")).version}catch{return null}})();export const KNOWN_AGENTS=[{label:"Claude Code",cmd:"claude",resume:["--continue"]},{label:"Codex",cmd:"codex"},{label:"Hermes",cmd:"thinkpool"},{label:"OpenCode",cmd:"opencode"}];function f(e){const r="win32"===process.platform?(process.env.PATHEXT||".EXE;.CMD;.BAT;.COM").split(";"):[""];for(const o of(process.env.PATH||"").split(t.delimiter))if(o)for(const i of r)try{if(n.existsSync(t.join(o,e+i)))return!0}catch{}return!1}function v(e){try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return null}}export function detectState(){const e=v(t.join(h,"auth.json")),o=v(t.join(h,"provider.json")),i=!!o?.provider&&"anthropic"!==o.provider,a=f("hermes"),s=f("thinkpool")&&l().available,g=f("opencode")&&c().available,y=function(e){const o=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";try{if("darwin"===process.platform){const e=r(`launchctl print gui/$(id -u)/${o}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return p(e)}return"linux"===process.platform?(r(`systemctl --user is-active --quiet ${o}.service`),!0):n.existsSync(function(e){const n=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";if("darwin"===process.platform)return t.join(m,"Library","LaunchAgents",`${n}.plist`);if("linux"===process.platform)return t.join(m,".config","systemd","user",`${n}.service`);const r=t.join(process.env.APPDATA||t.join(m,"AppData","Roaming"),"Microsoft","Windows","Start Menu","Programs","Startup");return t.join(r,`thinkpool-pair-${e||"account"}.cmd`)}(e))}catch{return!1}}(null),w=!!e?.reconnect_required;return{loggedIn:!!e?.refresh_token&&!w,reconnectRequired:w,email:e?.email||e?.user?.email||null,provider:i?`Custom (${o.baseUrl||"endpoint"})`:"Anthropic (default)",providerModel:i&&o.model||null,agents:KNOWN_AGENTS.filter(e=>f(e.cmd)&&("thinkpool"!==e.cmd||s)&&("opencode"!==e.cmd||g)),hermesInstalled:a,hermesReady:s,opencodeReady:g,accountSvc:y,keepAwake:u(),serviceVersion:y?d(null):null,platform:process.platform,cwd:process.cwd(),version:k}}const g=e=>`${e}`,y=e=>`${e}`,w=e=>`${e}`,b=e=>`${e}`,$=e=>`${e}`;export async function runLauncher({actions:e,io:n,state:t=detectState(),refresh:r=null}){const l=e=>({...e,agents:Array.isArray(e?.agents)?e.agents:[],loggedIn:!!e?.loggedIn,reconnectRequired:!!e?.reconnectRequired,accountSvc:!!e?.accountSvc,keepAwake:!0===e?.keepAwake,serviceVersion:"string"==typeof e?.serviceVersion&&e.serviceVersion?e.serviceVersion:null,platform:e?.platform||process.platform,provider:e?.provider||"Anthropic (default)",cwd:e?.cwd||process.cwd(),version:e?.version||k});t=l(t);const c=()=>{r&&(t=l(r()))},p=async(e,t,r=1)=>{t.forEach((e,t)=>n.print(` ${w(String(t+1))}) ${e.label}${e.hint?" "+g(e.hint):""}`));const o=await n.ask(`\n ${e} ${g(`[${r}]`)} ${w("▸")} `),i=parseInt(o,10);return Number.isInteger(i)&&i>=1&&i<=t.length?i-1:r-1},d=async(e,t=!0)=>{const r=(await n.ask(` ${e} ${g(t?"[Y/n]":"[y/N]")} ${w("▸")} `)).toLowerCase();return r?"y"===r[0]:t},u=()=>{n.print(""),n.print(" "+g("┌ ")+y("thinkpool-pair")+g(" "+"─".repeat(40))),n.print(` ${g("│ launcher ")} ${t.version?`v${t.version}`:"version unavailable"}`),n.print(` ${g("│ account ")} ${t.reconnectRequired?$("reconnect required"):t.loggedIn?b((t.email||"linked")+" ✓"):$("not linked")}`),n.print(` ${g("│ agents ")} ${t.agents.length?t.agents.map(e=>e.label).join(", "):$("none ready: install Claude Code, Codex, or Hermes")}`),n.print(` ${g("│ directory")} ${g(t.cwd)}`);const e=t.accountSvc?"win32"===t.platform?b("startup entry installed"):b("running"+(t.serviceVersion?` v${t.serviceVersion}`:" (version unavailable)")):"not installed";n.print(` ${g("│ bridge ")} ${e}`),n.print(` ${g("│ keep awake")} ${t.keepAwake?b("on")+g(", display may still dim"):"off"}`),n.print(" "+g("└"+"─".repeat(54))+"\n")},m=async()=>!!t.loggedIn||(n.print("\n "+$(t.reconnectRequired?"This bridge lost its thinkpool account link.":"You are not linked to a thinkpool account yet.")),await d(" Link this device now?",!0)&&(await e.login(),c()),!!t.loggedIn||(n.print(" "+$("login needed to serve your sessions — back to menu")),!1)),h=async()=>!!t.agents.length||(n.print("\n "+$("No coding agent is ready on this computer")),n.print(" thinkpool connects an agent already installed on this machine. It does not"),n.print(" include one. Install and configure at least one of:"),n.print(`\n ${w("Claude Code")} command: claude`),n.print(` ${w("Codex")} command: codex`),n.print(` ${w("Hermes")} command: hermes, then set up its thinkpool profile here`),n.print("\n "+y("BYOK does not replace the agent. It only gives an installed agent")),n.print(" credentials for a model provider."),n.print("\n Install one, then reopen this launcher."),!1),f=async()=>{const r=t.provider+(t.providerModel?`, model ${t.providerModel}`:"");n.print("\n "+g("Provider settings supply model credentials. Claude Code, Codex, or Hermes must still be installed locally."));const l=await p("\n provider",[{label:"Anthropic (default)",hint:"regular Claude login"},{label:"Paste a key, pick a model",hint:"auto-detects the provider · OpenRouter = any model"},{label:"Custom endpoint",hint:"enter a base url + token by hand"},{label:"← Back",hint:`current: ${r}`}],4);if(3===l)return;if(0===l)return await e.setProvider({kind:"anthropic"}),c(),void n.print("\n "+b("✓ back on regular Claude."));if(1===l){const t=(await n.ask(`\n Paste your API key ${g("(blank to cancel)")} ${w("▸")} `)).trim();if(!t)return void n.print(" "+$("cancelled — provider unchanged"));const r=o(t);if(!r.compatible)return void n.print("\n "+$(s(r.name)));const l=a[r.id];n.print(`\n ${b("✓ detected "+l.name)} ${g("— fetching models…")}`);const d=await i({modelsUrl:l.models,key:t,auth:l.auth});let u;if(0===d.length)u=(await n.ask(` Couldn't list models. Model id ${g("(blank = endpoint default)")} ${w("▸")} `)).trim()||void 0;else if(d.length<=40){const e=await p("\n model",d.map(e=>({label:e})),1);u=d[e]}else for(n.print(` ${g(d.length+" models available, e.g. "+d.slice(0,3).join(", ")+"…")}`);;){const e=(await n.ask(` Type a model id or part of it ${g("(blank to cancel)")} ${w("▸")} `)).trim();if(!e)return void n.print(" "+$("cancelled — provider unchanged"));const t=d.filter(n=>n.toLowerCase().includes(e.toLowerCase()));if(0!==t.length){if(1===t.length){u=t[0];break}if(t.length<=40){const e=await p("\n pick",t.map(e=>({label:e})),1);u=t[e];break}n.print(` ${$(t.length+" matches — narrow it down")}`)}else n.print(" "+$("no match — try again"))}return await e.setProvider({kind:"custom",base:l.base,token:t,model:u}),c(),void n.print(`\n ${b("✓ provider set.")}\n ${l.name}\n model: ${u||"(endpoint default)"}`)}const d=(await n.ask(`\n Base url ${g("(blank to cancel · SDK appends /v1/messages)")} ${w("▸")} `)).trim();if(!d)return void n.print(" "+$("cancelled — provider unchanged"));const u=(await n.ask(` API token ${g("(blank to cancel)")} ${w("▸")} `)).trim();if(!u)return void n.print(" "+$("cancelled — provider unchanged"));const m=(await n.ask(` Model ${g("(blank = endpoint default · e.g. glm-4.6)")} ${w("▸")} `)).trim();await e.setProvider({kind:"custom",base:d,token:u,model:m||void 0}),c(),n.print(`\n ${b("✓ provider set to custom.")}\n base url: ${t.provider}\n model: ${t.providerModel||"(endpoint default)"}`)},v=async()=>{for(;;){const r=[{key:"provider",label:"Provider",hint:`current: ${t.provider}`},{key:"account",label:"Account",hint:t.reconnectRequired?"reconnect required":t.loggedIn?`linked: ${t.email||"yes"}`:"not linked"},{key:"awake",label:"Keep computer awake",hint:(t.keepAwake?"ON":"off")+" · the screen can still dim, lock, and turn off"}];t.hermesInstalled&&r.push({key:"hermes",label:t.hermesReady?"Hermes profile":"Set up Hermes",hint:t.hermesReady?"isolated profile ready":"set up isolated profile + delegation guard"}),r.push({key:"back",label:"Back to main menu",hint:""});const o=r[await p("\n change",r,r.length)];if("back"===o.key)return;if("provider"===o.key)await f();else{if("account"===o.key)return await e.login(),void c();if("awake"===o.key)if(t.keepAwake){if(!1===await e.setKeepAwake({enabled:!1}))continue;t={...t,keepAwake:!1},c(),n.print("\n "+b("✓ keep-awake is off. The computer may sleep normally."))}else if(n.print("\n "+$("Important: keep-awake changes this computer’s power behavior")),n.print(" While the bridge runs, thinkpool will prevent the computer itself from"),n.print(" automatically sleeping. This can use substantially more battery and may"),n.print(" also block suspend requests on some Linux systems."),n.print("\n "+b("It does NOT keep the screen on. Display dimming, screen locking, and")),n.print(" display sleep continue to work normally. Closing a laptop lid may still"),n.print(" sleep it, depending on the operating system and power settings."),await d("\n Enable keep-awake while the thinkpool bridge runs?",!1)){if(!1===await e.setKeepAwake({enabled:!0}))continue;t={...t,keepAwake:!0},c(),n.print("\n "+b("✓ keep-awake is on. Your screen is still free to dim and turn off."))}else n.print("\n "+$("unchanged: keep-awake remains off."));else if("hermes"===o.key){const n=await p("\n Hermes profile",[{label:"Clone active profile",hint:"copies provider/config explicitly; fresh thinkpool session history"},{label:"Create clean profile",hint:"no copied credentials or bundled skills; configure provider afterward"},{label:"Back",hint:""}],3);2!==n&&(await e.setupHermes({mode:0===n?"clone":"clean"}),c())}}}};for(;;){u(),n.print(" "+y("What do you want to do?")+"\n");const r=t.accountSvc?["win32"===t.platform?{key:"restart",label:"Update startup entry",hint:"installs the latest version; relaunch the bridge window to apply"}:{key:"restart",label:"Restart & update bridge",hint:"install the latest published version; sessions resume"},{key:"uninstall",label:"Remove background service",hint:"stop serving your sessions on this device"}]:[{key:"serve",label:"Serve all my sessions",hint:"runs here, Ctrl-C stops it"},{key:"service",label:"Always-on background service",hint:"recommended — survives reboot, restarts on crash"}];r.push({key:"settings",label:"Settings",hint:`provider · account · keep-awake ${t.keepAwake?"ON":"off"}${t.hermesInstalled?" · Hermes profile":""}`}),r.push({key:"quit",label:"Quit",hint:""});const o=r[await p("choose",r)];if("quit"===o.key)return;if("serve"===o.key)await h()&&await m()&&await e.serveAccountForeground();else if("service"===o.key){if(await h()&&await m()){if(!0===await e.installService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}}else if("restart"===o.key){if(!0===await e.restartUpdateService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}else"uninstall"===o.key?(await e.uninstallService({room:null}),c()):"settings"===o.key&&await v();await n.ask("\n "+g("press Enter to return to the menu…"))}}
1
+ import e from"node:os";import n from"node:fs";import t from"node:path";import{execSync as r}from"node:child_process";import{detectProvider as o,fetchModels as i,ANTHROPIC_COMPATIBLE as a,gatewayHint as s}from"./byok-detect.mjs";import{probeHermesRuntime as l}from"./hermes-probe.mjs";import{probeOpenCodeRuntime as c}from"./opencode-probe.mjs";import{darwinServiceRunning as p,serviceRuntimeVersion as d}from"./service.mjs";import{hasSavedAccountLogin as u}from"./auth-store.mjs";import{loadKeepAwakePreference as m}from"./keep-awake.mjs";const h=e.homedir(),k=t.join(h,".thinkpool-pair"),f=(()=>{try{return JSON.parse(n.readFileSync(new URL("./package.json",import.meta.url),"utf8")).version}catch{return null}})();export const KNOWN_AGENTS=[{label:"Claude Code",cmd:"claude",resume:["--continue"]},{label:"Codex",cmd:"codex"},{label:"Hermes",cmd:"thinkpool"},{label:"OpenCode",cmd:"opencode"}];function v(e){const r="win32"===process.platform?(process.env.PATHEXT||".EXE;.CMD;.BAT;.COM").split(";"):[""];for(const o of(process.env.PATH||"").split(t.delimiter))if(o)for(const i of r)try{if(n.existsSync(t.join(o,e+i)))return!0}catch{}return!1}function g(e){try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return null}}export function detectState(){const e=g(t.join(k,"auth.json")),o=g(t.join(k,"provider.json")),i=!!o?.provider&&"anthropic"!==o.provider,a=v("hermes"),s=v("thinkpool")&&l().available,y=v("opencode")&&c().available,w=function(e){const o=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";try{if("darwin"===process.platform){const e=r(`launchctl print gui/$(id -u)/${o}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return p(e)}return"linux"===process.platform?(r(`systemctl --user is-active --quiet ${o}.service`),!0):n.existsSync(function(e){const n=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";if("darwin"===process.platform)return t.join(h,"Library","LaunchAgents",`${n}.plist`);if("linux"===process.platform)return t.join(h,".config","systemd","user",`${n}.service`);const r=t.join(process.env.APPDATA||t.join(h,"AppData","Roaming"),"Microsoft","Windows","Start Menu","Programs","Startup");return t.join(r,`thinkpool-pair-${e||"account"}.cmd`)}(e))}catch{return!1}}(null);return{loggedIn:u(e),reconnectRequired:!1,email:e?.email||e?.user?.email||null,provider:i?`Custom (${o.baseUrl||"endpoint"})`:"Anthropic (default)",providerModel:i&&o.model||null,agents:KNOWN_AGENTS.filter(e=>v(e.cmd)&&("thinkpool"!==e.cmd||s)&&("opencode"!==e.cmd||y)),hermesInstalled:a,hermesReady:s,opencodeReady:y,accountSvc:w,keepAwake:m(),serviceVersion:w?d(null):null,platform:process.platform,cwd:process.cwd(),version:f}}const y=e=>`${e}`,w=e=>`${e}`,b=e=>`${e}`,$=e=>`${e}`,A=e=>`${e}`;export async function runLauncher({actions:e,io:n,state:t=detectState(),refresh:r=null}){const l=e=>({...e,agents:Array.isArray(e?.agents)?e.agents:[],loggedIn:!!e?.loggedIn,reconnectRequired:!!e?.reconnectRequired,accountSvc:!!e?.accountSvc,keepAwake:!0===e?.keepAwake,serviceVersion:"string"==typeof e?.serviceVersion&&e.serviceVersion?e.serviceVersion:null,platform:e?.platform||process.platform,provider:e?.provider||"Anthropic (default)",cwd:e?.cwd||process.cwd(),version:e?.version||f});t=l(t);const c=()=>{r&&(t=l(r()))},p=async(e,t,r=1)=>{t.forEach((e,t)=>n.print(` ${b(String(t+1))}) ${e.label}${e.hint?" "+y(e.hint):""}`));const o=await n.ask(`\n ${e} ${y(`[${r}]`)} ${b("▸")} `),i=parseInt(o,10);return Number.isInteger(i)&&i>=1&&i<=t.length?i-1:r-1},d=async(e,t=!0)=>{const r=(await n.ask(` ${e} ${y(t?"[Y/n]":"[y/N]")} ${b("▸")} `)).toLowerCase();return r?"y"===r[0]:t},u=()=>{n.print(""),n.print(" "+y("┌ ")+w("thinkpool-pair")+y(" "+"─".repeat(40))),n.print(` ${y("│ launcher ")} ${t.version?`v${t.version}`:"version unavailable"}`),n.print(` ${y("│ account ")} ${t.reconnectRequired?A("reconnect required"):t.loggedIn?$((t.email||"linked")+" ✓"):A("not linked")}`),n.print(` ${y("│ agents ")} ${t.agents.length?t.agents.map(e=>e.label).join(", "):A("none ready: install Claude Code, Codex, or Hermes")}`),n.print(` ${y("│ directory")} ${y(t.cwd)}`);const e=t.accountSvc?"win32"===t.platform?$("startup entry installed"):$("running"+(t.serviceVersion?` v${t.serviceVersion}`:" (version unavailable)")):"not installed";n.print(` ${y("│ bridge ")} ${e}`),n.print(` ${y("│ keep awake")} ${t.keepAwake?$("on")+y(", display may still dim"):"off"}`),n.print(" "+y("└"+"─".repeat(54))+"\n")},m=async()=>!!t.loggedIn||(n.print("\n "+A(t.reconnectRequired?"This bridge lost its thinkpool account link.":"You are not linked to a thinkpool account yet.")),await d(" Link this device now?",!0)&&(await e.login(),c()),!!t.loggedIn||(n.print(" "+A("login needed to serve your sessions — back to menu")),!1)),h=async()=>!!t.agents.length||(n.print("\n "+A("No coding agent is ready on this computer")),n.print(" thinkpool connects an agent already installed on this machine. It does not"),n.print(" include one. Install and configure at least one of:"),n.print(`\n ${b("Claude Code")} command: claude`),n.print(` ${b("Codex")} command: codex`),n.print(` ${b("Hermes")} command: hermes, then set up its thinkpool profile here`),n.print("\n "+w("BYOK does not replace the agent. It only gives an installed agent")),n.print(" credentials for a model provider."),n.print("\n Install one, then reopen this launcher."),!1),k=async()=>{const r=t.provider+(t.providerModel?`, model ${t.providerModel}`:"");n.print("\n "+y("Provider settings supply model credentials. Claude Code, Codex, or Hermes must still be installed locally."));const l=await p("\n provider",[{label:"Anthropic (default)",hint:"regular Claude login"},{label:"Paste a key, pick a model",hint:"auto-detects the provider · OpenRouter = any model"},{label:"Custom endpoint",hint:"enter a base url + token by hand"},{label:"← Back",hint:`current: ${r}`}],4);if(3===l)return;if(0===l)return await e.setProvider({kind:"anthropic"}),c(),void n.print("\n "+$("✓ back on regular Claude."));if(1===l){const t=(await n.ask(`\n Paste your API key ${y("(blank to cancel)")} ${b("▸")} `)).trim();if(!t)return void n.print(" "+A("cancelled — provider unchanged"));const r=o(t);if(!r.compatible)return void n.print("\n "+A(s(r.name)));const l=a[r.id];n.print(`\n ${$("✓ detected "+l.name)} ${y("— fetching models…")}`);const d=await i({modelsUrl:l.models,key:t,auth:l.auth});let u;if(0===d.length)u=(await n.ask(` Couldn't list models. Model id ${y("(blank = endpoint default)")} ${b("▸")} `)).trim()||void 0;else if(d.length<=40){const e=await p("\n model",d.map(e=>({label:e})),1);u=d[e]}else for(n.print(` ${y(d.length+" models available, e.g. "+d.slice(0,3).join(", ")+"…")}`);;){const e=(await n.ask(` Type a model id or part of it ${y("(blank to cancel)")} ${b("▸")} `)).trim();if(!e)return void n.print(" "+A("cancelled — provider unchanged"));const t=d.filter(n=>n.toLowerCase().includes(e.toLowerCase()));if(0!==t.length){if(1===t.length){u=t[0];break}if(t.length<=40){const e=await p("\n pick",t.map(e=>({label:e})),1);u=t[e];break}n.print(` ${A(t.length+" matches — narrow it down")}`)}else n.print(" "+A("no match — try again"))}return await e.setProvider({kind:"custom",base:l.base,token:t,model:u}),c(),void n.print(`\n ${$("✓ provider set.")}\n ${l.name}\n model: ${u||"(endpoint default)"}`)}const d=(await n.ask(`\n Base url ${y("(blank to cancel · SDK appends /v1/messages)")} ${b("▸")} `)).trim();if(!d)return void n.print(" "+A("cancelled — provider unchanged"));const u=(await n.ask(` API token ${y("(blank to cancel)")} ${b("▸")} `)).trim();if(!u)return void n.print(" "+A("cancelled — provider unchanged"));const m=(await n.ask(` Model ${y("(blank = endpoint default · e.g. glm-4.6)")} ${b("▸")} `)).trim();await e.setProvider({kind:"custom",base:d,token:u,model:m||void 0}),c(),n.print(`\n ${$("✓ provider set to custom.")}\n base url: ${t.provider}\n model: ${t.providerModel||"(endpoint default)"}`)},v=async()=>{for(;;){const r=[{key:"provider",label:"Provider",hint:`current: ${t.provider}`},{key:"account",label:"Account",hint:t.reconnectRequired?"reconnect required":t.loggedIn?`linked: ${t.email||"yes"}`:"not linked"},{key:"awake",label:"Keep computer awake",hint:(t.keepAwake?"ON":"off")+" · the screen can still dim, lock, and turn off"}];t.hermesInstalled&&r.push({key:"hermes",label:t.hermesReady?"Hermes profile":"Set up Hermes",hint:t.hermesReady?"isolated profile ready":"set up isolated profile + delegation guard"}),r.push({key:"back",label:"Back to main menu",hint:""});const o=r[await p("\n change",r,r.length)];if("back"===o.key)return;if("provider"===o.key)await k();else{if("account"===o.key)return await e.login(),void c();if("awake"===o.key)if(t.keepAwake){if(!1===await e.setKeepAwake({enabled:!1}))continue;t={...t,keepAwake:!1},c(),n.print("\n "+$("✓ keep-awake is off. The computer may sleep normally."))}else if(n.print("\n "+A("Important: keep-awake changes this computer’s power behavior")),n.print(" While the bridge runs, thinkpool will prevent the computer itself from"),n.print(" automatically sleeping. This can use substantially more battery and may"),n.print(" also block suspend requests on some Linux systems."),n.print("\n "+$("It does NOT keep the screen on. Display dimming, screen locking, and")),n.print(" display sleep continue to work normally. Closing a laptop lid may still"),n.print(" sleep it, depending on the operating system and power settings."),await d("\n Enable keep-awake while the thinkpool bridge runs?",!1)){if(!1===await e.setKeepAwake({enabled:!0}))continue;t={...t,keepAwake:!0},c(),n.print("\n "+$("✓ keep-awake is on. Your screen is still free to dim and turn off."))}else n.print("\n "+A("unchanged: keep-awake remains off."));else if("hermes"===o.key){const n=await p("\n Hermes profile",[{label:"Clone active profile",hint:"copies provider/config explicitly; fresh thinkpool session history"},{label:"Create clean profile",hint:"no copied credentials or bundled skills; configure provider afterward"},{label:"Back",hint:""}],3);2!==n&&(await e.setupHermes({mode:0===n?"clone":"clean"}),c())}}}};for(;;){u(),n.print(" "+w("What do you want to do?")+"\n");const r=t.accountSvc?["win32"===t.platform?{key:"restart",label:"Update startup entry",hint:"installs the latest version; relaunch the bridge window to apply"}:{key:"restart",label:"Restart & update bridge",hint:"install the latest published version; sessions resume"},{key:"uninstall",label:"Remove background service",hint:"stop serving your sessions on this device"}]:[{key:"serve",label:"Serve all my sessions",hint:"runs here, Ctrl-C stops it"},{key:"service",label:"Always-on background service",hint:"recommended — survives reboot, restarts on crash"}];r.push({key:"settings",label:"Settings",hint:`provider · account · keep-awake ${t.keepAwake?"ON":"off"}${t.hermesInstalled?" · Hermes profile":""}`}),r.push({key:"quit",label:"Quit",hint:""});const o=r[await p("choose",r)];if("quit"===o.key)return;if("serve"===o.key)await h()&&await m()&&await e.serveAccountForeground();else if("service"===o.key){if(await h()&&await m()){if(!0===await e.installService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}}else if("restart"===o.key){if(!0===await e.restartUpdateService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}else"uninstall"===o.key?(await e.uninstallService({room:null}),c()):"settings"===o.key&&await v();await n.ask("\n "+y("press Enter to return to the menu…"))}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.449",
3
+ "version": "0.7.451",
4
4
  "description": "Connect Claude Code, Codex, or Hermes on your computer to a thinkpool Code room.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "format": 1,
3
3
  "package": "thinkpool-pair",
4
- "version": "0.7.449",
4
+ "version": "0.7.451",
5
5
  "kind": "minified",
6
6
  "minifiedModules": 150,
7
- "sourceModuleBytes": 1977347,
8
- "artifactModuleBytes": 903682,
7
+ "sourceModuleBytes": 1979634,
8
+ "artifactModuleBytes": 905362,
9
9
  "files": {
10
- "package.json": "f1ed6ad7eb2268a56226686c41cbc25a0a4a25e5c3506a6734ea4f151c6bea2e",
10
+ "package.json": "7d37a246a5a328a589b13a01d99463a13001d1348121d636fedf03b09334319c",
11
11
  "LICENSE": "8a030197ca1cad9669909ca14c9ad15cf48d9fe551da526ccaf1fca993eddb23",
12
- "bridge.mjs": "1bab3b6045603450a92eb3199568e05a8842a18dcfb31fdbe93d413c7fb1ae7f",
12
+ "bridge.mjs": "e77aa7b300fe32dfe8305a56bf5c75516388bbcfed8a4e92a63476070ebefc49",
13
13
  "bridge-service.sh": "b64f27c473ca8f3a49190fd08c23947ab14ca9958d70b6fa53c7bddc2be91403",
14
14
  "command-guidance.mjs": "b9b967b59ff4e77c9613abb0f28858bf3ac5b106a509e471039777b53bec3e29",
15
15
  "abort-turn-barrier.mjs": "3734a5b7e2e940ec6d7fa8b5b04935e1429a23d1bd9e92b2ac2a2ecd9f77c75e",
@@ -20,7 +20,7 @@
20
20
  "sdk-smoke.mjs": "106e480019d553d3274c1c95bdd3330b640d9ecd3560ca364517985e0bc53ab3",
21
21
  "sdk-admission.mjs": "eb33725cc33347b900c75289eae7917f6ce3526203711515aecd48d43b3c895d",
22
22
  "mcp-schema-fixture.mjs": "1db9bc9494fe02831cda004a0720d7f93191c39a8a8761452c7ac0d6d3329fee",
23
- "launcher.mjs": "a63c63492be0e97b41aae9c0ae24045903207a768c2cb86f8d7a9297d2741f1b",
23
+ "launcher.mjs": "876ad3e6715cf2253b894e2621f6f40328e3109ea733d7d8312f36d2fcb98697",
24
24
  "privacy-report.mjs": "b444610be6de8b3bcf0086d585594286ef000dad1e075df87013003a315bf211",
25
25
  "agent-visibility.mjs": "6afed5ce9120c4aaa7582d3cfb30c0658186a936ad1f87b1b01dfd20e988f839",
26
26
  "byok-detect.mjs": "f31f2ca426737581a97c391b3cecfc75f0207cfa31f51c6bf4a9148309c9625d",
@@ -94,7 +94,7 @@
94
94
  "scheduled-run-admission.mjs": "3a2098d904c3aff571bf314e0182d5636d25d242cef52207e70f9dc1618c94f5",
95
95
  "event-id.mjs": "09c75e7fc8da282fc00651add58e5d2f82d3020e0c4dddb9921250cf9a6fa8c0",
96
96
  "event-bounds.mjs": "aa8eb8517c4aa81b3378f71401ed39c550cdc80e28e3a57ab46d0a5ef5c7c9c1",
97
- "replay-transport.mjs": "f17bd7ac0aaaaf096d7969ea8c54ee9d084e366b62a561bf5f656ed30c010465",
97
+ "replay-transport.mjs": "51c954ffb8c46d03a3b0cdb7f290ce961bfa2dc8e0b320f1dfe6c1d588ca9534",
98
98
  "event-delivery-queue.mjs": "0ed00938cca30627896a944fbdf8da21f91341e6d96a3ca190777843b302025f",
99
99
  "transport-frame.mjs": "729d5a5de85a9f4bf026b8759eb2a6ca2ad408194e0c6176428f759197278cfd",
100
100
  "turn-admission.mjs": "d327a8a1226dfde72c71a2585f9e69230cb8cdfc978e78556241c3403156d06e",
@@ -154,8 +154,8 @@
154
154
  "service.mjs": "51472fdda8fd249620924c5b9515bec2d754483bebae29fbe0cb4b02a59ecbed",
155
155
  "supervisor-ready.mjs": "7d5e4ebe3c7a93e0888dd335fb4556a670e55a80a89b861c8a44feac159cd840",
156
156
  "presence.mjs": "2516ebb4474a8f45dd881530335c2174af9cde44be8eed6896ff8c62497b6e2b",
157
- "account.mjs": "06af4bbd7719cb1552f5ef445f7edb47c09dd4c807c812fe8610e0f2503900c9",
158
- "auth-store.mjs": "c4d41ee7cdc34c2002c6dd50d32e53ace0a3f03a116ffa930ed94a437e247625",
157
+ "account.mjs": "656468adba7f48ca2cd7304e93e7dc6f9317c7cb4da6e4901dd8aa57c17654d7",
158
+ "auth-store.mjs": "02eb51f7aa3f0a679ef772fd577c8599a43c2216b2f4dab1d40eea6497e62fc9",
159
159
  "key-shape.mjs": "a4113293d52eeba39b05e70a3e0345abc1ddc181b4f236b413bde0ae11a3f646",
160
160
  "supabase-key.mjs": "041bde6cf50b15f6426daaf738374abc9647861d8c1dd3d85cedc45684271888",
161
161
  "provider.mjs": "5a96b83d8aa65fb344c8e8ec5ffcb9cc360497925120cdf7b4ad26416d284790",
@@ -1 +1 @@
1
- export function requestedReplayIds(e,r,t){const n=Array.isArray(r)?new Set(r.filter(Boolean)):null;return[...e].filter(([e])=>!n||n.has(e)).sort(([e],[r])=>e===t?-1:r===t?1:0)}export function replayChunkRanges(e,{budget:t=15e4,maxChunks:n=14,sizeOf:o=r}={}){if(!Array.isArray(e)||!e.length)return[];const a=[];let i=e.length,l=i,s=0;for(let r=e.length-1;r>=0;r--){let u=1/0;try{u=o(e[r])}catch{u=1/0}if(!Number.isFinite(u))return[];if(l<i&&s+u>t){if(a.push([l,i]),a.length>=n)break;i=r+1,l=i,s=0}l=r,s+=u}return l<i&&a.length<n&&a.push([l,i]),a.reverse()}const e=()=>new Promise(e=>setTimeout(e,0)),r=e=>{try{return Buffer.byteLength(JSON.stringify(e))}catch{return 1/0}},t=e=>"ok"===e||!0===e?.success;export function createLatestReplayPump({send:n,yieldTurn:o=e,maxViewers:a=32,maxQueuedBytes:i=4194304,maxConcurrent:l=2,maxFrameBytes:s=262144,maxRetries:u=2,operationTimeoutMs:c=1e4,identifyViewer:d=e=>"symbol"==typeof e?e:String(e||"*"),onError:f=null}={}){if("function"!=typeof n)throw new TypeError("replay pump requires send(frame)");const g=new Map;let y=0,m=0,h=0;const p=Math.min(s,i),w=(e,r)=>new Promise((t,n)=>{const o=new AbortController;let a;const i=(e,o)=>{clearTimeout(a),r.removeEventListener("abort",l),e?n(e):t(o)},l=()=>{o.abort(),i(new Error("replay_cancelled"))};r.aborted?l():(r.addEventListener("abort",l,{once:!0}),a=setTimeout(()=>{o.abort(),i(new Error("replay_deadline"))},Math.max(1,c)),Promise.resolve().then(()=>{if(o.signal.aborted)throw new Error("replay_cancelled");return e(o.signal)}).then(e=>i(null,e),e=>i(e)))}),b=async(e,r,t,n=null)=>{if(g.get(e)===r){g.delete(e);try{await(f?.({viewer:e,reason:t,error:n}))}catch{}}},v=()=>{if(!(m>=l||h+p>i))for(const[e,r]of g)r.running||r.scheduled||!r.source||(r.scheduled=!0,Promise.resolve().then(()=>x(e,r)))},x=async(e,a)=>{if(a.scheduled=!1,g.get(e)!==a||!a.source||a.running||m>=l||h+p>i)return;a.running=!0,m+=1,h+=p;let s=0;const c=a.generation,d=a.controller.signal;try{const i=a.source,l=await w(()=>i.next(),d);if(c!==a.generation||g.get(e)!==a)return;if(l.done){const r=a.onDone;return a.source=null,a.onDone=null,r&&await w(()=>r(),d),void(g.get(e)===a&&c===a.generation&&g.delete(e))}const f=l.value,m=r(f);if(!Number.isFinite(m)||m>p)return void await b(e,a,"frame_too_large");s=m,y+=s;let h=!1,v=null;for(let e=0;e<=u&&c===a.generation;e++){try{h=t(await w(e=>n(f,{signal:e}),d))}catch(e){v=e}if(h)break;await o()}if(c!==a.generation||g.get(e)!==a)return;if(!h)return void await b(e,a,"send_failed",v);await o()}catch(r){c===a.generation&&await b(e,a,"source_failed",r)}finally{y-=s,h-=p,a.running=!1,m-=1,g.get(e)!==a||a.source||g.delete(e),v()}};return{canAccept(e){const r=d(e);return!!r&&(g.has(r)||g.size<a)},enqueue(e,r,t=null){const n=d(e);if(!n)return{accepted:!1,reason:"invalid_viewer"};let o,i=g.get(n);if(!i&&g.size>=a)return{accepted:!1,reason:"viewer_limit"};try{o=(e=>{const r="function"==typeof e?e():e;return r?.[Symbol.asyncIterator]?r[Symbol.asyncIterator]():r?.[Symbol.iterator]?r[Symbol.iterator]():[][Symbol.iterator]()})(r)}catch{return{accepted:!1,reason:"invalid_source"}}return i||(i={generation:0,source:null,onDone:null,running:!1,scheduled:!1},g.set(n,i)),i.controller?.abort(),i.controller=new AbortController,i.generation+=1,i.source=o,i.onDone="function"==typeof t?t:null,v(),{accepted:!0,generation:i.generation}},cancel(e){const r=d(e),t=g.get(r);return!!t&&(t.generation+=1,t.controller?.abort(),t.source=null,t.onDone=null,t.running||g.delete(r),!0)},pending:e=>g.has(d(e))?1:0,stats:()=>({viewers:g.size,queuedBytes:y,active:m,reservedBytes:h})}}
1
+ export function requestedReplayIds(e,t,r){const n=Array.isArray(t)?new Set(t.filter(Boolean)):null;return[...e].filter(([e])=>!n||n.has(e)).sort(([e],[t])=>e===r?-1:t===r?1:0)}export function replayViewerKey(e,t,r){const n="string"==typeof t?t.slice(0,128):"",o=Array.isArray(r)?[...new Set(r.filter(e=>"string"==typeof e))].sort():null;return JSON.stringify([e??null,n,o])}export function replayChunkRanges(e,{budget:r=15e4,maxChunks:n=14,sizeOf:o=t}={}){if(!Array.isArray(e)||!e.length)return[];const i=[];let a=e.length,l=a,s=0;for(let t=e.length-1;t>=0;t--){let u=1/0;try{u=o(e[t])}catch{u=1/0}if(!Number.isFinite(u))return[];if(l<a&&s+u>r){if(i.push([l,a]),i.length>=n)break;a=t+1,l=a,s=0}l=t,s+=u}return l<a&&i.length<n&&i.push([l,a]),i.reverse()}const e=()=>new Promise(e=>setTimeout(e,0)),t=e=>{try{return Buffer.byteLength(JSON.stringify(e))}catch{return 1/0}},r=e=>"ok"===e||!0===e?.success;export function createLatestReplayPump({send:n,yieldTurn:o=e,maxViewers:i=32,maxQueuedBytes:a=4194304,maxConcurrent:l=2,maxFrameBytes:s=262144,maxRetries:u=2,operationTimeoutMs:c=1e4,identifyViewer:g=e=>"symbol"==typeof e?e:String(e||"*"),onError:f=null}={}){if("function"!=typeof n)throw new TypeError("replay pump requires send(frame)");const d=new Map,y=new Map,p=(e,t,r)=>{t&&(y.set(JSON.stringify([String(e),String(t)]),{generation:r,until:Date.now()+6e4}),y.size>256&&y.delete(y.keys().next().value))};let w=0,m=0,h=0;const b=Math.min(s,a),v=(e,t)=>new Promise((r,n)=>{const o=new AbortController;let i;const a=(e,o)=>{clearTimeout(i),t.removeEventListener("abort",l),e?n(e):r(o)},l=()=>{o.abort(),a(new Error("replay_cancelled"))};t.aborted?l():(t.addEventListener("abort",l,{once:!0}),i=setTimeout(()=>{o.abort(),a(new Error("replay_deadline"))},Math.max(1,c)),Promise.resolve().then(()=>{if(o.signal.aborted)throw new Error("replay_cancelled");return e(o.signal)}).then(e=>a(null,e),e=>a(e)))}),S=async(e,t,r,n=null)=>{if(d.get(e)===t){d.delete(e);try{await(f?.({viewer:e,reason:r,error:n}))}catch{}}},x=()=>{if(!(m>=l||h+b>a))for(const[e,t]of d)t.running||t.scheduled||!t.source||(t.scheduled=!0,Promise.resolve().then(()=>_(e,t)))},_=async(e,i)=>{if(i.scheduled=!1,d.get(e)!==i||!i.source||i.running||m>=l||h+b>a)return;i.running=!0,m+=1,h+=b;let s=0;const c=i.generation,g=i.controller.signal;try{const a=i.source,l=await v(()=>a.next(),g);if(c!==i.generation||d.get(e)!==i)return;if(l.done){const t=i.onDone;return i.source=null,i.onDone=null,t&&await v(()=>t(),g),void(d.get(e)===i&&c===i.generation&&(p(e,i.requestId,c),d.delete(e)))}const f=l.value,y=t(f);if(!Number.isFinite(y)||y>b)return void await S(e,i,"frame_too_large");s=y,w+=s;let m=!1,h=null;for(let e=0;e<=u&&c===i.generation;e++){try{m=r(await v(e=>n(f,{signal:e}),g))}catch(e){h=e}if(m)break;await o()}if(c!==i.generation||d.get(e)!==i)return;if(!m)return void await S(e,i,"send_failed",h);await o()}catch(t){c===i.generation&&await S(e,i,"source_failed",t)}finally{w-=s,h-=b,i.running=!1,m-=1,d.get(e)!==i||i.source||d.delete(e),x()}};return{canAccept(e){const t=g(e);return!!t&&(d.has(t)||d.size<i)},enqueue(e,t,r=null,n=null){const o=g(e);if(!o)return{accepted:!1,reason:"invalid_viewer"};let a=d.get(o);if(n&&a?.requestId===n)return{accepted:!0,duplicate:!0,generation:a.generation};const l=n?JSON.stringify([String(o),String(n)]):null,s=Date.now();for(const[e,t]of y)t.until<=s&&y.delete(e);const u=l&&y.get(l);if(u)return{accepted:!0,duplicate:!0,generation:u.generation};if(!a&&d.size>=i)return{accepted:!1,reason:"viewer_limit"};let c;try{c=(e=>{const t="function"==typeof e?e():e;return t?.[Symbol.asyncIterator]?t[Symbol.asyncIterator]():t?.[Symbol.iterator]?t[Symbol.iterator]():[][Symbol.iterator]()})(t)}catch{return{accepted:!1,reason:"invalid_source"}}return a||(a={generation:0,source:null,onDone:null,running:!1,scheduled:!1},d.set(o,a)),a.controller?.abort(),a.controller=new AbortController,a.generation+=1,a.requestId=n,a.source=c,a.onDone="function"==typeof r?r:null,p(o,n,a.generation),x(),{accepted:!0,generation:a.generation}},cancel(e){const t=g(e),r=d.get(t);return!!r&&(r.generation+=1,r.controller?.abort(),r.source=null,r.onDone=null,r.running||d.delete(t),!0)},pending:e=>d.has(g(e))?1:0,stats:()=>({viewers:d.size,queuedBytes:w,active:m,reservedBytes:h})}}