@vpxa/aikit 0.1.206 → 0.1.208
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/package.json +3 -3
- package/packages/browser/dist/index.js +7 -7
- package/packages/server/dist/auth-lzZKfxlV.js +2 -0
- package/packages/server/dist/bin.js +8 -0
- package/packages/server/dist/config-PfoXsIC3.js +2 -0
- package/packages/server/dist/dashboard-static-CRfR1yKU.js +2 -0
- package/packages/server/dist/index.d.ts +4 -1
- package/packages/server/dist/index.js +1 -1
- package/packages/server/dist/resolve-sibling-ByoHo7Tp.js +2 -0
- package/packages/server/dist/routes-Afg7J7xK.js +6 -0
- package/packages/server/dist/server-BaMsrcyc.js +2996 -0
- package/packages/server/dist/server-BhQwVWsr.js +2997 -0
- package/packages/server/dist/settings-static-B3lnYvcb.js +2 -0
- package/packages/server/dist/supersession-BIV-v6JG.js +3 -0
- package/packages/server/dist/version-check-gazMo-D4.js +2 -0
- package/packages/server/package.json +14 -11
- package/packages/store/dist/index.d.ts +39 -1
- package/packages/store/dist/index.js +36 -26
- package/packages/tools/dist/index.d.ts +138 -63
- package/packages/tools/dist/index.js +71 -71
- package/scaffold/dist/definitions/plugins.mjs +1 -1
- package/scaffold/dist/definitions/policies.mjs +2 -0
- package/scaffold/scripts/compile-policies.mjs +31 -0
- package/packages/server/dist/server-DdnaotHn.js +0 -2992
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vpxa/aikit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.208",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local-first AI developer toolkit — knowledge base, code analysis, context management, and developer tools for LLM agents",
|
|
6
6
|
"license": "MIT",
|
|
@@ -111,8 +111,8 @@
|
|
|
111
111
|
"lint": "biome check .",
|
|
112
112
|
"lint:fix": "biome check --write .",
|
|
113
113
|
"format": "biome format --write .",
|
|
114
|
-
"start": "node packages/server/dist/
|
|
115
|
-
"start:http": "node packages/server/dist/
|
|
114
|
+
"start": "node packages/server/dist/bin.js",
|
|
115
|
+
"start:http": "node packages/server/dist/bin.js --transport http",
|
|
116
116
|
"test": "vitest run",
|
|
117
117
|
"verify-publish": "node scripts/verify-publish.mjs",
|
|
118
118
|
"release": "node scripts/release.mjs",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import{createRequire as e}from"node:module";import{createHash as t,randomUUID as n}from"node:crypto";import{homedir as r}from"node:os";import{join as i}from"node:path";import{execFileSync as a}from"node:child_process";import{existsSync as o,readdirSync as s}from"node:fs";import{Buffer as c}from"node:buffer";import{z as l}from"zod";var u=class{#e;#t;#n=0;#r=0;constructor(e=200){if(!Number.isInteger(e)||e<1)throw RangeError(`CircularBuffer capacity must be a positive integer.`);this.#e=e,this.#t=Array(e)}push(e){this.#t[this.#n]=e,this.#n=(this.#n+1)%this.#e,this.#r<this.#e&&(this.#r+=1)}toArray(){if(this.#r===0)return[];let e=(this.#n-this.#r+this.#e)%this.#e,t=[];for(let n=0;n<this.#r;n+=1)t.push(this.#t[(e+n)%this.#e]);return t}clear(){this.#t=Array(this.#e),this.#n=0,this.#r=0}get size(){return this.#r}get capacity(){return this.#e}isEmpty(){return this.#r===0}};const d=[`xhr`,`fetch`,`websocket`,`document`],f=[/^authorization$/i,/^cookie$/i,/^set-cookie$/i,/^x-api-key$/i,/^x-access-token$/i,/^x-auth-token$/i,/^x-session$/i,/^x-secret$/i,/^x-password$/i,/^proxy-authorization$/i,/^www-authenticate$/i,/^x-csrf-token$/i,/^x-xsrf-token$/i];function p(e,t){if(t?.showSensitive)return{...e};let n=[...f,...t?.additionalPatterns??[]],r={};for(let[t,i]of Object.entries(e))r[t]=n.some(e=>e.test(t))?`[REDACTED]`:i;return r}function m(e,t=512){if(e)return e.length<=t?e:`${e.slice(0,t)}...[truncated]`}function ee(){return{networkBuffer:new u(200),consoleBuffer:new u(1e3),networkEnabled:!1,consoleEnabled:!1,networkFilter:{},nextRequestSequence:0,pendingRequests:new Map}}function te(e,t){if(e.capacity===t)return e;let n=new u(t),r=e.toArray(),i=Math.max(0,r.length-t);for(let e of r.slice(i))n.push(e);return n}function ne(e){switch(e){case`error`:case`info`:case`log`:case`debug`:return e;case`warning`:return`warn`;default:return`log`}}function re(e){if(e)return Buffer.byteLength(e)<=5242880?m(e):void 0}function ie(e){if(!e)return{};let t=Buffer.byteLength(e);return{bodySize:t,bodyPreview:t<=5242880?m(e):void 0}}function ae(e,t){return!t.resourceTypes.has(e.resourceType)||t.urlPattern&&!t.urlPattern.test(e.url)?!1:!t.excludeUrls.some(t=>t.test(e.url))}function h(e){if(e.length>200)throw Error(`Network filter regex patterns must be 200 characters or fewer`);try{let t=new RegExp(e);return t.test(`a`.repeat(100)),t}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Invalid network filter regex pattern: ${t}`)}}function g(e={}){return{resourceTypes:new Set(e.resourceTypes??d),urlPattern:e.urlPattern?h(e.urlPattern):void 0,excludeUrls:(e.excludeUrls??[]).map(e=>h(e))}}function oe(e){return e.nextRequestSequence+=1,`req-${e.nextRequestSequence}`}function se(e,t,n){let r={request:t,previous:e.pendingRequestTail};e.pendingRequestTail?e.pendingRequestTail.next=r:e.pendingRequestHead=r,e.pendingRequestTail=r,e.pendingRequests.set(t,{entry:n,node:r})}function _(e,t){t.previous?t.previous.next=t.next:e.pendingRequestHead=t.next,t.next?t.next.previous=t.previous:e.pendingRequestTail=t.previous,delete t.previous,delete t.next}function ce(e){for(;e.pendingRequests.size>e.networkBuffer.capacity;){let t=e.pendingRequestHead;if(!t)return;_(e,t),e.pendingRequests.delete(t.request)}}function v(e){e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0}var le=class{states=new Map;getOrCreate(e){let t=this.states.get(e);if(t)return t;let n=ee();return this.states.set(e,n),n}get(e){return this.states.get(e)}remove(e){let t=this.states.get(e);t&&(t.networkEnabled=!1,t.consoleEnabled=!1,this.detachNetworkListeners(t),this.detachConsoleListeners(t),v(t),this.states.delete(e))}clear(){for(let e of this.states.values())e.networkEnabled=!1,e.consoleEnabled=!1,this.detachNetworkListeners(e),this.detachConsoleListeners(e),v(e);this.states.clear()}enableNetwork(e,t,n,r){let i=this.getOrCreate(e);i.networkEnabled&&this.disableNetwork(e,t),i.page=t,i.networkEnabled=!0,i.networkFilter=n?{...n}:{},i.compiledNetworkFilter=g(i.networkFilter),r?.bufferSize&&(i.networkBuffer=te(i.networkBuffer,r.bufferSize));let a=e=>{let t=i.compiledNetworkFilter??g(i.networkFilter),n={resourceType:e.resourceType(),url:e.url()};if(!ae(n,t))return;let a={...e.headers()},o=Date.now(),s={id:oe(i),url:n.url,method:e.method(),resourceType:n.resourceType,headers:r?.showSensitive?a:p(a),postData:r?.includeBody?re(e.postData()??void 0):void 0,timestamp:o};i.networkBuffer.push(s),se(i,e,s),ce(i)},o=async e=>{let t=e.request(),n=i.pendingRequests.get(t);if(!n)return;i.pendingRequests.delete(t),_(i,n.node);let a;if(r?.includeBody)try{a=ie(await e.text())}catch{a=void 0}let o={...e.headers()};n.entry.response={status:e.status(),statusText:e.statusText(),headers:r?.showSensitive?o:p(o),bodySize:a?.bodySize,bodyPreview:a?.bodyPreview,timing:Date.now()-n.entry.timestamp}};i.listenerRefs={...i.listenerRefs,requestHandler:a,responseHandler:o},this.ensurePageCloseHandler(e,i,t),t.on(`request`,a),t.on(`response`,o)}disableNetwork(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.networkEnabled=!1,this.detachNetworkListeners(n),v(n))}enableConsole(e,t,n){let r=this.getOrCreate(e);r.consoleEnabled&&this.disableConsole(e,t),r.page=t,r.consoleEnabled=!0,n?.bufferSize&&(r.consoleBuffer=te(r.consoleBuffer,n.bufferSize));let i=e=>{let t=e.location();r.consoleBuffer.push({level:ne(e.type()),text:e.text(),timestamp:Date.now(),location:t.url?{url:t.url,line:t.lineNumber,column:t.columnNumber}:void 0,args:e.args().map(e=>e.toString())})};r.listenerRefs={...r.listenerRefs,consoleHandler:i},this.ensurePageCloseHandler(e,r,t),t.on(`console`,i)}disableConsole(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.consoleEnabled=!1,this.detachConsoleListeners(n))}detachNetworkListeners(e){let t=e.page,n=e.listenerRefs?.requestHandler,r=e.listenerRefs?.responseHandler;t&&n&&t.off(`request`,n),t&&r&&t.off(`response`,r),e.listenerRefs&&(delete e.listenerRefs.requestHandler,delete e.listenerRefs.responseHandler),this.detachPageCloseHandlerIfUnused(e)}detachConsoleListeners(e){let t=e.page,n=e.listenerRefs?.consoleHandler;t&&n&&t.off(`console`,n),e.listenerRefs&&delete e.listenerRefs.consoleHandler,this.detachPageCloseHandlerIfUnused(e)}ensurePageCloseHandler(e,t,n){if(t.listenerRefs?.pageCloseHandler)return;let r=()=>{this.remove(e)};t.listenerRefs={...t.listenerRefs,pageCloseHandler:r},n.on(`close`,r)}detachPageCloseHandlerIfUnused(e){if(e.networkEnabled||e.consoleEnabled)return;let t=e.page,n=e.listenerRefs?.pageCloseHandler;t&&n&&t.off(`close`,n),e.listenerRefs&&delete e.listenerRefs.pageCloseHandler,e.listenerRefs&&!e.listenerRefs.requestHandler&&!e.listenerRefs.responseHandler&&!e.listenerRefs.consoleHandler&&!e.listenerRefs.pageCloseHandler&&delete e.listenerRefs}};let ue=null;function y(){return ue||=new le,ue}const de=e(import.meta.url);function fe(e){return typeof e==`string`?e.trim():e instanceof Buffer?e.toString(`utf8`).trim():``}function pe(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e}async function me(e){pe(e);let{chromium:t}=await import(`playwright-core`),n=t.executablePath();if(!n)throw Error(`Chromium executable not found in ${e}`);return n}function he(){return i(r(),`.aikit`,`browsers`)}function b(e){return e.browsersPath??process.env.PLAYWRIGHT_BROWSERS_PATH??he()}function x(e){return o(e)?s(e,{withFileTypes:!0}).some(e=>e.isDirectory()&&e.name.toLowerCase().startsWith(`chromium-`)):!1}async function S(e,t){let n=b(e);if(pe(n),t?.(`Using Chromium cache at ${n}`),x(n))return t?.(`Chromium already installed`),me(n);let r=i(de.resolve(`playwright-core`),`..`,`cli.js`);t?.(`Installing Chromium via playwright-core`);try{a(process.execPath,[r,`install`,`chromium`],{env:{...process.env,PLAYWRIGHT_BROWSERS_PATH:n},encoding:`utf8`,stdio:`pipe`})}catch(e){let t=e,n=fe(t.stderr),r=fe(t.stdout),i=n||r||t.message||`Unknown playwright install failure`;throw Error(`Failed to install Chromium: ${i}`)}return t?.(`Chromium install complete`),me(n)}function C(){return process.platform===`win32`||process.platform===`darwin`?!0:!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY)}function w(e){return e===`headless`||!C()?`headless`:e}function T(e){switch(e){case`headless`:return{headless:!0,args:[]};default:return{headless:!1,args:[]}}}var E=class{pages=new Map;registerPage(e,t,r,i){let a=n();return this.pages.set(a,{page:e,url:t,title:r,label:i,createdAt:new Date}),a}getPageByLabel(e){for(let t of this.pages.values())if(t.label===e)return t.page;throw Error(`Page not found: ${e}`)}resolvePageId(e){if(this.pages.has(e))return e;for(let[t,n]of this.pages.entries())if(n.label===e)return t;throw Error(`Page not found: ${e}`)}getPage(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.page}getPageInfo(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return{pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}}setSnapshot(e,t){let n=this.pages.get(e);if(!n)throw Error(`Page not found: ${e}`);this.pages.set(e,{...n,lastSnapshot:t})}getSnapshot(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.lastSnapshot}updatePageInfo(e,t,n){let r=this.pages.get(e);if(!r)throw Error(`Page not found: ${e}`);this.pages.set(e,{...r,url:t,title:n})}async removePage(e){let t=this.pages.get(e);t&&(await t.page.close(),this.pages.delete(e))}listPages(){return[...this.pages.entries()].map(([e,t])=>({pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}))}async closeAll(){await Promise.allSettled([...this.pages.keys()].map(async e=>this.removePage(e)))}get size(){return this.pages.size}};const D={defaultMode:`ui`,browsersPath:null,userDataDirRoot:null,idleShutdownMinutes:10,allowInternalSchemes:!1,evalTimeoutMs:1e4,evalMaxResultBytes:262144,redactPasswordFieldsInScreenshots:!0};var O=class{browser=null;context=null;idleTimer=null;launchPromise=null;currentMode=null;session=new E;config;constructor(e={}){this.config={...D,...e}}async launch(e,t){let n=w(e??this.config.defaultMode);if(this.context&&this.currentMode!==n)if(this.currentMode===`headless`&&n===`ui`)await this.close();else return;if(!this.context){if(this.launchPromise)return this.launchPromise;this.launchPromise=this.doLaunch(n,t);try{await this.launchPromise}finally{this.launchPromise=null}}}async doLaunch(e,n){if(this.browser&&this.context)return;let a=b(this.config);process.env.PLAYWRIGHT_BROWSERS_PATH=a;let{chromium:o}=await import(`playwright-core`),s=await S(this.config,n),c=T(e),l=t(`sha256`).update(process.cwd()).digest(`hex`).slice(0,12),u=i(this.config.userDataDirRoot??i(r(),`.aikit`,`profiles`),l);if(n?.(`Launching Chromium in ${e} mode`),this.context=await o.launchPersistentContext(u,{headless:c.headless,args:c.args,executablePath:s,viewport:null}),this.browser=this.context.browser(),!this.browser)throw await this.context.close(),this.context=null,Error(`Failed to acquire Chromium browser instance`);this.currentMode=e,this.browser.on(`disconnected`,()=>{this.browser=null,this.context=null,this.currentMode=null,y().clear(),this.session.closeAll().catch(()=>{}),this.stopIdleTimer()}),this.resetIdleTimer()}stopIdleTimer(){this.idleTimer&&=(clearTimeout(this.idleTimer),null)}resetIdleTimer(){this.stopIdleTimer();let e=setTimeout(()=>{this.close()},this.config.idleShutdownMinutes*6e4);e.unref?.(),this.idleTimer=e}async close(){this.stopIdleTimer(),this.launchPromise=null;let e=this.context,t=this.browser;this.context=null,this.browser=null,this.currentMode=null;let n;try{y().clear()}catch(e){n=e}try{await this.session.closeAll()}catch(e){n??=e}try{e&&await e.close()}catch(e){n??=e}finally{try{t&&await t.close()}catch(e){n??=e}}if(n)throw n}getContext(){if(!this.context)throw Error(`Browser not launched. Call launch() first.`);return this.context}getConfig(){return this.config}isLaunched(){return this.browser!==null}};let k=null;function A(e){return k||=new O(e),k}async function ge(){k&&=(await k.close(),null)}const _e=[`file:`,`chrome:`,`chrome-extension:`,`data:`,`javascript:`],ve=[`169.254.169.254`,`metadata.google.internal`,`metadata.google.com`],j=[`localhost`,`127.0.0.1`,`::1`,`[::1]`];function ye(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function be(e){let t=new WeakSet,n=JSON.stringify(e,(e,n)=>{if(typeof n==`bigint`)return n.toString();if(typeof n==`function`)return`[Function ${n.name||`anonymous`}]`;if(typeof n==`symbol`)return n.toString();if(n instanceof Error)return{name:n.name,message:n.message,stack:n.stack};if(typeof n==`object`&&n){if(t.has(n))return`[Circular]`;t.add(n)}return n});return n===void 0?e===void 0?`undefined`:String(e):n}function xe(e,t){let n=c.from(e,`utf8`);return n.byteLength<=t?e:n.subarray(0,t).toString(`utf8`)}function M(e,t){let n;try{n=new URL(e)}catch{return{allowed:!1,reason:`Invalid URL: ${e}`}}let r=n.protocol.toLowerCase();if(_e.includes(r)&&!t.allowInternalSchemes)return{allowed:!1,reason:`Blocked URL scheme: ${r}`};let i=ye(n.hostname);return ve.includes(i)?{allowed:!1,reason:`Blocked host: ${n.hostname}`}:(j.includes(i)||j.includes(n.hostname),{allowed:!0})}function N(e){return e?{allowed:!0}:{allowed:!1,reason:`Cookie access requires explicit confirmation (confirm: true)`}}function P(e,t){if(t<=0)return{valid:!1,reason:`maxBytes must be greater than 0`};try{let n=be(e);return c.byteLength(n,`utf8`)<=t?{valid:!0,result:n,truncated:!1}:{valid:!0,result:`${xe(n,Math.max(t-3,0))}...`,truncated:!0,reason:`Result exceeded ${t} bytes and was truncated`}}catch(e){return{valid:!1,reason:`Unable to serialize eval result: ${e instanceof Error?e.message:String(e)}`}}}function Se(e){return e.replace(/<input\b[^>]*>/gi,e=>/\btype\s*=\s*(["'])password\1/i.test(e)?/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i.test(e)?e.replace(/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`value="***"`):e.replace(/\s*\/?>$/,e=>` value="***"${e}`):e)}const Ce=[`ui`,`headless`];function we(e){return typeof e==`object`&&e?e:null}function F(e){return typeof e==`string`&&e.length>0?e:void 0}function I(e){return typeof e==`boolean`?e:void 0}function L(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function R(e){return typeof e==`string`&&Ce.includes(e)?e:void 0}function z(e){let t=process.env[e];if(!t)return;let n=Number(t);return Number.isFinite(n)?n:void 0}function Te(e){let t=we(e.browser)??{};return{...D,defaultMode:R(process.env.AIKIT_BROWSER_DEFAULT_MODE)??R(t.defaultMode)??D.defaultMode,browsersPath:F(process.env.AIKIT_BROWSER_PATH)??F(process.env.AIKIT_BROWSER_BROWSERS_PATH)??F(t.browsersPath)??D.browsersPath,userDataDirRoot:F(t.userDataDirRoot)??D.userDataDirRoot,idleShutdownMinutes:z(`AIKIT_BROWSER_IDLE_MINUTES`)??L(t.idleShutdownMinutes)??D.idleShutdownMinutes,allowInternalSchemes:I(t.allowInternalSchemes)??D.allowInternalSchemes,evalTimeoutMs:z(`AIKIT_BROWSER_EVAL_TIMEOUT_MS`)??L(t.evalTimeoutMs)??D.evalTimeoutMs,evalMaxResultBytes:L(t.evalMaxResultBytes)??D.evalMaxResultBytes,redactPasswordFieldsInScreenshots:I(t.redactPasswordFieldsInScreenshots)??D.redactPasswordFieldsInScreenshots}}function B(e,t){return{content:[{type:`text`,text:e}],structuredContent:t}}function V(e,t){let n=e.selector??e.ref??(e.element?`text=${e.element}`:void 0);if(!n)throw Error(`${t} requires selector, ref, or element`);return n}async function Ee(e,t,n){let r=null;try{return await Promise.race([e,new Promise((e,i)=>{r=setTimeout(()=>{i(Error(`${n} timed out after ${t}ms`))},t)})])}finally{r&&clearTimeout(r)}}l.string().describe(`Tracked browser page identifier`),l.enum([`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`]).describe(`Interaction kind: click, type, press, hover, drag, select, scroll, upload`),l.string().optional().describe(`Target ref alias`),l.string().optional().describe(`Playwright selector`),l.string().optional().describe(`Human-readable element label`),l.string().optional().describe(`Text to type`),l.string().optional().describe(`Key to press`),l.string().optional().describe(`Option value, drag target, scroll spec, or file path(s)`),l.string().optional().describe(`Drag source ref`),l.string().optional().describe(`Drag source selector`),l.string().optional().describe(`Drag target ref`),l.string().optional().describe(`Drag target selector`);function De(e){return async({pageId:t,kind:n,ref:r,selector:i,element:a,text:o,key:s,value:c,fromRef:l,fromSelector:u,toRef:d,toSelector:f})=>{let p=A(e),m=p.session.getPage(t);switch(n){case`click`:{let e=V({ref:r,selector:i,element:a},`browser_act(click)`);await m.click(e);break}case`type`:{let e=V({ref:r,selector:i,element:a},`browser_act(type)`);await m.fill(e,o??``);break}case`press`:{let e=V({ref:r,selector:i,element:a},`browser_act(press)`);if(!s)throw Error(`browser_act(press) requires key`);await m.press(e,s);break}case`hover`:{let e=V({ref:r,selector:i,element:a},`browser_act(hover)`);await m.hover(e);break}case`drag`:{let e=V({ref:l??r,selector:u??i,element:a},`browser_act(drag) source`),t=V({ref:d,selector:f??c,element:a},`browser_act(drag) target`);await m.dragAndDrop(e,t);break}case`select`:{let e=V({ref:r,selector:i,element:a},`browser_act(select)`);if(c===void 0)throw Error(`browser_act(select) requires value`);await m.selectOption(e,c);break}case`scroll`:{if(i||r||a){let e=V({ref:r,selector:i,element:a},`browser_act(scroll)`);await m.locator(e).scrollIntoViewIfNeeded();break}let e=c??`down 500`;await m.evaluate(e=>{if(e===`bottom`){window.scrollTo(0,document.body.scrollHeight);return}if(e===`top`){window.scrollTo(0,0);return}let t=e.split(` `),n=t[0]||`down`,r=Number.parseInt(t[1]||`500`,10),i=n===`left`?-r:n===`right`?r:0,a=n===`up`?-r:n===`down`?r:0;window.scrollBy(i,a)},e);break}case`upload`:{let e=V({ref:r,selector:i,element:a},`browser_act(upload)`);if(!c)throw Error(`value (file path or JSON array of paths) required for upload`);let t;try{let e=JSON.parse(c);t=Array.isArray(e)?e:[c]}catch{t=[c]}await m.locator(e).setInputFiles(t);break}}return p.resetIdleTimer(),B(`ok`,{ok:!0})}}function Oe(e,t){return t?e.filter(e=>e.level===t):e}function ke(e){return e.map(e=>{let t=e.location?` (${e.location.url}:${e.location.line})`:``;return`[${e.level.toUpperCase()}] ${e.text}${t}`}).join(`
|
|
2
|
-
`)}async function
|
|
1
|
+
import{createRequire as e}from"node:module";import{createHash as t,randomUUID as n}from"node:crypto";import{homedir as r}from"node:os";import{join as i}from"node:path";import{execFileSync as a}from"node:child_process";import{existsSync as o,readdirSync as s}from"node:fs";import{Buffer as c}from"node:buffer";import{z as l}from"zod";import u from"node:dns/promises";var d=class{#e;#t;#n=0;#r=0;constructor(e=200){if(!Number.isInteger(e)||e<1)throw RangeError(`CircularBuffer capacity must be a positive integer.`);this.#e=e,this.#t=Array(e)}push(e){this.#t[this.#n]=e,this.#n=(this.#n+1)%this.#e,this.#r<this.#e&&(this.#r+=1)}toArray(){if(this.#r===0)return[];let e=(this.#n-this.#r+this.#e)%this.#e,t=[];for(let n=0;n<this.#r;n+=1)t.push(this.#t[(e+n)%this.#e]);return t}clear(){this.#t=Array(this.#e),this.#n=0,this.#r=0}get size(){return this.#r}get capacity(){return this.#e}isEmpty(){return this.#r===0}};const f=[`xhr`,`fetch`,`websocket`,`document`],p=[/^authorization$/i,/^cookie$/i,/^set-cookie$/i,/^x-api-key$/i,/^x-access-token$/i,/^x-auth-token$/i,/^x-session$/i,/^x-secret$/i,/^x-password$/i,/^proxy-authorization$/i,/^www-authenticate$/i,/^x-csrf-token$/i,/^x-xsrf-token$/i];function m(e,t){if(t?.showSensitive)return{...e};let n=[...p,...t?.additionalPatterns??[]],r={};for(let[t,i]of Object.entries(e))r[t]=n.some(e=>e.test(t))?`[REDACTED]`:i;return r}function h(e,t=512){if(e)return e.length<=t?e:`${e.slice(0,t)}...[truncated]`}function ee(){return{networkBuffer:new d(200),consoleBuffer:new d(1e3),networkEnabled:!1,consoleEnabled:!1,networkFilter:{},nextRequestSequence:0,pendingRequests:new Map}}function g(e,t){if(e.capacity===t)return e;let n=new d(t),r=e.toArray(),i=Math.max(0,r.length-t);for(let e of r.slice(i))n.push(e);return n}function te(e){switch(e){case`error`:case`info`:case`log`:case`debug`:return e;case`warning`:return`warn`;default:return`log`}}function ne(e){if(e)return Buffer.byteLength(e)<=5242880?h(e):void 0}function re(e){if(!e)return{};let t=Buffer.byteLength(e);return{bodySize:t,bodyPreview:t<=5242880?h(e):void 0}}function ie(e,t){return!t.resourceTypes.has(e.resourceType)||t.urlPattern&&!t.urlPattern.test(e.url)?!1:!t.excludeUrls.some(t=>t.test(e.url))}function ae(e){if(e.length>200)throw Error(`Network filter regex patterns must be 200 characters or fewer`);try{let t=new RegExp(e);return t.test(`a`.repeat(100)),t}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Invalid network filter regex pattern: ${t}`)}}function oe(e={}){return{resourceTypes:new Set(e.resourceTypes??f),urlPattern:e.urlPattern?ae(e.urlPattern):void 0,excludeUrls:(e.excludeUrls??[]).map(e=>ae(e))}}function se(e){return e.nextRequestSequence+=1,`req-${e.nextRequestSequence}`}function ce(e,t,n){let r={request:t,previous:e.pendingRequestTail};e.pendingRequestTail?e.pendingRequestTail.next=r:e.pendingRequestHead=r,e.pendingRequestTail=r,e.pendingRequests.set(t,{entry:n,node:r})}function le(e,t){t.previous?t.previous.next=t.next:e.pendingRequestHead=t.next,t.next?t.next.previous=t.previous:e.pendingRequestTail=t.previous,delete t.previous,delete t.next}function ue(e){for(;e.pendingRequests.size>e.networkBuffer.capacity;){let t=e.pendingRequestHead;if(!t)return;le(e,t),e.pendingRequests.delete(t.request)}}function _(e){e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0}var de=class{states=new Map;getOrCreate(e){let t=this.states.get(e);if(t)return t;let n=ee();return this.states.set(e,n),n}get(e){return this.states.get(e)}remove(e){let t=this.states.get(e);t&&(t.networkEnabled=!1,t.consoleEnabled=!1,this.detachNetworkListeners(t),this.detachConsoleListeners(t),_(t),this.states.delete(e))}clear(){for(let e of this.states.values())e.networkEnabled=!1,e.consoleEnabled=!1,this.detachNetworkListeners(e),this.detachConsoleListeners(e),_(e);this.states.clear()}enableNetwork(e,t,n,r){let i=this.getOrCreate(e);i.networkEnabled&&this.disableNetwork(e,t),i.page=t,i.networkEnabled=!0,i.networkFilter=n?{...n}:{},i.compiledNetworkFilter=oe(i.networkFilter),r?.bufferSize&&(i.networkBuffer=g(i.networkBuffer,r.bufferSize));let a=e=>{let t=i.compiledNetworkFilter??oe(i.networkFilter),n={resourceType:e.resourceType(),url:e.url()};if(!ie(n,t))return;let a={...e.headers()},o=Date.now(),s={id:se(i),url:n.url,method:e.method(),resourceType:n.resourceType,headers:r?.showSensitive?a:m(a),postData:r?.includeBody?ne(e.postData()??void 0):void 0,timestamp:o};i.networkBuffer.push(s),ce(i,e,s),ue(i)},o=async e=>{let t=e.request(),n=i.pendingRequests.get(t);if(!n)return;i.pendingRequests.delete(t),le(i,n.node);let a;if(r?.includeBody)try{a=re(await e.text())}catch{a=void 0}let o={...e.headers()};n.entry.response={status:e.status(),statusText:e.statusText(),headers:r?.showSensitive?o:m(o),bodySize:a?.bodySize,bodyPreview:a?.bodyPreview,timing:Date.now()-n.entry.timestamp}};i.listenerRefs={...i.listenerRefs,requestHandler:a,responseHandler:o},this.ensurePageCloseHandler(e,i,t),t.on(`request`,a),t.on(`response`,o)}disableNetwork(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.networkEnabled=!1,this.detachNetworkListeners(n),_(n))}enableConsole(e,t,n){let r=this.getOrCreate(e);r.consoleEnabled&&this.disableConsole(e,t),r.page=t,r.consoleEnabled=!0,n?.bufferSize&&(r.consoleBuffer=g(r.consoleBuffer,n.bufferSize));let i=e=>{let t=e.location();r.consoleBuffer.push({level:te(e.type()),text:e.text(),timestamp:Date.now(),location:t.url?{url:t.url,line:t.lineNumber,column:t.columnNumber}:void 0,args:e.args().map(e=>e.toString())})};r.listenerRefs={...r.listenerRefs,consoleHandler:i},this.ensurePageCloseHandler(e,r,t),t.on(`console`,i)}disableConsole(e,t){let n=this.states.get(e);n&&(n.page=n.page??t,n.consoleEnabled=!1,this.detachConsoleListeners(n))}detachNetworkListeners(e){let t=e.page,n=e.listenerRefs?.requestHandler,r=e.listenerRefs?.responseHandler;t&&n&&t.off(`request`,n),t&&r&&t.off(`response`,r),e.listenerRefs&&(delete e.listenerRefs.requestHandler,delete e.listenerRefs.responseHandler),this.detachPageCloseHandlerIfUnused(e)}detachConsoleListeners(e){let t=e.page,n=e.listenerRefs?.consoleHandler;t&&n&&t.off(`console`,n),e.listenerRefs&&delete e.listenerRefs.consoleHandler,this.detachPageCloseHandlerIfUnused(e)}ensurePageCloseHandler(e,t,n){if(t.listenerRefs?.pageCloseHandler)return;let r=()=>{this.remove(e)};t.listenerRefs={...t.listenerRefs,pageCloseHandler:r},n.on(`close`,r)}detachPageCloseHandlerIfUnused(e){if(e.networkEnabled||e.consoleEnabled)return;let t=e.page,n=e.listenerRefs?.pageCloseHandler;t&&n&&t.off(`close`,n),e.listenerRefs&&delete e.listenerRefs.pageCloseHandler,e.listenerRefs&&!e.listenerRefs.requestHandler&&!e.listenerRefs.responseHandler&&!e.listenerRefs.consoleHandler&&!e.listenerRefs.pageCloseHandler&&delete e.listenerRefs}};let v=null;function y(){return v||=new de,v}const fe=e(import.meta.url);function b(e){return typeof e==`string`?e.trim():e instanceof Buffer?e.toString(`utf8`).trim():``}function x(e){process.env.PLAYWRIGHT_BROWSERS_PATH=e}async function S(e){x(e);let{chromium:t}=await import(`playwright-core`),n=t.executablePath();if(!n)throw Error(`Chromium executable not found in ${e}`);return n}function pe(){return i(r(),`.aikit`,`browsers`)}function C(e){return e.browsersPath??process.env.PLAYWRIGHT_BROWSERS_PATH??pe()}function me(e){return o(e)?s(e,{withFileTypes:!0}).some(e=>e.isDirectory()&&e.name.toLowerCase().startsWith(`chromium-`)):!1}async function he(e,t){let n=C(e);if(x(n),t?.(`Using Chromium cache at ${n}`),me(n))return t?.(`Chromium already installed`),S(n);let r=i(fe.resolve(`playwright-core`),`..`,`cli.js`);t?.(`Installing Chromium via playwright-core`);try{a(process.execPath,[r,`install`,`chromium`],{env:{...process.env,PLAYWRIGHT_BROWSERS_PATH:n},encoding:`utf8`,stdio:`pipe`})}catch(e){let t=e,n=b(t.stderr),r=b(t.stdout),i=n||r||t.message||`Unknown playwright install failure`;throw Error(`Failed to install Chromium: ${i}`)}return t?.(`Chromium install complete`),S(n)}function ge(){return process.platform===`win32`||process.platform===`darwin`?!0:!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY)}function w(e){return e===`headless`||!ge()?`headless`:e}function T(e){switch(e){case`headless`:return{headless:!0,args:[]};default:return{headless:!1,args:[]}}}var E=class{pages=new Map;registerPage(e,t,r,i){let a=n();return this.pages.set(a,{page:e,url:t,title:r,label:i,createdAt:new Date}),a}getPageByLabel(e){for(let t of this.pages.values())if(t.label===e)return t.page;throw Error(`Page not found: ${e}`)}resolvePageId(e){if(this.pages.has(e))return e;for(let[t,n]of this.pages.entries())if(n.label===e)return t;throw Error(`Page not found: ${e}`)}getPage(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.page}getPageInfo(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return{pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}}setSnapshot(e,t){let n=this.pages.get(e);if(!n)throw Error(`Page not found: ${e}`);this.pages.set(e,{...n,lastSnapshot:t})}getSnapshot(e){let t=this.pages.get(e);if(!t)throw Error(`Page not found: ${e}`);return t.lastSnapshot}updatePageInfo(e,t,n){let r=this.pages.get(e);if(!r)throw Error(`Page not found: ${e}`);this.pages.set(e,{...r,url:t,title:n})}async removePage(e){let t=this.pages.get(e);t&&(await t.page.close(),this.pages.delete(e))}listPages(){return[...this.pages.entries()].map(([e,t])=>({pageId:e,url:t.url,title:t.title,label:t.label,createdAt:t.createdAt}))}async closeAll(){await Promise.allSettled([...this.pages.keys()].map(async e=>this.removePage(e)))}get size(){return this.pages.size}};const D={defaultMode:`ui`,browsersPath:null,userDataDirRoot:null,idleShutdownMinutes:10,allowInternalSchemes:!1,evalTimeoutMs:1e4,evalMaxResultBytes:262144,redactPasswordFieldsInScreenshots:!0};var O=class{browser=null;context=null;idleTimer=null;launchPromise=null;currentMode=null;session=new E;config;constructor(e={}){this.config={...D,...e}}async launch(e,t){let n=w(e??this.config.defaultMode);if(this.context&&this.currentMode!==n)if(this.currentMode===`headless`&&n===`ui`)await this.close();else return;if(!this.context){if(this.launchPromise)return this.launchPromise;this.launchPromise=this.doLaunch(n,t);try{await this.launchPromise}finally{this.launchPromise=null}}}async doLaunch(e,n){if(this.browser&&this.context)return;let a=C(this.config);process.env.PLAYWRIGHT_BROWSERS_PATH=a;let{chromium:o}=await import(`playwright-core`),s=await he(this.config,n),c=T(e),l=t(`sha256`).update(process.cwd()).digest(`hex`).slice(0,12),u=i(this.config.userDataDirRoot??i(r(),`.aikit`,`profiles`),l);if(n?.(`Launching Chromium in ${e} mode`),this.context=await o.launchPersistentContext(u,{headless:c.headless,args:c.args,executablePath:s,viewport:null}),this.browser=this.context.browser(),!this.browser)throw await this.context.close(),this.context=null,Error(`Failed to acquire Chromium browser instance`);this.currentMode=e,this.browser.on(`disconnected`,()=>{this.browser=null,this.context=null,this.currentMode=null,y().clear(),this.session.closeAll().catch(()=>{}),this.stopIdleTimer()}),this.resetIdleTimer()}stopIdleTimer(){this.idleTimer&&=(clearTimeout(this.idleTimer),null)}resetIdleTimer(){this.stopIdleTimer();let e=setTimeout(()=>{this.close()},this.config.idleShutdownMinutes*6e4);e.unref?.(),this.idleTimer=e}async close(){this.stopIdleTimer(),this.launchPromise=null;let e=this.context,t=this.browser;this.context=null,this.browser=null,this.currentMode=null;let n;try{y().clear()}catch(e){n=e}try{await this.session.closeAll()}catch(e){n??=e}try{e&&await e.close()}catch(e){n??=e}finally{try{t&&await t.close()}catch(e){n??=e}}if(n)throw n}getContext(){if(!this.context)throw Error(`Browser not launched. Call launch() first.`);return this.context}getConfig(){return this.config}isLaunched(){return this.browser!==null}};let k=null;function A(e){return k||=new O(e),k}async function _e(){k&&=(await k.close(),null)}const ve=[`file:`,`chrome:`,`chrome-extension:`,`data:`,`javascript:`],ye=[`169.254.169.254`,`metadata.google.internal`,`metadata.google.com`],j=[`localhost`,`127.0.0.1`,`::1`,`[::1]`];function be(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function xe(e){let t=new WeakSet,n=JSON.stringify(e,(e,n)=>{if(typeof n==`bigint`)return n.toString();if(typeof n==`function`)return`[Function ${n.name||`anonymous`}]`;if(typeof n==`symbol`)return n.toString();if(n instanceof Error)return{name:n.name,message:n.message,stack:n.stack};if(typeof n==`object`&&n){if(t.has(n))return`[Circular]`;t.add(n)}return n});return n===void 0?e===void 0?`undefined`:String(e):n}function Se(e,t){let n=c.from(e,`utf8`);return n.byteLength<=t?e:n.subarray(0,t).toString(`utf8`)}function M(e,t){let n;try{n=new URL(e)}catch{return{allowed:!1,reason:`Invalid URL: ${e}`}}let r=n.protocol.toLowerCase();if(ve.includes(r)&&!t.allowInternalSchemes)return{allowed:!1,reason:`Blocked URL scheme: ${r}`};let i=be(n.hostname);return ye.includes(i)?{allowed:!1,reason:`Blocked host: ${n.hostname}`}:(j.includes(i)||j.includes(n.hostname),{allowed:!0})}function N(e){return e?{allowed:!0}:{allowed:!1,reason:`Cookie access requires explicit confirmation (confirm: true)`}}function P(e,t){if(t<=0)return{valid:!1,reason:`maxBytes must be greater than 0`};try{let n=xe(e);return c.byteLength(n,`utf8`)<=t?{valid:!0,result:n,truncated:!1}:{valid:!0,result:`${Se(n,Math.max(t-3,0))}...`,truncated:!0,reason:`Result exceeded ${t} bytes and was truncated`}}catch(e){return{valid:!1,reason:`Unable to serialize eval result: ${e instanceof Error?e.message:String(e)}`}}}function Ce(e){return e.replace(/<input\b[^>]*>/gi,e=>/\btype\s*=\s*(["'])password\1/i.test(e)?/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i.test(e)?e.replace(/\bvalue\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`value="***"`):e.replace(/\s*\/?>$/,e=>` value="***"${e}`):e)}const we=[`ui`,`headless`];function Te(e){return typeof e==`object`&&e?e:null}function F(e){return typeof e==`string`&&e.length>0?e:void 0}function I(e){return typeof e==`boolean`?e:void 0}function L(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function R(e){return typeof e==`string`&&we.includes(e)?e:void 0}function z(e){let t=process.env[e];if(!t)return;let n=Number(t);return Number.isFinite(n)?n:void 0}function Ee(e){let t=Te(e.browser)??{};return{...D,defaultMode:R(process.env.AIKIT_BROWSER_DEFAULT_MODE)??R(t.defaultMode)??D.defaultMode,browsersPath:F(process.env.AIKIT_BROWSER_PATH)??F(process.env.AIKIT_BROWSER_BROWSERS_PATH)??F(t.browsersPath)??D.browsersPath,userDataDirRoot:F(t.userDataDirRoot)??D.userDataDirRoot,idleShutdownMinutes:z(`AIKIT_BROWSER_IDLE_MINUTES`)??L(t.idleShutdownMinutes)??D.idleShutdownMinutes,allowInternalSchemes:I(t.allowInternalSchemes)??D.allowInternalSchemes,evalTimeoutMs:z(`AIKIT_BROWSER_EVAL_TIMEOUT_MS`)??L(t.evalTimeoutMs)??D.evalTimeoutMs,evalMaxResultBytes:L(t.evalMaxResultBytes)??D.evalMaxResultBytes,redactPasswordFieldsInScreenshots:I(t.redactPasswordFieldsInScreenshots)??D.redactPasswordFieldsInScreenshots}}function B(e,t){return{content:[{type:`text`,text:e}],structuredContent:t}}function V(e,t){let n=e.selector??e.ref??(e.element?`text=${e.element}`:void 0);if(!n)throw Error(`${t} requires selector, ref, or element`);return n}async function De(e,t,n){let r=null;try{return await Promise.race([e,new Promise((e,i)=>{r=setTimeout(()=>{i(Error(`${n} timed out after ${t}ms`))},t)})])}finally{r&&clearTimeout(r)}}l.string().describe(`Tracked browser page identifier`),l.enum([`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`]).describe(`Interaction kind: click, type, press, hover, drag, select, scroll, upload`),l.string().optional().describe(`Target ref alias`),l.string().optional().describe(`Playwright selector`),l.string().optional().describe(`Human-readable element label`),l.string().optional().describe(`Text to type`),l.string().optional().describe(`Key to press`),l.string().optional().describe(`Option value, drag target, scroll spec, or file path(s)`),l.string().optional().describe(`Drag source ref`),l.string().optional().describe(`Drag source selector`),l.string().optional().describe(`Drag target ref`),l.string().optional().describe(`Drag target selector`);function Oe(e){return async({pageId:t,kind:n,ref:r,selector:i,element:a,text:o,key:s,value:c,fromRef:l,fromSelector:u,toRef:d,toSelector:f})=>{let p=A(e),m=p.session.getPage(t);switch(n){case`click`:{let e=V({ref:r,selector:i,element:a},`browser_act(click)`);await m.click(e);break}case`type`:{let e=V({ref:r,selector:i,element:a},`browser_act(type)`);await m.fill(e,o??``);break}case`press`:{let e=V({ref:r,selector:i,element:a},`browser_act(press)`);if(!s)throw Error(`browser_act(press) requires key`);await m.press(e,s);break}case`hover`:{let e=V({ref:r,selector:i,element:a},`browser_act(hover)`);await m.hover(e);break}case`drag`:{let e=V({ref:l??r,selector:u??i,element:a},`browser_act(drag) source`),t=V({ref:d,selector:f??c,element:a},`browser_act(drag) target`);await m.dragAndDrop(e,t);break}case`select`:{let e=V({ref:r,selector:i,element:a},`browser_act(select)`);if(c===void 0)throw Error(`browser_act(select) requires value`);await m.selectOption(e,c);break}case`scroll`:{if(i||r||a){let e=V({ref:r,selector:i,element:a},`browser_act(scroll)`);await m.locator(e).scrollIntoViewIfNeeded();break}let e=c??`down 500`;await m.evaluate(e=>{if(e===`bottom`){window.scrollTo(0,document.body.scrollHeight);return}if(e===`top`){window.scrollTo(0,0);return}let t=e.split(` `),n=t[0]||`down`,r=Number.parseInt(t[1]||`500`,10),i=n===`left`?-r:n===`right`?r:0,a=n===`up`?-r:n===`down`?r:0;window.scrollBy(i,a)},e);break}case`upload`:{let e=V({ref:r,selector:i,element:a},`browser_act(upload)`);if(!c)throw Error(`value (file path or JSON array of paths) required for upload`);let t;try{let e=JSON.parse(c);t=Array.isArray(e)?e:[c]}catch{t=[c]}await m.locator(e).setInputFiles(t);break}}return p.resetIdleTimer(),B(`ok`,{ok:!0})}}function ke(e,t){return t?e.filter(e=>e.level===t):e}function Ae(e){return e.map(e=>{let t=e.location?` (${e.location.url}:${e.location.line})`:``;return`[${e.level.toUpperCase()}] ${e.text}${t}`}).join(`
|
|
2
|
+
`)}async function je(e,t){let n=y();switch(t.subAction){case`enable`:return n.enableConsole(t.pageId,e,{bufferSize:t.bufferSize}),B(`Console capture enabled for page ${t.pageId}`,{enabled:!0,bufferSize:t.bufferSize??1e3});case`get`:{let e=n.get(t.pageId);if(!e?.consoleEnabled)return B(`Console capture not enabled. Call with subAction: "enable" first.`,{enabled:!1,entries:[]});let r=ke(e.consoleBuffer.toArray(),t.level);return B(Ae(r)||`No console messages captured yet.`,{enabled:!0,count:r.length,entries:r})}case`clear`:{let e=n.get(t.pageId);return e&&e.consoleBuffer.clear(),B(`Console buffer cleared.`,{cleared:!0})}}}function Me(e){return async t=>{let n=A(e),r=await je(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}l.string().describe(`Tracked browser page identifier`),l.boolean().describe(`Whether to accept the next dialog`),l.string().optional().describe(`Text to provide when accepting a prompt dialog`);function Ne(e){return async({pageId:t,accept:n,promptText:r})=>{let i=A(e);return i.session.getPage(t).once(`dialog`,async e=>{n?await e.accept(r):await e.dismiss()}),i.resetIdleTimer(),B(`ok`,{ok:!0})}}function Pe(e){return async({pageId:t})=>{let n=A(e),r=await n.session.getPage(t).accessibility.snapshot()??{},i=JSON.stringify(r,null,2),a=n.session.getSnapshot(t);if(n.session.setSnapshot(t,i),n.resetIdleTimer(),!a)return B(`First snapshot captured (no previous to diff against).\n${i}`,{pageId:t,hasChanges:!1,isFirst:!0,snapshot:r});if(a===i)return B(`No changes detected.`,{pageId:t,hasChanges:!1});let o=a.split(`
|
|
3
3
|
`),s=i.split(`
|
|
4
4
|
`),c=new Set(o),l=new Set(s),u=s.filter(e=>!c.has(e)),d=o.filter(e=>!l.has(e)),f=u.length>0||d.length>0;return B([u.length>0?`Added:\n${u.join(`
|
|
5
5
|
`)}`:``,d.length>0?`Removed:\n${d.join(`
|
|
6
6
|
`)}`:``].filter(Boolean).join(`
|
|
7
7
|
|
|
8
|
-
`)||`No meaningful changes.`,{pageId:t,hasChanges:f,added:u,removed:d})}}l.string().describe(`Tracked browser page identifier`),l.string().describe(`JavaScript expression or IIFE evaluated in page context. Code is wrapped in Function("return (<code>)"). For multi-statement code, use an IIFE: (() => { stmt1; return result; })(). Do NOT use bare return or semicolons at the top level.`),l.number().min(1).max(6e4).optional().describe(`Optional evaluation timeout`);function
|
|
9
|
-
`)}function
|
|
10
|
-
`)||`No requests captured yet.`,{pageId:t.pageId,enabled:!0,count:r.length,entries:r})}case`clear`:{let e=n.get(t.pageId);return e?.networkEnabled?(e.networkBuffer.clear(),e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0,B(`Network capture buffer cleared.`,{pageId:t.pageId,cleared:!0})):B(`Network capture not enabled. Call with subAction: "enable" first.`,{pageId:t.pageId,enabled:!1,cleared:!1})}case`export-har`:{let e=n.get(t.pageId);if(!e?.networkEnabled)return
|
|
8
|
+
`)||`No meaningful changes.`,{pageId:t,hasChanges:f,added:u,removed:d})}}l.string().describe(`Tracked browser page identifier`),l.string().describe(`JavaScript expression or IIFE evaluated in page context. Code is wrapped in Function("return (<code>)"). For multi-statement code, use an IIFE: (() => { stmt1; return result; })(). Do NOT use bare return or semicolons at the top level.`),l.number().min(1).max(6e4).optional().describe(`Optional evaluation timeout`);function Fe(e){return async({pageId:t,code:n,timeoutMs:r})=>{let i=A(e),a=i.session.getPage(t),o=Math.min(r??e.evalTimeoutMs,6e4),s=P(await De(a.evaluate(e=>{let t=Function(`return (${e});`)();return typeof t==`function`?t():t},n),o,`browser_eval`),e.evalMaxResultBytes);if(!s.valid||s.result===void 0)throw Error(s.reason??`browser_eval result validation failed`);return i.resetIdleTimer(),B(s.result,{pageId:t,result:s.result,truncated:s.truncated??!1,reason:s.reason})}}const H=2e3,Ie=new Set([`localhost`,`metadata.google.internal`]),Le=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`];function U(e){return e.replace(/^\[(.*)\]$/,`$1`).toLowerCase()}function W(e){if(!/^(?:\d{1,3}\.){3}\d{1,3}$/.test(e))return;let t=e.split(`.`).map(e=>Number(e));if(!t.some(e=>!Number.isInteger(e)||e<0||e>255))return t}function G(e){let t=U(e).split(`::`);if(t.length>2)return;let n=e=>{if(e.length===0)return e;let t=e[e.length-1];if(!t?.includes(`.`))return e;let n=W(t);if(n)return[...e.slice(0,-1),(n[0]<<8|n[1]).toString(16),(n[2]<<8|n[3]).toString(16)]},r=n(t[0]?t[0].split(`:`):[]),i=n(t[1]?t[1].split(`:`):[]);if(!r||!i)return;let a=t.length===2,o=r.length+i.length;if(!a&&o!==8||o>8)return;let s=a?Array(8-o).fill(`0`):[],c=[...r,...s,...i];if(c.length!==8)return;let l=0n;for(let e of c){if(!/^[0-9a-f]{1,4}$/i.test(e))return;l=(l<<16n)+BigInt(`0x${e}`)}return l}function Re(e){let[t,n,r,i]=e;return t===127||t===10||t===172&&n>=16&&n<=31||t===192&&n===168||t===169&&n===254||t===0&&n===0&&r===0&&i===0}function ze(e){return e===1n||(e&339950059921992234495148655666698125312n)==338288524927261089654018896841347694592n||(e&337623910929368631717566993311207522304n)==334965454937798799971759379190646833152n}function Be(e){let t;try{t=new URL(e)}catch{return!1}let n=U(t.hostname);if(Ie.has(n))return!0;let r=W(n);if(r)return Re(r);let i=G(n);return i===void 0?!1:ze(i)}function Ve(e){let t;try{t=new URL(e)}catch{throw Error(`Invalid fetch URL: ${e}`)}if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`Fetch URL must use http:// or https://`);return t}function He(e){let t=U(e),n=W(t);if(n)return Re(n);let r=G(t);return r===void 0?!1:ze(r)}async function Ue(e){let t=Ve(e);if(Be(e))throw Error(`Fetch URL must not target private or local hosts`);let n=U(t.hostname);if(!(W(n)||G(n)!==void 0))try{let e=u.lookup(n,{family:0,verbatim:!0}),t=new Promise((e,t)=>setTimeout(()=>t(Error(`DNS lookup timed out after ${H}ms for hostname: ${n}`)),H)),{address:r}=await Promise.race([e,t]);if(He(r))throw Error(`Fetch URL must not target private or local hosts`)}catch(e){if(e instanceof Error&&e.message===`Fetch URL must not target private or local hosts`)throw e;let t=e instanceof Error?e.message:String(e);throw Error(`Failed to resolve hostname ${n}: ${t}`,{cause:e})}}async function We(e){await Ue(e)}function Ge(e,t=262144){return e.length<=t?{body:e,truncated:!1}:{body:`${e.slice(0,t)}\n\n[truncated after ${t} chars]`,truncated:!0}}function Ke(e){let t=[`HTTP ${e.status} ${e.statusText}`,`URL: ${e.url}`,`Redirected: ${e.redirected?`yes`:`no`}`,`JSON: ${e.isJson?`yes`:`no`}`],n=Object.entries(e.headers);if(n.length>0){t.push(``,`Headers:`);for(let[e,r]of n)t.push(`${e}: ${r}`)}return t.push(``,`Body:`,e.body||`<empty>`),t.join(`
|
|
9
|
+
`)}function qe(e){let t=(e??`GET`).toUpperCase();if(!Le.includes(t))throw Error(`Unsupported fetch method: ${t}`);return t}function Je(e){if(e===void 0)return 3e4;if(!Number.isFinite(e)||e<=0)throw Error(`Fetch timeoutMs must be a positive number`);return e}function Ye(e){return Object.entries(e).find(([e])=>e.toLowerCase()===`content-type`)?.[1]}function Xe(e,t){if(!t||e.length===0)return e;try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}async function Ze(e,t){await We(t.url);let n=qe(t.method),r=Je(t.timeoutMs),i=t.headers??{},a=n===`GET`||n===`HEAD`?void 0:t.body,o;try{o=await e.evaluate(async e=>{let t=new AbortController,n=setTimeout(()=>t.abort(),e.timeoutMs);try{let n=await fetch(e.url,{method:e.method,headers:e.headers,body:e.body,signal:t.signal,redirect:`follow`});return{status:n.status,statusText:n.statusText,headers:Object.fromEntries(n.headers.entries()),body:await n.text(),url:n.url,redirected:n.redirected}}catch(t){if(t instanceof Error&&t.name===`AbortError`)throw Error(`Fetch timed out after ${e.timeoutMs}ms`);let n=t instanceof Error?t.message:String(t);throw Error(`Fetch failed: ${n}`,{cause:t})}finally{clearTimeout(n)}},{url:t.url,method:n,headers:i,body:a,timeoutMs:r})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(t,{cause:e})}await We(o.url);let s=Ye(o.headers)?.toLowerCase().includes(`json`)??!1,c=Ge(Xe(o.body,s)),l=t.includeHeaders===!1?{}:o.headers,u={status:o.status,statusText:o.statusText,headers:l,body:c.body,isJson:s,url:o.url,redirected:o.redirected};return B(Ke(u),{...u,truncated:c.truncated})}function Qe(e){return async t=>{let n=A(e),r=await Ze(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}l.string().describe(`Tracked browser page identifier`),l.string().url().optional().describe(`Optional URL to navigate to`),l.enum([`back`,`forward`,`reload`,`waitFor`]).optional().describe(`Navigation helper action`),l.string().optional().describe(`Selector to wait for when type="waitFor"`),l.number().min(1).max(6e4).optional().describe(`Optional wait timeout`);function $e(e){return async({pageId:t,url:n,type:r,selector:i,timeoutMs:a})=>{let o=A(e),s=o.session.getPage(t);if(n){let r=M(n,e);if(!r.allowed)return B(`Navigation blocked: ${r.reason}`,{blocked:!0,pageId:t,reason:r.reason});await s.goto(n)}else if(!r)return B(`Navigation requires url or type`,{error:`Navigation requires url or type`,pageId:t});else if(r===`back`)await s.goBack();else if(r===`forward`)await s.goForward();else if(r===`reload`)await s.reload();else if(r===`waitFor`){if(!i)throw Error(`browser_navigate(waitFor) requires selector`);await s.waitForSelector(i,a?{timeout:a}:void 0)}let c=s.url(),l=await s.title();return o.session.updatePageInfo(t,c,l),o.resetIdleTimer(),B(`${l||c}\n${c}`,{pageId:t,url:c,title:l})}}function et(e){return Object.entries(e).map(([e,t])=>({name:e,value:t}))}function tt(e){try{return Array.from(new URL(e).searchParams.entries()).map(([e,t])=>({name:e,value:t}))}catch{return[]}}function nt(e,t){let n=t.toLowerCase();for(let[t,r]of Object.entries(e))if(t.toLowerCase()===n)return r}function rt(e){let t=nt(e,`content-type`);return t&&t.split(`;`,1)[0]?.trim()||`application/octet-stream`}function it(e){return e===void 0?0:Buffer.byteLength(e,`utf8`)}function at(e,t=!1){return t?{...e}:m(e)}function ot(e,t){return{...e,headers:at(e.headers,t.showSensitive),postData:t.includeBody?e.postData:void 0,response:e.response?{...e.response,headers:at(e.response.headers,t.showSensitive),bodyPreview:t.includeBody?e.response.bodyPreview:void 0}:void 0}}function st(e){return B(`Network capture not enabled. Call with subAction: "enable" first.`,{pageId:e,enabled:!1,count:0,entries:[]})}async function ct(e,t){let n=y();switch(t.subAction){case`enable`:return n.enableNetwork(t.pageId,e,t.filter,{bufferSize:t.bufferSize,showSensitive:t.showSensitive,includeBody:t.includeBody}),B(`Network capture enabled for page ${t.pageId}`,{pageId:t.pageId,enabled:!0,filter:t.filter??{resourceTypes:[...f]}});case`get`:{let e=n.get(t.pageId);if(!e?.networkEnabled)return st(t.pageId);let r=e.networkBuffer.toArray().map(e=>ot(e,t));return B(r.map(e=>`${e.method} ${e.url} -> ${e.response?.status??`pending`} (${e.response?.timing?`${e.response.timing}ms`:`no response`})`).join(`
|
|
10
|
+
`)||`No requests captured yet.`,{pageId:t.pageId,enabled:!0,count:r.length,entries:r})}case`clear`:{let e=n.get(t.pageId);return e?.networkEnabled?(e.networkBuffer.clear(),e.pendingRequests.clear(),e.pendingRequestHead=void 0,e.pendingRequestTail=void 0,B(`Network capture buffer cleared.`,{pageId:t.pageId,cleared:!0})):B(`Network capture not enabled. Call with subAction: "enable" first.`,{pageId:t.pageId,enabled:!1,cleared:!1})}case`export-har`:{let e=n.get(t.pageId);if(!e?.networkEnabled)return st(t.pageId);let r=e.networkBuffer.toArray().map(e=>{let n=ot(e,t);return{startedDateTime:new Date(n.timestamp).toISOString(),time:n.response?.timing??-1,request:{method:n.method,url:n.url,httpVersion:`HTTP/1.1`,cookies:[],headers:et(n.headers),queryString:tt(n.url),headersSize:-1,bodySize:it(e.postData),postData:n.postData?{text:n.postData}:void 0},response:n.response?{status:n.response.status,statusText:n.response.statusText,httpVersion:`HTTP/1.1`,cookies:[],headers:et(n.response.headers),content:{size:n.response.bodySize??0,mimeType:rt(n.response.headers),text:n.response.bodyPreview},redirectURL:``,headersSize:-1,bodySize:n.response.bodySize??-1}:{status:0,statusText:`pending`,httpVersion:`HTTP/1.1`,cookies:[],headers:[],content:{size:0,mimeType:`application/octet-stream`},redirectURL:``,headersSize:-1,bodySize:-1},cache:{},timings:{send:-1,wait:-1,receive:-1}}});return B(`Exported ${r.length} entries in HAR 1.2 format.`,{pageId:t.pageId,har:{log:{version:`1.2`,creator:{name:`aikit-browser`,version:`1.0.0`},entries:r}}})}default:{let e=`Unknown network subAction: ${String(t.subAction)}`;return B(e,{pageId:t.pageId,error:e})}}}function lt(e){return async t=>{let n=A(e),r=await ct(n.session.getPage(t.pageId),t);return n.resetIdleTimer(),r}}l.string().url().describe(`Absolute URL to open`),l.enum([`ui`,`headless`]).optional().describe(`Browser launch mode`),l.boolean().optional().describe(`Reserved for future tab reuse control`),l.string().optional().describe(`Human-readable label for the page`),l.boolean().optional().describe(`Auto-accept alert/beforeunload dialogs (default: true)`),l.enum([`load`,`domcontentloaded`,`networkidle`]).optional().describe(`Navigation readiness event`);function ut(e){return async({url:t,mode:n,label:r,autoDialog:i,waitUntil:a})=>{let o=M(t,e);if(!o.allowed)throw Error(o.reason??`Blocked URL: ${t}`);let s=A(e);s.isLaunched()||await s.launch(n??e.defaultMode);let c=await s.getContext().newPage();a?await c.goto(t,{waitUntil:a}):await c.goto(t);let l=await c.title(),u=s.session.registerPage(c,t,l,r);return i!==!1&&c.on(`dialog`,async e=>{let t=e.type();(t===`alert`||t===`beforeunload`)&&await e.accept()}),s.resetIdleTimer(),B(`Opened ${l||t}\npageId: ${u}`,{pageId:u,url:t,title:l,label:r})}}l.string().describe(`Tracked browser page identifier`),l.enum([`snapshot`,`dom`,`markdown`,`text`]).optional().describe(`Extraction mode: snapshot (ARIA tree), dom (HTML), markdown (clean MD), text (plain text)`),l.string().optional().describe(`CSS selector to scope extraction to a specific element`);function dt(e){return async({pageId:t,mode:n,selector:r})=>{let i=A(e).session.getPage(t),a=r??`body`,o=n??`snapshot`;switch(o){case`dom`:{let e=r?await i.locator(r).innerHTML():await i.content();return B(e||`empty page`,{pageId:t,mode:o,selector:r,dom:e})}case`markdown`:{let e=await i.evaluate(e=>{let t=globalThis,n=e?t.document?.querySelector(e):t.document?.body;if(!n)return``;function r(e){if(e.nodeType===t.Node?.TEXT_NODE)return e.textContent?.replace(/\s+/g,` `)||``;if(e.nodeType!==t.Node?.ELEMENT_NODE)return``;let n=e,a=n.tagName.toLowerCase(),o=t.window?.getComputedStyle(n);if(o?.display===`none`||o?.visibility===`hidden`)return``;let s=Array.from(n.childNodes).map(r).filter(Boolean).join(``);switch(a){case`h1`:return`# ${s.trim()}\n\n`;case`h2`:return`## ${s.trim()}\n\n`;case`h3`:return`### ${s.trim()}\n\n`;case`h4`:return`#### ${s.trim()}\n\n`;case`h5`:return`##### ${s.trim()}\n\n`;case`h6`:return`###### ${s.trim()}\n\n`;case`p`:return`${s.trim()}\n\n`;case`br`:return`
|
|
11
11
|
`;case`hr`:return`
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ import{createRequire as e}from"node:module";import{createHash as t,randomUUID as
|
|
|
15
15
|
> `)}\n\n`;case`a`:return`[${s.trim()}](${n.getAttribute(`href`)||``})`;case`img`:return`||``})`;case`ul`:case`ol`:return`${s}\n`;case`li`:return`${n.parentElement?.tagName===`OL`?`${Array.from(n.parentElement.children).indexOf(n)+1}. `:`- `}${s.trim()}\n`;case`table`:return`${i(n)}\n\n`;case`script`:case`style`:case`noscript`:case`nav`:case`footer`:case`header`:return``;case`div`:case`section`:case`article`:case`main`:case`span`:return s;default:return s}}function i(e){let t=Array.from(e.querySelectorAll(`tr`));if(!t.length)return``;let n=[];return t.forEach((e,t)=>{let r=Array.from(e.querySelectorAll(`th, td`)).map(e=>e.textContent?.trim()||``);n.push(`| ${r.join(` | `)} |`),t===0&&n.push(`| ${r.map(()=>`---`).join(` | `)} |`)}),n.join(`
|
|
16
16
|
`)}return r(n).replace(/\n{3,}/g,`
|
|
17
17
|
|
|
18
|
-
`).trim()},r);return B(e||`empty page`,{pageId:t,mode:o,selector:r,markdown:e})}case`text`:{let e=await i.locator(a).innerText();return B(e||`empty page`,{pageId:t,mode:o,selector:r,text:e})}default:{let e=await i.locator(a).ariaSnapshot();return B(e||`empty page`,{pageId:t,mode:o,selector:r,snapshot:e})}}}}l.string().describe(`Tracked browser page identifier`),l.string().optional().describe(`Optional target ref alias`),l.string().optional().describe(`Optional target selector`),l.boolean().optional().describe(`Capture the full page when no selector is provided`),l.boolean().optional().describe(`Mask password fields before capture`),l.object({x:l.number(),y:l.number(),width:l.number(),height:l.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),l.enum([`png`,`jpeg`]).optional().describe(`Image format (default: png)`),l.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`);function ct(e){return async({pageId:t,ref:n,selector:r,fullPage:i,redactPasswords:a,clip:o,format:s,quality:c})=>{let l=A(e),u=l.session.getPage(t),d=a??e.redactPasswordFieldsInScreenshots,f=[];d&&(f=await u.evaluate(()=>{let e=globalThis.document;return Array.from(e.querySelectorAll(`input[type="password"]`)).map((e,t)=>{let n=e,r=`data-aikit-password-mask-${t}`;return n.setAttribute(r,n.value),n.value=`***`,r})}));try{let e={};s&&(e.type=s),s===`jpeg`&&c!==void 0&&(e.quality=c);let a=r??n?await u.locator(V({ref:n,selector:r},`browser_screenshot`)).screenshot(e):await u.screenshot({...e,...o?{clip:o}:{fullPage:i??!1}});return l.resetIdleTimer(),B(`Screenshot captured`,{pageId:t,base64:a.toString(`base64`)})}finally{d&&f.length>0&&await u.evaluate(e=>{let t=globalThis.document;for(let n of e){let e=t.querySelector(`input[${n}]`);e&&(e.value=e.getAttribute(n)??``,e.removeAttribute(n))}},f)}}}const lt=l.enum([`Lax`,`None`,`Strict`]),ut=l.enum([`localStorage`,`sessionStorage`]);l.enum([`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`]).describe(`Session sub-action`),l.string().optional().describe(`Page ID (required for close, storage actions)`),l.boolean().optional().describe(`Explicit confirmation for cookie operations`),l.array(l.object({name:l.string(),value:l.string(),url:l.string().optional(),domain:l.string().optional(),path:l.string().optional(),expires:l.number().optional(),httpOnly:l.boolean().optional(),secure:l.boolean().optional(),sameSite:lt.optional()})).optional().describe(`Cookies to set (for set-cookie action)`),l.string().optional().describe(`Cookie name (for delete-cookie action)`),ut.optional().describe(`Storage type for storage actions`),l.string().optional().describe(`Storage key to get/set`),l.string().optional().describe(`Storage value to set`);function dt(e){return async({action:t,pageId:n,confirm:r,cookies:i,name:a,storageType:o,storageKey:s,storageValue:c})=>{let l=A(e);if(t===`list`){let e=l.session.listPages();return B(JSON.stringify(e,null,2),{pages:e})}if(t===`close`){if(!n)throw Error(`browser_session(close) requires pageId`);return await l.session.removePage(n),l.resetIdleTimer(),B(`ok`,{ok:!0,pageId:n})}if(t===`set-cookie`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);if(!i?.length)throw Error(`cookies array required for set-cookie`);return await l.getContext().addCookies(i),l.resetIdleTimer(),B(`Set ${i.length} cookie(s)`,{ok:!0,count:i.length})}if(t===`delete-cookie`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);if(!a)throw Error(`name required for delete-cookie`);return await l.getContext().clearCookies({name:a}),l.resetIdleTimer(),B(`Deleted cookie: ${a}`,{ok:!0,name:a})}if(t===`clear-cookies`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);return await l.getContext().clearCookies(),l.resetIdleTimer(),B(`All cookies cleared`,{ok:!0})}if(t===`get-storage`){if(!n)throw Error(`pageId required for get-storage`);if(!o)throw Error(`storageType required for get-storage`);let e=await l.session.getPage(n).evaluate(({type:e,key:t})=>{let n=e===`localStorage`?localStorage:sessionStorage;if(t)return n.getItem(t);let r={};for(let e=0;e<n.length;e+=1){let t=n.key(e);t&&(r[t]=n.getItem(t)||``)}return r},{type:o,key:s});return l.resetIdleTimer(),B(s?`${o}.${s} = ${String(e)}`:`${o}: ${JSON.stringify(e)}`,{storageType:o,key:s,data:e})}if(t===`set-storage`){if(!n)throw Error(`pageId required for set-storage`);if(!o)throw Error(`storageType required for set-storage`);if(!s)throw Error(`storageKey required for set-storage`);if(c===void 0)throw Error(`storageValue required for set-storage`);return await l.session.getPage(n).evaluate(({type:e,key:t,value:n})=>{(e===`localStorage`?localStorage:sessionStorage).setItem(t,n)},{type:o,key:s,value:c}),l.resetIdleTimer(),B(`Set ${o}.${s}`,{ok:!0,storageType:o,key:s})}if(t===`clear-storage`){if(!n)throw Error(`pageId required for clear-storage`);if(!o)throw Error(`storageType required for clear-storage`);return await l.session.getPage(n).evaluate(e=>{(e===`localStorage`?localStorage:sessionStorage).clear()},o),l.resetIdleTimer(),B(`Cleared ${o}`,{ok:!0,storageType:o})}let u=N(!!r);if(!u.allowed)throw Error(u.reason??`Cookie access denied`);let d=await l.getContext().cookies();return l.resetIdleTimer(),B(JSON.stringify(d,null,2),{cookies:d})}}const ft=[`open`,`batch`,`read`,`act`,`navigate`,`network`,`console`,`fetch`,`eval`,`diff`,`screenshot`,`dialog`,`session`],G=[`ui`,`headless`],pt=[`load`,`domcontentloaded`,`networkidle`],mt=[`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`],ht=[`back`,`forward`,`reload`,`waitFor`],gt=[`enable`,`get`,`clear`,`export-har`],_t=[`enable`,`get`,`clear`],vt=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],yt=[`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`],bt={action:l.enum(ft).describe(`Browser action to perform`),pageId:l.string().optional().describe(`Tracked browser page identifier`),label:l.string().optional().describe(`Human-readable label for the page (used as alternative to pageId)`),url:l.string().url().optional().describe(`URL to open or navigate to`),mode:l.enum(G).optional().describe(`Browser launch mode (open only)`),forceNew:l.boolean().optional().describe(`Reserved for future tab reuse`),autoDialog:l.boolean().optional().describe(`Auto-accept alert/beforeunload dialogs (default: true). Set false to handle all dialogs manually.`),waitUntil:l.enum(pt).optional().describe(`Navigation readiness event`),kind:l.enum(mt).optional().describe(`Interaction kind (act only)`),ref:l.string().optional().describe(`Target ref alias`),selector:l.string().optional().describe(`Playwright selector`),element:l.string().optional().describe(`Human-readable element label (resolved via Playwright text selector). Prefer selector for precision.`),text:l.string().optional().describe(`Text to type`),key:l.string().optional().describe(`Key to press`),value:l.string().optional().describe(`Option value or drag target`),fromRef:l.string().optional().describe(`Drag source ref`),fromSelector:l.string().optional().describe(`Drag source selector`),toRef:l.string().optional().describe(`Drag target ref`),toSelector:l.string().optional().describe(`Drag target selector`),type:l.enum(ht).optional().describe(`Navigation type`),subAction:l.enum(gt).optional().describe(`Network sub-action: enable, get, clear, or export-har`),code:l.string().optional().describe(`JavaScript expression or IIFE evaluated in page context. Wrapped in Function("return (<code>)"). Multi-statement: use (() => { ...; return result; })(). No bare return or top-level semicolons.`),timeoutMs:l.number().min(1).max(6e4).optional().describe(`Timeout in milliseconds`),filter:l.object({resourceTypes:l.array(l.string()).optional(),urlPattern:l.string().optional(),excludeUrls:l.array(l.string()).optional()}).optional().describe(`Network capture filter for resource types and URL patterns`),showSensitive:l.boolean().optional().describe(`Include sensitive headers instead of redacting them`),includeBody:l.boolean().optional().describe(`Include truncated request bodies in captured entries`),bufferSize:l.number().int().min(1).max(1e4).optional().describe(`Buffer capacity (1-10000, default varies by action)`),consoleSubAction:l.enum(_t).optional().describe(`Console sub-action: enable, get, or clear`),level:l.string().optional().describe(`Filter console entries by level (log/info/warn/error/debug)`),fetchUrl:l.string().optional().describe(`URL for fetch action (http/https only). Uses page cookies/session.`),fetchMethod:l.enum(vt).optional().describe(`HTTP method for fetch action (default: GET)`),fetchHeaders:l.record(l.string(),l.string()).optional().describe(`Custom headers for fetch action`),fetchBody:l.string().optional().describe(`Request body for fetch action (POST/PUT/PATCH)`),includeResponseHeaders:l.boolean().optional().describe(`Include response headers in fetch output (default: true)`),fullPage:l.boolean().optional().describe(`Capture full page`),redactPasswords:l.boolean().optional().describe(`Mask password fields`),readMode:l.enum([`snapshot`,`dom`,`markdown`,`text`]).optional().describe(`Extraction mode for read action: snapshot (ARIA tree), dom (HTML), markdown (clean MD), text (plain text)`),clip:l.object({x:l.number(),y:l.number(),width:l.number(),height:l.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),format:l.enum([`png`,`jpeg`]).optional().describe(`Screenshot image format (default: png)`),quality:l.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`),cookies:l.array(l.object({name:l.string(),value:l.string(),url:l.string().optional(),domain:l.string().optional(),path:l.string().optional(),expires:l.number().optional(),httpOnly:l.boolean().optional(),secure:l.boolean().optional(),sameSite:l.enum([`Lax`,`None`,`Strict`]).optional()})).optional().describe(`Cookies to set (for session set-cookie action)`),name:l.string().optional().describe(`Cookie name for delete-cookie action`),storageType:l.enum([`localStorage`,`sessionStorage`]).optional().describe(`Storage type for get/set/clear-storage actions`),storageKey:l.string().optional().describe(`Storage key to get or set`),storageValue:l.string().optional().describe(`Value to set in storage`),accept:l.boolean().optional().describe(`Accept or dismiss dialog`),promptText:l.string().optional().describe(`Text for prompt dialog`),sessionAction:l.enum(yt).optional().describe(`Session sub-action (session only)`),confirm:l.boolean().optional().describe(`Explicit confirmation for cookie export`),steps:l.array(l.record(l.string(),l.unknown())).optional().describe(`Array of action steps for batch execution. Each step is an object with action + action-specific params.`)};function xt(e,t){return typeof e==`string`&&t.includes(e)}function K(e,t,n){let r=e[t];if(typeof r!=`string`)throw Error(`${n} requires ${t}`);return r}function St(e,t,n){let r=e[t];if(typeof r!=`boolean`)throw Error(`${n} requires ${t}`);return r}function q(e,t){let n=e[t];return typeof n==`string`?n:void 0}function J(e,t){let n=e[t];return typeof n==`boolean`?n:void 0}function Y(e,t){let n=e[t];return typeof n==`number`?n:void 0}function X(e,t){let n=e[t];return typeof n==`object`&&n?n:void 0}function Ct(e,t){let n=e.steps;if(!Array.isArray(n)||n.length===0)throw Error(`${t} requires non-empty steps`);return n.map((e,n)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`${t} step ${n+1} must be an object`);return e})}function Z(e,t,n,r){let i=e[t];if(!xt(i,n))throw Error(`${r} requires ${t}`);return i}function Q(e,t,n,r){let i=e[t];if(i!==void 0){if(!xt(i,n))throw Error(`${r} received invalid ${t}`);return i}}function wt(e,t){let n=q(e,`pageId`);if(n)return A(t).session.resolvePageId(n)}function $(e,t,n){let r=q(e,`pageId`);if(!r)throw Error(`${n} requires pageId`);return A(t).session.resolvePageId(r)}async function Tt(e,t,n){switch(Z(e,`action`,ft,`browser`)){case`open`:return t.openHandler({url:K(e,`url`,`browser(open)`),mode:Q(e,`mode`,G,`browser(open)`),forceNew:J(e,`forceNew`),label:q(e,`label`),autoDialog:J(e,`autoDialog`),waitUntil:Q(e,`waitUntil`,pt,`browser(open)`)});case`batch`:{let r=Ct(e,`browser(batch)`),i=[],a=0;for(let e=0;e<r.length;e+=1){let o=r[e],s=q(o,`action`)??`unknown`;try{let r=await Tt(o,t,n);a+=1,i.push({index:e,action:s,ok:!0,result:r})}catch(t){i.push({index:e,action:s,ok:!1,error:t instanceof Error?t.message:String(t)})}}return B(`Batch completed: ${a}/${r.length} steps succeeded`,{total:r.length,succeeded:a,steps:i})}case`read`:return t.readHandler({pageId:$(e,n,`browser(read)`),mode:Q(e,`readMode`,[`snapshot`,`dom`,`markdown`,`text`],`browser(read)`),selector:q(e,`selector`)});case`act`:return t.actHandler({pageId:$(e,n,`browser(act)`),kind:Z(e,`kind`,mt,`browser(act)`),ref:q(e,`ref`),selector:q(e,`selector`),element:q(e,`element`),text:q(e,`text`),key:q(e,`key`),value:q(e,`value`),fromRef:q(e,`fromRef`),fromSelector:q(e,`fromSelector`),toRef:q(e,`toRef`),toSelector:q(e,`toSelector`)});case`navigate`:return t.navigateHandler({pageId:$(e,n,`browser(navigate)`),url:q(e,`url`),type:Q(e,`type`,ht,`browser(navigate)`),selector:q(e,`selector`),timeoutMs:Y(e,`timeoutMs`)});case`network`:return t.networkHandler({pageId:$(e,n,`browser(network)`),subAction:Z(e,`subAction`,gt,`browser(network)`),filter:X(e,`filter`),showSensitive:J(e,`showSensitive`),includeBody:J(e,`includeBody`),bufferSize:Y(e,`bufferSize`)});case`console`:return t.consoleHandler({pageId:$(e,n,`browser(console)`),subAction:Z(e,`consoleSubAction`,_t,`browser(console)`),level:q(e,`level`),bufferSize:Y(e,`bufferSize`)});case`fetch`:return t.fetchHandler({pageId:$(e,n,`browser(fetch)`),url:K(e,`fetchUrl`,`browser(fetch)`),method:Q(e,`fetchMethod`,vt,`browser(fetch)`),headers:X(e,`fetchHeaders`),body:q(e,`fetchBody`),timeoutMs:Y(e,`timeoutMs`),includeHeaders:J(e,`includeResponseHeaders`)});case`eval`:return t.evalHandler({pageId:$(e,n,`browser(eval)`),code:K(e,`code`,`browser(eval)`),timeoutMs:Y(e,`timeoutMs`)});case`diff`:return t.diffHandler({pageId:$(e,n,`browser(diff)`)});case`screenshot`:{let r=X(e,`clip`);return t.screenshotHandler({pageId:$(e,n,`browser(screenshot)`),ref:q(e,`ref`),selector:q(e,`selector`),fullPage:J(e,`fullPage`),redactPasswords:J(e,`redactPasswords`),clip:r,format:Q(e,`format`,[`png`,`jpeg`],`browser(screenshot)`),quality:Y(e,`quality`)})}case`dialog`:return t.dialogHandler({pageId:$(e,n,`browser(dialog)`),accept:St(e,`accept`,`browser(dialog)`),promptText:q(e,`promptText`)});case`session`:{let r=e.cookies;return t.sessionHandler({action:Z(e,`sessionAction`,yt,`browser(session)`),pageId:wt(e,n),confirm:J(e,`confirm`),cookies:r,name:q(e,`name`),storageType:Q(e,`storageType`,[`localStorage`,`sessionStorage`],`browser(session)`),storageKey:q(e,`storageKey`),storageValue:q(e,`storageValue`)})}}}function Et(e,t){let n=Te(t),r={openHandler:ot(n),readHandler:st(n),actHandler:De(n),navigateHandler:Xe(n),networkHandler:at(n),consoleHandler:je(n),fetchHandler:Ye(n),evalHandler:Pe(n),diffHandler:Ne(n),screenshotHandler:ct(n),dialogHandler:Me(n),sessionHandler:dt(n)};e.registerTool(`browser`,{title:`Browser Automation`,description:`Unified browser automation tool. Actions:
|
|
18
|
+
`).trim()},r);return B(e||`empty page`,{pageId:t,mode:o,selector:r,markdown:e})}case`text`:{let e=await i.locator(a).innerText();return B(e||`empty page`,{pageId:t,mode:o,selector:r,text:e})}default:{let e=await i.locator(a).ariaSnapshot();return B(e||`empty page`,{pageId:t,mode:o,selector:r,snapshot:e})}}}}l.string().describe(`Tracked browser page identifier`),l.string().optional().describe(`Optional target ref alias`),l.string().optional().describe(`Optional target selector`),l.boolean().optional().describe(`Capture the full page when no selector is provided`),l.boolean().optional().describe(`Mask password fields before capture`),l.object({x:l.number(),y:l.number(),width:l.number(),height:l.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),l.enum([`png`,`jpeg`]).optional().describe(`Image format (default: png)`),l.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`);function ft(e){return async({pageId:t,ref:n,selector:r,fullPage:i,redactPasswords:a,clip:o,format:s,quality:c})=>{let l=A(e),u=l.session.getPage(t),d=a??e.redactPasswordFieldsInScreenshots,f=[];d&&(f=await u.evaluate(()=>{let e=globalThis.document;return Array.from(e.querySelectorAll(`input[type="password"]`)).map((e,t)=>{let n=e,r=`data-aikit-password-mask-${t}`;return n.setAttribute(r,n.value),n.value=`***`,r})}));try{let e={};s&&(e.type=s),s===`jpeg`&&c!==void 0&&(e.quality=c);let a=r??n?await u.locator(V({ref:n,selector:r},`browser_screenshot`)).screenshot(e):await u.screenshot({...e,...o?{clip:o}:{fullPage:i??!1}});return l.resetIdleTimer(),B(`Screenshot captured`,{pageId:t,base64:a.toString(`base64`)})}finally{d&&f.length>0&&await u.evaluate(e=>{let t=globalThis.document;for(let n of e){let e=t.querySelector(`input[${n}]`);e&&(e.value=e.getAttribute(n)??``,e.removeAttribute(n))}},f)}}}const pt=l.enum([`Lax`,`None`,`Strict`]),mt=l.enum([`localStorage`,`sessionStorage`]);l.enum([`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`]).describe(`Session sub-action`),l.string().optional().describe(`Page ID (required for close, storage actions)`),l.boolean().optional().describe(`Explicit confirmation for cookie operations`),l.array(l.object({name:l.string(),value:l.string(),url:l.string().optional(),domain:l.string().optional(),path:l.string().optional(),expires:l.number().optional(),httpOnly:l.boolean().optional(),secure:l.boolean().optional(),sameSite:pt.optional()})).optional().describe(`Cookies to set (for set-cookie action)`),l.string().optional().describe(`Cookie name (for delete-cookie action)`),mt.optional().describe(`Storage type for storage actions`),l.string().optional().describe(`Storage key to get/set`),l.string().optional().describe(`Storage value to set`);function ht(e){return async({action:t,pageId:n,confirm:r,cookies:i,name:a,storageType:o,storageKey:s,storageValue:c})=>{let l=A(e);if(t===`list`){let e=l.session.listPages();return B(JSON.stringify(e,null,2),{pages:e})}if(t===`close`){if(!n)throw Error(`browser_session(close) requires pageId`);return await l.session.removePage(n),l.resetIdleTimer(),B(`ok`,{ok:!0,pageId:n})}if(t===`set-cookie`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);if(!i?.length)throw Error(`cookies array required for set-cookie`);return await l.getContext().addCookies(i),l.resetIdleTimer(),B(`Set ${i.length} cookie(s)`,{ok:!0,count:i.length})}if(t===`delete-cookie`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);if(!a)throw Error(`name required for delete-cookie`);return await l.getContext().clearCookies({name:a}),l.resetIdleTimer(),B(`Deleted cookie: ${a}`,{ok:!0,name:a})}if(t===`clear-cookies`){let e=N(!!r);if(!e.allowed)throw Error(e.reason??`Cookie access denied`);return await l.getContext().clearCookies(),l.resetIdleTimer(),B(`All cookies cleared`,{ok:!0})}if(t===`get-storage`){if(!n)throw Error(`pageId required for get-storage`);if(!o)throw Error(`storageType required for get-storage`);let e=await l.session.getPage(n).evaluate(({type:e,key:t})=>{let n=e===`localStorage`?localStorage:sessionStorage;if(t)return n.getItem(t);let r={};for(let e=0;e<n.length;e+=1){let t=n.key(e);t&&(r[t]=n.getItem(t)||``)}return r},{type:o,key:s});return l.resetIdleTimer(),B(s?`${o}.${s} = ${String(e)}`:`${o}: ${JSON.stringify(e)}`,{storageType:o,key:s,data:e})}if(t===`set-storage`){if(!n)throw Error(`pageId required for set-storage`);if(!o)throw Error(`storageType required for set-storage`);if(!s)throw Error(`storageKey required for set-storage`);if(c===void 0)throw Error(`storageValue required for set-storage`);return await l.session.getPage(n).evaluate(({type:e,key:t,value:n})=>{(e===`localStorage`?localStorage:sessionStorage).setItem(t,n)},{type:o,key:s,value:c}),l.resetIdleTimer(),B(`Set ${o}.${s}`,{ok:!0,storageType:o,key:s})}if(t===`clear-storage`){if(!n)throw Error(`pageId required for clear-storage`);if(!o)throw Error(`storageType required for clear-storage`);return await l.session.getPage(n).evaluate(e=>{(e===`localStorage`?localStorage:sessionStorage).clear()},o),l.resetIdleTimer(),B(`Cleared ${o}`,{ok:!0,storageType:o})}let u=N(!!r);if(!u.allowed)throw Error(u.reason??`Cookie access denied`);let d=await l.getContext().cookies();return l.resetIdleTimer(),B(JSON.stringify(d,null,2),{cookies:d})}}const gt=[`open`,`batch`,`read`,`act`,`navigate`,`network`,`console`,`fetch`,`eval`,`diff`,`screenshot`,`dialog`,`session`],_t=[`ui`,`headless`],vt=[`load`,`domcontentloaded`,`networkidle`],yt=[`click`,`type`,`press`,`hover`,`drag`,`select`,`scroll`,`upload`],bt=[`back`,`forward`,`reload`,`waitFor`],xt=[`enable`,`get`,`clear`,`export-har`],St=[`enable`,`get`,`clear`],Ct=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],wt=[`list`,`close`,`cookies`,`set-cookie`,`delete-cookie`,`clear-cookies`,`get-storage`,`set-storage`,`clear-storage`],Tt={action:l.enum(gt).describe(`Browser action to perform`),pageId:l.string().optional().describe(`Tracked browser page identifier`),label:l.string().optional().describe(`Human-readable label for the page (used as alternative to pageId)`),url:l.string().url().optional().describe(`URL to open or navigate to`),mode:l.enum(_t).optional().describe(`Browser launch mode (open only)`),forceNew:l.boolean().optional().describe(`Reserved for future tab reuse`),autoDialog:l.boolean().optional().describe(`Auto-accept alert/beforeunload dialogs (default: true). Set false to handle all dialogs manually.`),waitUntil:l.enum(vt).optional().describe(`Navigation readiness event`),kind:l.enum(yt).optional().describe(`Interaction kind (act only)`),ref:l.string().optional().describe(`Target ref alias`),selector:l.string().optional().describe(`Playwright selector`),element:l.string().optional().describe(`Human-readable element label (resolved via Playwright text selector). Prefer selector for precision.`),text:l.string().optional().describe(`Text to type`),key:l.string().optional().describe(`Key to press`),value:l.string().optional().describe(`Option value or drag target`),fromRef:l.string().optional().describe(`Drag source ref`),fromSelector:l.string().optional().describe(`Drag source selector`),toRef:l.string().optional().describe(`Drag target ref`),toSelector:l.string().optional().describe(`Drag target selector`),type:l.enum(bt).optional().describe(`Navigation type`),subAction:l.enum(xt).optional().describe(`Network sub-action: enable, get, clear, or export-har`),code:l.string().optional().describe(`JavaScript expression or IIFE evaluated in page context. Wrapped in Function("return (<code>)"). Multi-statement: use (() => { ...; return result; })(). No bare return or top-level semicolons.`),timeoutMs:l.number().min(1).max(6e4).optional().describe(`Timeout in milliseconds`),filter:l.object({resourceTypes:l.array(l.string()).optional(),urlPattern:l.string().optional(),excludeUrls:l.array(l.string()).optional()}).optional().describe(`Network capture filter for resource types and URL patterns`),showSensitive:l.boolean().optional().describe(`Include sensitive headers instead of redacting them`),includeBody:l.boolean().optional().describe(`Include truncated request bodies in captured entries`),bufferSize:l.number().int().min(1).max(1e4).optional().describe(`Buffer capacity (1-10000, default varies by action)`),consoleSubAction:l.enum(St).optional().describe(`Console sub-action: enable, get, or clear`),level:l.string().optional().describe(`Filter console entries by level (log/info/warn/error/debug)`),fetchUrl:l.string().optional().describe(`URL for fetch action (http/https only). Uses page cookies/session.`),fetchMethod:l.enum(Ct).optional().describe(`HTTP method for fetch action (default: GET)`),fetchHeaders:l.record(l.string(),l.string()).optional().describe(`Custom headers for fetch action`),fetchBody:l.string().optional().describe(`Request body for fetch action (POST/PUT/PATCH)`),includeResponseHeaders:l.boolean().optional().describe(`Include response headers in fetch output (default: true)`),fullPage:l.boolean().optional().describe(`Capture full page`),redactPasswords:l.boolean().optional().describe(`Mask password fields`),readMode:l.enum([`snapshot`,`dom`,`markdown`,`text`]).optional().describe(`Extraction mode for read action: snapshot (ARIA tree), dom (HTML), markdown (clean MD), text (plain text)`),clip:l.object({x:l.number(),y:l.number(),width:l.number(),height:l.number()}).optional().describe(`Capture a specific page region { x, y, width, height }`),format:l.enum([`png`,`jpeg`]).optional().describe(`Screenshot image format (default: png)`),quality:l.number().min(0).max(100).optional().describe(`JPEG quality 0-100 (only for jpeg format)`),cookies:l.array(l.object({name:l.string(),value:l.string(),url:l.string().optional(),domain:l.string().optional(),path:l.string().optional(),expires:l.number().optional(),httpOnly:l.boolean().optional(),secure:l.boolean().optional(),sameSite:l.enum([`Lax`,`None`,`Strict`]).optional()})).optional().describe(`Cookies to set (for session set-cookie action)`),name:l.string().optional().describe(`Cookie name for delete-cookie action`),storageType:l.enum([`localStorage`,`sessionStorage`]).optional().describe(`Storage type for get/set/clear-storage actions`),storageKey:l.string().optional().describe(`Storage key to get or set`),storageValue:l.string().optional().describe(`Value to set in storage`),accept:l.boolean().optional().describe(`Accept or dismiss dialog`),promptText:l.string().optional().describe(`Text for prompt dialog`),sessionAction:l.enum(wt).optional().describe(`Session sub-action (session only)`),confirm:l.boolean().optional().describe(`Explicit confirmation for cookie export`),steps:l.array(l.record(l.string(),l.unknown())).optional().describe(`Array of action steps for batch execution. Each step is an object with action + action-specific params.`)};function Et(e,t){return typeof e==`string`&&t.includes(e)}function K(e,t,n){let r=e[t];if(typeof r!=`string`)throw Error(`${n} requires ${t}`);return r}function Dt(e,t,n){let r=e[t];if(typeof r!=`boolean`)throw Error(`${n} requires ${t}`);return r}function q(e,t){let n=e[t];return typeof n==`string`?n:void 0}function J(e,t){let n=e[t];return typeof n==`boolean`?n:void 0}function Y(e,t){let n=e[t];return typeof n==`number`?n:void 0}function X(e,t){let n=e[t];return typeof n==`object`&&n?n:void 0}function Ot(e,t){let n=e.steps;if(!Array.isArray(n)||n.length===0)throw Error(`${t} requires non-empty steps`);return n.map((e,n)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`${t} step ${n+1} must be an object`);return e})}function Z(e,t,n,r){let i=e[t];if(!Et(i,n))throw Error(`${r} requires ${t}`);return i}function Q(e,t,n,r){let i=e[t];if(i!==void 0){if(!Et(i,n))throw Error(`${r} received invalid ${t}`);return i}}function kt(e,t){let n=q(e,`pageId`);if(n)return A(t).session.resolvePageId(n)}function $(e,t,n){let r=q(e,`pageId`);if(!r)throw Error(`${n} requires pageId`);return A(t).session.resolvePageId(r)}async function At(e,t,n){switch(Z(e,`action`,gt,`browser`)){case`open`:return t.openHandler({url:K(e,`url`,`browser(open)`),mode:Q(e,`mode`,_t,`browser(open)`),forceNew:J(e,`forceNew`),label:q(e,`label`),autoDialog:J(e,`autoDialog`),waitUntil:Q(e,`waitUntil`,vt,`browser(open)`)});case`batch`:{let r=Ot(e,`browser(batch)`),i=[],a=0;for(let e=0;e<r.length;e+=1){let o=r[e],s=q(o,`action`)??`unknown`;try{let r=await At(o,t,n);a+=1,i.push({index:e,action:s,ok:!0,result:r})}catch(t){i.push({index:e,action:s,ok:!1,error:t instanceof Error?t.message:String(t)})}}return B(`Batch completed: ${a}/${r.length} steps succeeded`,{total:r.length,succeeded:a,steps:i})}case`read`:return t.readHandler({pageId:$(e,n,`browser(read)`),mode:Q(e,`readMode`,[`snapshot`,`dom`,`markdown`,`text`],`browser(read)`),selector:q(e,`selector`)});case`act`:return t.actHandler({pageId:$(e,n,`browser(act)`),kind:Z(e,`kind`,yt,`browser(act)`),ref:q(e,`ref`),selector:q(e,`selector`),element:q(e,`element`),text:q(e,`text`),key:q(e,`key`),value:q(e,`value`),fromRef:q(e,`fromRef`),fromSelector:q(e,`fromSelector`),toRef:q(e,`toRef`),toSelector:q(e,`toSelector`)});case`navigate`:return t.navigateHandler({pageId:$(e,n,`browser(navigate)`),url:q(e,`url`),type:Q(e,`type`,bt,`browser(navigate)`),selector:q(e,`selector`),timeoutMs:Y(e,`timeoutMs`)});case`network`:return t.networkHandler({pageId:$(e,n,`browser(network)`),subAction:Z(e,`subAction`,xt,`browser(network)`),filter:X(e,`filter`),showSensitive:J(e,`showSensitive`),includeBody:J(e,`includeBody`),bufferSize:Y(e,`bufferSize`)});case`console`:return t.consoleHandler({pageId:$(e,n,`browser(console)`),subAction:Z(e,`consoleSubAction`,St,`browser(console)`),level:q(e,`level`),bufferSize:Y(e,`bufferSize`)});case`fetch`:return t.fetchHandler({pageId:$(e,n,`browser(fetch)`),url:K(e,`fetchUrl`,`browser(fetch)`),method:Q(e,`fetchMethod`,Ct,`browser(fetch)`),headers:X(e,`fetchHeaders`),body:q(e,`fetchBody`),timeoutMs:Y(e,`timeoutMs`),includeHeaders:J(e,`includeResponseHeaders`)});case`eval`:return t.evalHandler({pageId:$(e,n,`browser(eval)`),code:K(e,`code`,`browser(eval)`),timeoutMs:Y(e,`timeoutMs`)});case`diff`:return t.diffHandler({pageId:$(e,n,`browser(diff)`)});case`screenshot`:{let r=X(e,`clip`);return t.screenshotHandler({pageId:$(e,n,`browser(screenshot)`),ref:q(e,`ref`),selector:q(e,`selector`),fullPage:J(e,`fullPage`),redactPasswords:J(e,`redactPasswords`),clip:r,format:Q(e,`format`,[`png`,`jpeg`],`browser(screenshot)`),quality:Y(e,`quality`)})}case`dialog`:return t.dialogHandler({pageId:$(e,n,`browser(dialog)`),accept:Dt(e,`accept`,`browser(dialog)`),promptText:q(e,`promptText`)});case`session`:{let r=e.cookies;return t.sessionHandler({action:Z(e,`sessionAction`,wt,`browser(session)`),pageId:kt(e,n),confirm:J(e,`confirm`),cookies:r,name:q(e,`name`),storageType:Q(e,`storageType`,[`localStorage`,`sessionStorage`],`browser(session)`),storageKey:q(e,`storageKey`),storageValue:q(e,`storageValue`)})}}}function jt(e,t){let n=Ee(t),r={openHandler:ut(n),readHandler:dt(n),actHandler:Oe(n),navigateHandler:$e(n),networkHandler:lt(n),consoleHandler:Me(n),fetchHandler:Qe(n),evalHandler:Fe(n),diffHandler:Pe(n),screenshotHandler:ft(n),dialogHandler:Ne(n),sessionHandler:ht(n)};e.registerTool(`browser`,{title:`Browser Automation`,description:`Unified browser automation tool. Actions:
|
|
19
19
|
- open: Launch a browser page (url required, mode/waitUntil optional)
|
|
20
20
|
- batch: Execute multiple browser actions sequentially (steps required)
|
|
21
21
|
- read: Get accessibility snapshot of a page (pageId required)
|
|
@@ -28,4 +28,4 @@ import{createRequire as e}from"node:module";import{createHash as t,randomUUID as
|
|
|
28
28
|
- diff: Compare the current accessibility snapshot to the previous one (pageId required)
|
|
29
29
|
- screenshot: Capture page or element screenshot (pageId required)
|
|
30
30
|
- dialog: Accept or dismiss next browser dialog (pageId + accept required)
|
|
31
|
-
- session: List pages, close a page, or export cookies (sessionAction required)`,inputSchema:
|
|
31
|
+
- session: List pages, close a page, or export cookies (sessionAction required)`,inputSchema:Tt,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},async e=>At(e,r,n))}export{O as BrowserEngine,D as DEFAULT_BROWSER_CONFIG,E as SessionRegistry,w as autoSelectMode,_e as closeEngine,ge as detectDisplayAvailable,he as ensureBrowserInstalled,pe as getDefaultBrowsersPath,A as getEngine,T as getLaunchArgs,me as isBrowserInstalled,N as isCookieAccessAllowed,M as isUrlAllowed,Ce as redactPasswordFields,jt as registerBrowserTools,C as resolveBrowsersPath,P as validateEvalResult};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{randomBytes as e,timingSafeEqual as t}from"node:crypto";import*as n from"node:fs";import{join as r}from"node:path";import{homedir as i}from"node:os";const a=r(i(),`.aikit`);function o(e){let t=e?.tokenDir??a;return{tokenDir:t,tokenFile:r(t,`token`)}}function s(e,n){let r=Buffer.from(e),i=Buffer.from(n);return r.length===i.length?t(r,i):!1}function c(t){let{tokenDir:r,tokenFile:i}=o(t);if(n.existsSync(i)){let e=n.readFileSync(i,`utf-8`).trim();if(e.length>0)return e}n.existsSync(r)||n.mkdirSync(r,{recursive:!0,mode:448});let a=e(32).toString(`hex`);return n.writeFileSync(i,a,{mode:384}),a}function l(e){return(t,n,r)=>{if(t.path===`/health`||t.path===`/healthz`){r();return}let i=t.headers.authorization;if(!i?.startsWith(`Bearer `)){n.status(401).json({error:`Unauthorized: Bearer token required`});return}if(!s(i.slice(7).trim(),e)){n.status(401).json({error:`Unauthorized: Invalid token`});return}r()}}function u(e){try{let{tokenFile:t}=o(e);return n.readFileSync(t,`utf-8`).trim()}catch{return}}export{l as authMiddleware,c as getOrCreateToken,u as readToken};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{createHash as e,randomUUID as t}from"node:crypto";import{readFileSync as n}from"node:fs";import{dirname as r,isAbsolute as i,join as a,resolve as o}from"node:path";import{fileURLToPath as s,pathToFileURL as c}from"node:url";import{parseArgs as l}from"node:util";import{createLogger as u,serializeError as d}from"../../core/dist/index.js";import{GIT_REF_SLUG_PATTERN as f,gitAvailable as p,gitCommitToRef as m,gitExec as h,slugForRef as g}from"../../tools/dist/index.js";import{mkdir as _,readFile as v,readdir as y,stat as b,unlink as x,writeFile as S}from"node:fs/promises";const C=`__pending__:`;function w(e){return e.startsWith(C)}function T(e){let t=e.headers[`mcp-session-id`];return Array.isArray(t)?t[0]:t}function E(e,t,n,r){e.status(t).json({jsonrpc:`2.0`,error:{code:n,message:r},id:null})}var D=class{options;runtimes=new Map;maxSessions;sessionTimeoutMs;now;constructor(e){this.options=e,this.maxSessions=e.maxSessions??8,this.sessionTimeoutMs=(e.sessionTimeoutMinutes??30)*60*1e3,this.now=e.now??(()=>Date.now())}hasSession(e){return this.runtimes.has(e)}getSessionCount(){return this.runtimes.size}async handleRequest(e,t,n=e.body){let r=T(e),i=r?this.runtimes.get(r):void 0;if(r&&!i){E(t,404,-32001,`Session not found`);return}if(!i){if(e.method!==`POST`){E(t,400,-32e3,`Session required`);return}if(i=await this.createRuntime(t),!i)return}await this.withRuntimeLock(i,async()=>{await i.transport.handleRequest(e,t,n),i.lastAccessAt=this.now();let r=i.transport.sessionId??i.id;e.method!==`DELETE`&&!w(r)&&this.options.onSessionActivity?.(r),e.method===`DELETE`&&!w(r)&&await this.closeSession(r,{closeTransport:!1})})}async closeExpiredSessions(){let e=[...this.runtimes.values()].filter(e=>this.now()-e.lastAccessAt>=this.sessionTimeoutMs).map(e=>e.id);for(let t of e)await this.closeSession(t);return e.length}async closeSession(e,t={}){let n=this.runtimes.get(e);return n?(this.runtimes.delete(e),t.notifySessionEnd!==!1&&!w(e)&&this.options.onSessionEnd?.(e),t.closeTransport!==!1&&await n.transport.close().catch(()=>void 0),await n.server.close().catch(()=>void 0),!0):!1}async closeAll(){let e=[...this.runtimes.keys()];for(let t of e)await this.closeSession(t)}async createRuntime(e){if(await this.closeExpiredSessions(),this.runtimes.size>=this.maxSessions){E(e,503,-32003,`Session capacity reached`);return}let n=this.now(),r=await this.options.createServer(),i={id:`${C}${t()}`,transport:void 0,createdAt:n,lastAccessAt:n,server:r,requestChain:Promise.resolve()},a=this.options.createTransport({sessionIdGenerator:()=>t(),onsessioninitialized:async e=>{this.runtimes.delete(i.id),i.id=e,this.runtimes.set(e,i),this.options.onSessionStart?.(e)},onsessionclosed:async e=>{e&&await this.closeSession(e,{closeTransport:!1})}});return i.transport=a,a.onclose=()=>{let e=i.transport.sessionId??i.id;this.closeSession(e,{closeTransport:!1})},this.runtimes.set(i.id,i),await r.connect(a),i}async withRuntimeLock(e,t){let n=e.requestChain,r;e.requestChain=new Promise(e=>{r=e}),await n;try{await t()}finally{r()}}};function O(e){let t=e.includes(`T`)?e:`${e.replace(` `,`T`)}Z`;return Date.parse(t)}var k=class{stateStore;options;gcTimer=null;constructor(e,t={}){this.stateStore=e,this.options=t}onSessionStart(e,t){this.stateStore.sessionCreate(e,t)}onSessionActivity(e){this.stateStore.sessionTouch(e)}onSessionEnd(e){this.stateStore.sessionDelete(e)}startGC(){if(this.gcTimer)return;let e=(this.options.gcIntervalMinutes??5)*60*1e3;this.gcTimer=setInterval(()=>{this.runGC()},e),this.gcTimer.unref()}runGC(){let e=this.getStaleSessionIds();for(let t of e)this.options.onBeforeSessionDelete?.(t),this.stateStore.sessionDelete(t);return e.length}stop(){this.gcTimer&&=(clearInterval(this.gcTimer),null)}getActiveSessions(){return this.stateStore.sessionList().length}listSessions(){return this.stateStore.sessionList()}getStaleSessionIds(e=Date.now()){let t=(this.options.staleTimeoutMinutes??30)*60*1e3;return this.stateStore.sessionList().filter(n=>{let r=O(n.lastActivity);return Number.isFinite(r)&&e-r>=t}).map(e=>e.sessionId)}},A=class{baseDir;constructor(e){this.baseDir=e}async initialize(){await _(this.baseDir,{recursive:!0})}async read(e){let t=a(this.baseDir,e);try{return await v(t,`utf-8`)}catch(e){if(e.code===`ENOENT`)return null;throw e}}async write(e,t,n){let i=a(this.baseDir,e);await _(r(i),{recursive:!0}),await S(i,t,{encoding:`utf-8`,flag:n?.exclusive?`wx`:`w`})}async delete(e){let t=a(this.baseDir,e);try{return await x(t),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async list(e){let t=[],n=e?[e]:await this.listDirectories();for(let e of n){let n=a(this.baseDir,e),r;try{r=await y(n)}catch{continue}for(let n of r){if(!n.endsWith(`.md`))continue;let r=`${e}/${n}`,i=a(this.baseDir,r);try{let e=await b(i);t.push({path:r,size:e.size})}catch{}}}return t}async exists(e){let t=a(this.baseDir,e);try{return await b(t),!0}catch{return!1}}async listDirectories(){try{return(await y(this.baseDir,{withFileTypes:!0})).filter(e=>e.isDirectory()&&/^[a-z][a-z0-9-]*$/.test(e.name)).map(e=>e.name)}catch{return[]}}async close(){}};const j=50*1024,M=`refs/aikit/knowledge`,N=u(`server`);var P=class{curatedDir;store;embedder;adapter;constructor(e,t,n,r=new A(e)){this.curatedDir=e,this.store=t,this.embedder=n,this.adapter=r}async remember(e,t,n,r=[]){this.validateCategoryName(n),this.validateContentSize(t);let i=this.slugify(e),a=await this.uniqueRelativePath(n,i),o=new Date().toISOString(),s={title:e,category:n,tags:r,created:o,updated:o,version:1,origin:`curated`,changelog:[{version:1,date:o,reason:`Initial creation`}]},c=this.serializeFile(t,s);try{await this.adapter.write(a,c,{exclusive:!0})}catch(e){throw e.code===`EEXIST`?Error(`Concurrent write collision for "${a}" — retry the operation`):e}try{await this.indexCuratedFile(a,t,s)}catch(e){throw await this.adapter.delete(a).catch(()=>{}),Error(`Remember failed: wrote file but indexing failed — rolled back. ${e.message}`)}return this.gitCommitKnowledge(a,c,`remember: ${e}\n\nCategory: ${n}\nTags: ${r.join(`, `)}`),{path:a}}async update(e,t,n){e=this.guardPath(e),this.validateContentSize(t);let r=await this.adapter.read(e);if(r==null)throw Error(`Curated entry not found: ${e}`);let{frontmatter:i}=this.parseFile(r),a=(i.version??1)+1,o=new Date().toISOString();i.version=a,i.updated=o,i.changelog=[...i.changelog??[],{version:a,date:o,reason:n}];let s=this.serializeFile(t,i);return await this.adapter.write(e,s),await this.indexCuratedFile(e,t,i),this.gitCommitKnowledge(e,s,`update(v${a}): ${i.title}\n\nReason: ${n}\nVersion: ${a}`),{path:e,version:a}}async forget(e,t){if(e=this.guardPath(e),!await this.adapter.delete(e))throw Error(`Curated entry not found: ${e}`);let n=`.ai/curated/${e}`;return await this.store.deleteBySourcePath(n).catch(e=>{N.warn(`File deleted but vector cleanup failed`,{sourcePath:n,...d(e)})}),this.gitDeleteKnowledgeRef(e),{path:e}}async history(e,t=20){if(e=this.guardPath(e),!p(this.curatedDir))return[];let n=this.knowledgeRefForPath(e);if(!n)return[];let r=h([`log`,`--format=%H|%aI|%s`,n,`-n`,String(t)],this.curatedDir);return r?r.split(`
|
|
3
|
+
`).filter(Boolean).map(e=>{let[t,n,...r]=e.split(`|`);return{sha:t,date:n,message:r.join(`|`)}}):[]}async diff(e,t,n){if(e=this.guardPath(e),!p(this.curatedDir))return``;if(!n){let r=await this.history(e,2);if(r.length===0)return``;n=r[0].sha,r.length>1&&!t&&(t=r[1].sha)}return t&&n?h([`diff`,`${t}:entry.md`,`${n}:entry.md`],this.curatedDir)??``:n?h([`show`,`${n}:entry.md`],this.curatedDir)??``:``}async recover(e){if(e=this.guardPath(e),await this.adapter.exists(e))throw Error(`Entry already exists on filesystem: ${e}. Use update() instead.`);if(!p(this.curatedDir))return null;let t=this.knowledgeRefForPath(e);if(!t)return null;let n=h([`show`,`${t}:entry.md`],this.curatedDir);if(!n)return null;await this.adapter.write(e,n);let{frontmatter:r,content:i}=this.parseFile(n);return await this.indexCuratedFile(e,i,r),{path:e,version:r.version??1}}async listOrphaned(){if(!p(this.curatedDir))return[];let e=h([`for-each-ref`,`--format=%(refname)|%(subject)`,`${M}/`],this.curatedDir);if(!e)return[];let t=[];for(let n of e.split(`
|
|
4
|
+
`).filter(Boolean)){let[e,...r]=n.split(`|`),i=r.join(`|`),a=`${e.replace(`${M}/`,``)}.md`;await this.adapter.exists(a)||t.push({ref:e,path:a,lastMessage:i})}return t}async read(e){e=this.guardPath(e);let t=await this.adapter.read(e);if(t==null)throw Error(`Curated entry not found: ${e}`);let{frontmatter:n,content:r}=this.parseFile(t),i=e.split(`/`)[0];return{path:e,title:n.title??e,category:i,tags:n.tags??[],version:n.version??1,created:n.created??``,updated:n.updated??``,contentPreview:r.slice(0,200),content:r}}async list(e){let t=[],n=e?.category?[e.category]:await this.discoverCategories();for(let r of n){let n=await this.adapter.list(r);for(let i of n){let n=await this.adapter.read(i.path);if(n==null)continue;let{frontmatter:a,content:o}=this.parseFile(n);e?.tag&&!(a.tags??[]).includes(e.tag)||t.push({path:i.path,title:a.title??i.path,category:r,tags:a.tags??[],version:a.version??1,created:a.created??``,updated:a.updated??``,contentPreview:o.slice(0,200)})}}return t}async reindexAll(){let e=await this.discoverCategories(),t=[],n=[];for(let r of e){let e=await this.adapter.list(r);for(let r of e){let e=r.path;try{let r=await this.adapter.read(e);if(r==null){t.push(`${e}: read failed`);continue}let{frontmatter:i,content:a}=this.parseFile(r);n.push({relativePath:e,content:a,frontmatter:i})}catch(n){N.error(`Failed to read curated file`,{relativePath:e,...d(n)}),t.push(`${e}: read failed`)}}}if(n.length===0)return{indexed:0,errors:t};let r=await this.embedder.embedBatch(n.map(e=>e.content)),i=new Date().toISOString(),a=n.map(e=>{let t=`.ai/curated/${e.relativePath}`;return{id:this.hashId(t,0),content:e.content,sourcePath:t,contentType:`curated-knowledge`,headingPath:e.frontmatter.title,chunkIndex:0,totalChunks:1,startLine:1,endLine:e.content.split(`
|
|
5
|
+
`).length,fileHash:this.hash(e.content),indexedAt:i,origin:`curated`,tags:e.frontmatter.tags,category:e.frontmatter.category,version:e.frontmatter.version}});return await this.store.upsert(a,r),{indexed:n.length,errors:t}}gitCommitKnowledge(e,t,n){try{if(!p(this.curatedDir))return;let r=this.knowledgeRefForPath(e);if(!r)return;m(r,`entry.md`,t,n,this.curatedDir)}catch{}}gitDeleteKnowledgeRef(e){try{if(!p(this.curatedDir))return;let t=this.knowledgeRefForPath(e);if(!t)return;h([`update-ref`,`-d`,t],this.curatedDir)}catch{}}knowledgeRefForPath(e){let t=e.replace(/\.md$/,``).split(`/`).map(e=>g(e)).join(`/`);return t.split(`/`).every(e=>f.test(e))?`${M}/${t}`:null}async indexCuratedFile(e,t,n){let r=await this.embedder.embed(t),i=`.ai/curated/${e}`,a=new Date().toISOString(),o={id:this.hashId(i,0),content:t,sourcePath:i,contentType:`curated-knowledge`,headingPath:n.title,chunkIndex:0,totalChunks:1,startLine:1,endLine:t.split(`
|
|
6
|
+
`).length,fileHash:this.hash(t),indexedAt:a,origin:`curated`,tags:n.tags,category:n.category,version:n.version};await this.store.upsert([o],[r])}async discoverCategories(){return this.adapter.listDirectories()}guardPath(e){let t=e.replace(/^\.ai\/curated\//,``);if(t.endsWith(`.md`)||(t+=`.md`),t.includes(`..`)||i(t))throw Error(`Invalid path: ${t}. Must be relative within .ai/curated/ directory.`);let n=t.split(`/`)[0];return this.validateCategoryName(n),t}validateCategoryName(e){if(!/^[a-z][a-z0-9-]*$/.test(e))throw Error(`Invalid category name: "${e}". Must be lowercase kebab-case (e.g., "decisions", "api-contracts").`)}validateContentSize(e){if(Buffer.byteLength(e,`utf-8`)>j)throw Error(`Content exceeds maximum size of ${j/1024}KB`)}slugify(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-|-$/g,``).slice(0,80)}async uniqueRelativePath(e,t){let n=`${e}/${t}.md`;if(!await this.adapter.exists(n))return n;for(let n=2;n<=100;n++){let r=`${e}/${t}-${n}.md`;if(!await this.adapter.exists(r))return r}throw Error(`Too many entries with slug "${t}" in category "${e}"`)}hash(t){return e(`sha256`).update(t).digest(`hex`).slice(0,16)}hashId(e,t){return this.hash(`${e}::${t}`)}serializeFile(e,t){return`${[`---`,`title: "${t.title.replace(/"/g,`\\"`)}"`,`category: ${t.category}`,`tags: [${t.tags.map(e=>`"${e}"`).join(`, `)}]`,`created: ${t.created}`,`updated: ${t.updated}`,`version: ${t.version}`,`origin: ${t.origin}`,`changelog:`,...t.changelog.map(e=>` - version: ${e.version}\n date: ${e.date}\n reason: "${e.reason.replace(/"/g,`\\"`)}"`),`---`].join(`
|
|
7
|
+
`)}\n\n${e}\n`}parseFile(e){let t=e.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);if(!t)return{frontmatter:{title:`Untitled`,category:`notes`,tags:[],created:``,updated:``,version:1,origin:`curated`,changelog:[]},content:e};let n=t[1],r=t[2].trim(),i={},a=[],o=n.split(`
|
|
8
|
+
`),s=!1,c={};for(let e of o){if(/^changelog:\s*$/.test(e)){s=!0;continue}if(s){let t=e.match(/^\s+-\s+version:\s*(\d+)$/);if(t){c.version!=null&&a.push(c),c={version:parseInt(t[1],10)};continue}let n=e.match(/^\s+date:\s*(.+)$/);if(n){c.date=n[1].trim();continue}let r=e.match(/^\s+reason:\s*"?(.*?)"?\s*$/);if(r){c.reason=r[1];continue}/^\w/.test(e)&&(s=!1,c.version!=null&&a.push(c),c={});continue}let t=e.match(/^(\w+):\s*(.*)$/);if(t){let e=t[1],n=t[2];typeof n==`string`&&n.startsWith(`[`)&&n.endsWith(`]`)?n=n.slice(1,-1).split(`,`).map(e=>e.trim().replace(/^"|"$/g,``)).filter(e=>e.length>0):typeof n==`string`&&/^\d+$/.test(n)?n=parseInt(n,10):typeof n==`string`&&n.startsWith(`"`)&&n.endsWith(`"`)&&(n=n.slice(1,-1)),i[e]=n}}return c.version!=null&&a.push(c),{frontmatter:{title:i.title??`Untitled`,category:i.category??`notes`,tags:i.tags??[],created:i.created??``,updated:i.updated??``,version:i.version??1,origin:`curated`,changelog:a},content:r}}};function F(){try{let e=o(r(s(import.meta.url)),`..`,`..`,`..`,`package.json`);return JSON.parse(n(e,`utf-8`)).version??`0.0.0`}catch{return`0.0.0`}}const I=u(`server`);function L(){return process.env.AIKIT_TRANSPORT?process.env.AIKIT_TRANSPORT:process.stdin.isTTY?`http`:`stdio`}function R(){let e=process.argv[1];if(!e)return!1;try{return import.meta.url===c(e).href}catch{return!1}}function z(e){let t=e.headers[`mcp-session-id`];return Array.isArray(t)?t[0]:t}function B(e,t){let n=process.env[e];if(!n)return t;let r=Number.parseInt(n,10);return Number.isFinite(r)&&r>0?r:t}function V(){return R()?l({allowPositionals:!0,options:{transport:{type:`string`,default:L()},port:{type:`string`,default:process.env.AIKIT_PORT??`3210`}}}).values:{transport:L(),port:process.env.AIKIT_PORT??`3210`}}async function H(){let e=F(),n=V();process.on(`unhandledRejection`,e=>{I.error(`Unhandled rejection`,d(e))}),process.on(`uncaughtException`,e=>{I.error(`Uncaught exception — exiting`,d(e)),process.exit(1)});let r=e=>{e.then(async()=>{try{let{shouldRunStartupPrune:e,prune:t,markPruneRun:n}=await import(`../../tools/dist/index.js`);if(!e())return;let r=await t({});n(),r.totalBytesFreed>0&&I.info(`Storage maintenance complete`,{forgeOrphans:r.forgeGroundOrphans.count,legacyLance:r.legacyLance.count,bytesFreed:r.totalBytesFreed})}catch(e){I.warn(`Startup maintenance failed (non-critical)`,d(e))}}).catch(()=>{})};if(I.info(`Starting MCP AI Kit server`,{version:e}),n.transport===`http`){let[{default:e},{loadConfig:i,resolveIndexMode:a},{registerDashboardRoutes:o,resolveDashboardDir:s},{registerSettingsRoutes:c,resolveSettingsDir:l},{createSettingsRouter:u},{authMiddleware:f,getOrCreateToken:p}]=await Promise.all([import(`express`),import(`./config-PfoXsIC3.js`),import(`./dashboard-static-CRfR1yKU.js`),import(`./settings-static-B3lnYvcb.js`),import(`./routes-Afg7J7xK.js`),import(`./auth-lzZKfxlV.js`)]),m=i();I.info(`Config loaded`,{sourceCount:m.sources.length,storePath:m.store.path});let h=e();h.use(e.json());let g=Number(n.port),_=B(`AIKIT_HTTP_MAX_SESSIONS`,8),v=B(`AIKIT_HTTP_SESSION_TIMEOUT_MINUTES`,30),y=B(`AIKIT_HTTP_SESSION_GC_INTERVAL_MINUTES`,5);h.use((e,t,n)=>{if(t.setHeader(`Access-Control-Allow-Origin`,process.env.AIKIT_CORS_ORIGIN??`http://localhost:${g}`),t.setHeader(`Access-Control-Allow-Methods`,`GET, POST, PUT, PATCH, DELETE, OPTIONS`),t.setHeader(`Access-Control-Allow-Headers`,`Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID`),t.setHeader(`Access-Control-Expose-Headers`,`Mcp-Session-Id`),e.method===`OPTIONS`){t.status(204).end();return}n()});let b=p();console.error(`[aikit] Auth token: ~/.aikit/token`),h.use(f(b)),o(h,s(),I);let x=new Date().toISOString();h.use(`/settings/api`,u({log:I,mcpInfo:()=>({transport:`http`,port:g,pid:process.pid,startedAt:x})})),c(h,l(),I),h.get(`/health`,(e,t)=>{t.json({status:`ok`})});let S=!1,C=null,w=null,T=null,E=null,O=null,A=null,j=null,M=Promise.resolve(),N=async(e,n)=>{if(!S||!T||!E){n.status(503).json({jsonrpc:`2.0`,error:{code:-32603,message:`Server initializing — please retry in a few seconds`},id:null});return}let r=M,i;M=new Promise(e=>{i=e}),await r;try{let r=z(e);if(!A){if(r){n.status(404).json({jsonrpc:`2.0`,error:{code:-32001,message:`Session not found`},id:null});return}let e=new E({sessionIdGenerator:()=>t(),onsessioninitialized:async e=>{j=e,w?.onSessionStart(e,{transport:`http`})},onsessionclosed:async e=>{e&&w?.onSessionEnd(e),j=null}});e.onclose=()=>{A===e&&(A=null),j===e.sessionId&&(j=null)},A=e,await T.connect(e)}let i=A;await i.handleRequest(e,n,e.body),e.method!==`DELETE`&&(!r&&i.sessionId?(j=i.sessionId,w?.onSessionStart(i.sessionId,{transport:`http`}),w?.onSessionActivity(i.sessionId)):r&&w?.onSessionActivity(r))}catch(e){if(I.error(`MCP handler error`,d(e)),!n.headersSent){let t=e instanceof Error?e.message:String(e),r=t.includes(`Not Acceptable`);n.status(r?406:500).json({jsonrpc:`2.0`,error:{code:r?-32e3:-32603,message:r?t:`Internal server error`},id:null})}}finally{i()}},P=async(e,t)=>{let n=z(e);if(O&&(!A||n!==j)){await O.handleRequest(e,t,e.body);return}await N(e,t)};h.post(`/mcp`,P),h.get(`/mcp`,P),h.delete(`/mcp`,P);let F=h.listen(g,`127.0.0.1`,()=>{I.info(`MCP server listening`,{url:`http://127.0.0.1:${g}/mcp`,port:g}),setTimeout(async()=>{try{let[{createLazyServer:e,createMcpServer:t,ALL_TOOL_NAMES:n},{StreamableHTTPServerTransport:i},{checkForUpdates:o,autoUpgradeScaffold:s}]=await Promise.all([import(`./server-BhQwVWsr.js`),import(`@modelcontextprotocol/sdk/server/streamableHttp.js`),import(`./version-check-gazMo-D4.js`)]);o(),s();let c=a(m),l=e(m,c);T=l.server,E=i,S=!0,I.info(`MCP server configured (lazy — AI Kit initializing in background)`,{toolCount:n.length,resourceCount:2}),l.startInit(),l.ready.then(()=>{if(!l.aikit)throw Error(`AI Kit components are not available after initialization`);w=new k(l.aikit.stateStore,{staleTimeoutMinutes:v,gcIntervalMinutes:y,onBeforeSessionDelete:e=>{if(j===e&&A){let e=A;A=null,j=null,e.close().catch(()=>void 0)}O?.closeSession(e,{notifySessionEnd:!1})}}),O=new D({createServer:()=>{if(!l.aikit)throw Error(`AI Kit components are not available after initialization`);return t(l.aikit,m)},createTransport:e=>new i(e),maxSessions:_,sessionTimeoutMinutes:v,onSessionStart:e=>w?.onSessionStart(e,{transport:`http`}),onSessionActivity:e=>w?.onSessionActivity(e),onSessionEnd:e=>w?.onSessionEnd(e)}),w.startGC(),j&&(w.onSessionStart(j,{transport:`http`}),w.onSessionActivity(j)),I.info(`HTTP session runtime ready`,{maxSessions:_,sessionTimeoutMinutes:v,gcIntervalMinutes:y})}).catch(e=>I.error(`Failed to start session manager`,d(e))),c===`auto`?l.ready.then(async()=>{try{let e=m.sources.map(e=>e.path).join(`, `);I.info(`Running initial index`,{sourcePaths:e}),await l.runInitialIndex(),I.info(`Initial index complete`)}catch(e){I.error(`Initial index failed; will retry on aikit_reindex`,d(e))}}).catch(e=>I.error(`AI Kit init or indexing failed`,d(e))):c===`smart`?l.ready.then(async()=>{try{if(!l.aikit)throw Error(`AI Kit components are not available after initialization`);let{SmartIndexScheduler:e}=await import(`../../indexer/dist/index.js`),t=new e(l.aikit.indexer,m,l.aikit.store),n=l.aikit.store;C=t,t.start(),n.onBeforeClose&&n.onBeforeClose(()=>t.stop()),l.setSmartScheduler(t),I.info(`Smart index scheduler started (HTTP mode)`)}catch(e){I.error(`Failed to start smart index scheduler`,d(e))}}).catch(e=>I.error(`AI Kit initialization failed`,d(e))):(l.ready.catch(e=>I.error(`AI Kit initialization failed`,d(e))),I.info(`Initial full indexing skipped in HTTP mode`,{indexMode:c})),r(l.ready)}catch(e){I.error(`Failed to load server modules`,d(e))}},100)}),L=async e=>{I.info(`Shutdown signal received`,{signal:e}),C?.stop(),w?.stop(),await O?.closeAll().catch(()=>void 0),j&&w?.onSessionEnd(j),A&&(await A.close().catch(()=>void 0),A=null,j=null),F.close(),T&&await T.close(),process.exit(0)};process.on(`SIGINT`,()=>L(`SIGINT`)),process.on(`SIGTERM`,()=>L(`SIGTERM`))}else{let[{loadConfig:e,reconfigureForWorkspace:t,resolveIndexMode:n},{createLazyServer:i},{checkForUpdates:a,autoUpgradeScaffold:o},{RootsListChangedNotificationSchema:c}]=await Promise.all([import(`./config-PfoXsIC3.js`),import(`./server-BhQwVWsr.js`),import(`./version-check-gazMo-D4.js`),import(`@modelcontextprotocol/sdk/types.js`)]),l=e();I.info(`Config loaded`,{sourceCount:l.sources.length,storePath:l.store.path}),a(),o();let u=n(l),f=i(l,u),{server:p,startInit:m,ready:h,runInitialIndex:g}=f,{StdioServerTransport:_}=await import(`@modelcontextprotocol/sdk/server/stdio.js`),v=new _;await p.connect(v),I.info(`MCP server started`,{transport:`stdio`});let y=e=>{if(e.length===0)return!1;let n=e[0].uri,r=n.startsWith(`file://`)?s(n):n;return I.info(`MCP roots resolved`,{rootUri:n,rootPath:r,rootCount:e.length}),t(l,r),l.allRoots=e.map(e=>{let t=e.uri;return t.startsWith(`file://`)?s(t):t}),!0},b=!1;try{b=y((await p.server.listRoots()).roots),b||I.info(`No MCP roots yet; waiting for roots/list_changed notification`)}catch(e){I.warn(`MCP roots/list not supported by client; using cwd fallback`,{cwd:process.cwd(),...d(e)}),b=!0}b||=await new Promise(e=>{let t=setTimeout(()=>{I.warn(`Timed out waiting for MCP roots/list_changed; using cwd fallback`,{cwd:process.cwd()}),e(!1)},5e3);p.server.setNotificationHandler(c,async()=>{clearTimeout(t);try{e(y((await p.server.listRoots()).roots))}catch(t){I.warn(`roots/list retry failed after notification`,d(t)),e(!1)}})}),m();let x=null,S=()=>{x&&clearTimeout(x),x=setTimeout(async()=>{I.info(`Auto-shutdown: no activity for 30 minutes — shutting down gracefully`);try{let e=f.aikit;e&&await Promise.all([e.embedder.shutdown?.().catch(()=>{})??Promise.resolve(),e.graphStore.close().catch(()=>{}),e.store.close().catch(()=>{})])}catch{}process.exit(0)},18e5),x.unref&&x.unref()};S(),process.stdin.on(`data`,()=>S()),h.catch(e=>{I.error(`Initialization failed — server will continue with limited tools`,d(e))}),u===`auto`?g().catch(e=>I.error(`Initial index failed`,d(e))):u===`smart`?h.then(async()=>{try{if(!f.aikit)throw Error(`AI Kit components are not available after initialization`);let{SmartIndexScheduler:e}=await import(`../../indexer/dist/index.js`),t=new e(f.aikit.indexer,l,f.aikit.store),n=f.aikit.store;t.start(),n.onBeforeClose&&n.onBeforeClose(()=>t.stop()),f.setSmartScheduler(t),I.info(`Smart index scheduler started (stdio mode)`)}catch(e){I.error(`Failed to start smart index scheduler`,d(e))}}).catch(e=>I.error(`AI Kit init failed for smart scheduler`,d(e))):I.warn(`Initial full indexing skipped; use aikit_reindex to index manually`,{indexMode:u}),r(h)}}H();export{A as n,P as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{_ as e,a as t,l as n,t as r}from"./supersession-BIV-v6JG.js";import{existsSync as i,readFileSync as a}from"node:fs";import{dirname as o,resolve as s}from"node:path";import{fileURLToPath as c}from"node:url";import{AIKIT_PATHS as l,EMBEDDING_DEFAULTS as u,createLogger as d,getPartitionDir as f,isUserInstalled as p,registerWorkspace as m,serializeError as h}from"../../core/dist/index.js";const g=o(c(import.meta.url)),_=d(`server`),v=[`auto`,`manual`,`smart`],y={model:u.model,dimensions:u.dimensions,childProcess:!0,idleTimeoutMs:6e4};function b(e){return typeof e==`string`&&v.includes(e)}function x(e,t,n){let r=s(e),i=s(t);if(!r.startsWith(i))throw Error(`Config ${n} path escapes workspace root: ${e} is not under ${t}`);return r}function S(e){let t=process.env.AIKIT_INDEX_MODE;if(b(t))return t;if(e.indexMode)return e.indexMode;let n=process.env.AIKIT_AUTO_INDEX;return n===void 0?e.autoIndex===void 0?`smart`:e.autoIndex?`auto`:`manual`:n===`true`?`auto`:`manual`}function C(){let c=process.env.AIKIT_CONFIG_PATH??(i(s(process.cwd(),`aikit.config.json`))?s(process.cwd(),`aikit.config.json`):s(g,`..`,`..`,`..`,`aikit.config.json`));try{if(!i(c))return _.info(`No config file found, using defaults`,{configPath:c}),w();let u=a(c,`utf-8`),d=JSON.parse(u);if(d.embedding={...y,...d.embedding},d.memory={retention:{...t,...d.memory?.retention},lessons:{...n,...d.memory?.lessons},consolidation:{...e,...d.memory?.consolidation},supersession:{...r,...d.memory?.supersession}},!d.sources||!Array.isArray(d.sources)||d.sources.length===0)throw Error(`Config must have at least one source`);if(!d.store?.path)throw Error(`Config must specify store.path`);if(d.autoIndex!==void 0&&typeof d.autoIndex!=`boolean`)throw Error(`Config autoIndex must be a boolean`);if(d.indexMode!==void 0&&!b(d.indexMode))throw Error(`Config indexMode must be one of: ${v.join(`, `)}`);let f=o(c);return d.sources=d.sources.map(e=>({...e,path:x(s(f,e.path),f,`source`)})),d.store.path=x(s(f,d.store.path),f,`store`),d.curated=d.curated??{path:l.aiCurated},d.curated.path=x(s(f,d.curated.path),f,`curated`),T(d,f),d.indexMode=S(d),d}catch(e){return _.error(`Failed to load config`,{configPath:c,...h(e)}),_.warn(`Falling back to default configuration`,{configPath:c}),w()}}function w(){let i=process.env.AIKIT_WORKSPACE_ROOT??process.cwd(),a={sources:[{path:i,excludePatterns:[`node_modules/**`,`dist/**`,`.git/**`,`coverage/**`,`*.lock`,`pnpm-lock.yaml`]}],serverName:`aikit`,indexing:{chunkSize:1500,chunkOverlap:200,minChunkSize:100},embedding:{...y},store:{backend:`sqlite-vec`,path:s(i,l.data)},curated:{path:s(i,l.aiCurated)},memory:{retention:{...t},lessons:{...n},consolidation:{...e},supersession:{...r}},onboardDir:s(i,l.aiContext),stateDir:s(i,l.state)};return T(a,i),a.indexMode=S(a),a}function T(e,t){if(!p())return;let n=m(t);e.store.path=s(f(n.partition)),e.onboardDir=s(f(n.partition),`onboard`),e.stateDir=s(f(n.partition),`state`),e.curated={path:s(f(n.partition),`curated`)}}function E(e,t){if(!i(t))throw Error(`Workspace root does not exist: ${t}`);_.info(`Reconfiguring for workspace root`,{workspaceRoot:t});try{process.chdir(t),_.info(`Changed process cwd to workspace root`,{cwd:process.cwd()})}catch(e){_.warn(`Failed to chdir to workspace root`,{workspaceRoot:t,...h(e)})}e.sources=[{path:t,excludePatterns:e.sources[0]?.excludePatterns??[`node_modules/**`,`dist/**`,`.git/**`,`coverage/**`,`*.lock`,`pnpm-lock.yaml`]}],e.store.path=s(t,l.data),e.curated={path:s(t,l.aiCurated)},e.onboardDir=s(t,l.aiContext),e.stateDir=s(t,l.state),T(e,t)}export{C as loadConfig,E as reconfigureForWorkspace,S as resolveIndexMode};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{t as e}from"./resolve-sibling-ByoHo7Tp.js";import{createReadStream as t,existsSync as n,statSync as r}from"node:fs";import{dirname as i,extname as a,join as o,resolve as s,sep as c}from"node:path";import{fileURLToPath as l}from"node:url";const u=import.meta.dirname??i(l(import.meta.url)),d={".html":`text/html`,".js":`application/javascript`,".css":`text/css`,".json":`application/json`,".png":`image/png`,".svg":`image/svg+xml`,".ico":`image/x-icon`,".woff":`font/woff`,".woff2":`font/woff2`};function f(t){return t?o(t,`..`,`..`,`dashboard`,`dist`):e(`dashboard`)??o(u,`..`,`..`,`dashboard`,`dist`)}function p(e,t){let i=t.replace(/^\/_dashboard\/?/,``)||`index.html`;try{i=decodeURIComponent(i)}catch{}let l=s(e,i);if(!(l===e||l.startsWith(`${e}${c}`)))return{kind:`forbidden`};try{if(r(l).isFile())return{kind:`file`,path:l,contentType:d[a(l)]??`application/octet-stream`}}catch{}let u=o(e,`index.html`);return n(u)?{kind:`spa`,path:u,contentType:`text/html`}:{kind:`not-found`}}function m(e,r,i){return n(r)?(e.get(`/_dashboard{/*path}`,(e,n)=>{let i=p(r,e.path);if(i.kind===`forbidden`){n.status(403).end(`Forbidden`);return}if(i.kind===`not-found`){n.status(404).end(`Not found`);return}n.setHeader(`Content-Type`,i.contentType),i.kind===`file`&&/\.[a-f0-9]{8,}\.(js|css)$/i.test(i.path)?n.setHeader(`Cache-Control`,`public, max-age=31536000, immutable`):n.setHeader(`Cache-Control`,`no-cache`),t(i.path).pipe(n)}),i.info(`Dashboard available`,{url:`/_dashboard/`,dir:r}),!0):!1}export{m as registerDashboardRoutes,f as resolveDashboardDir,p as resolveDashboardRequest};
|
|
@@ -181,4 +181,7 @@ declare class CuratedKnowledgeManager {
|
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
183
|
//#endregion
|
|
184
|
-
|
|
184
|
+
//#region packages/server/src/index.d.ts
|
|
185
|
+
declare function main(): Promise<void>;
|
|
186
|
+
//#endregion
|
|
187
|
+
export { CuratedKnowledgeManager, main };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./curated-manager-BnP6VqvL.js";import{randomUUID as t}from"node:crypto";import{readFileSync as n}from"node:fs";import{dirname as r,resolve as i}from"node:path";import{fileURLToPath as a,pathToFileURL as o}from"node:url";import{parseArgs as s}from"node:util";import{createLogger as c,serializeError as l}from"../../core/dist/index.js";var u=class{stateStore;options;gcTimer=null;constructor(e,t={}){this.stateStore=e,this.options=t}onSessionStart(e,t){this.stateStore.sessionCreate(e,t)}onSessionActivity(e){this.stateStore.sessionTouch(e)}onSessionEnd(e){this.stateStore.sessionDelete(e)}startGC(){if(this.gcTimer)return;let e=(this.options.gcIntervalMinutes??5)*60*1e3;this.gcTimer=setInterval(()=>{this.runGC()},e),this.gcTimer.unref()}runGC(){let e=this.options.staleTimeoutMinutes??30;return this.stateStore.sessionDeleteStale(e)}stop(){this.gcTimer&&=(clearInterval(this.gcTimer),null)}getActiveSessions(){return this.stateStore.sessionList().length}};const d=r(a(import.meta.url)),f=(()=>{try{let e=i(d,`..`,`..`,`..`,`package.json`);return JSON.parse(n(e,`utf-8`)).version??`0.0.0`}catch{return`0.0.0`}})(),p=c(`server`);function m(){return process.env.AIKIT_TRANSPORT?process.env.AIKIT_TRANSPORT:process.stdin.isTTY?`http`:`stdio`}const h=(()=>{let e=process.argv[1];if(!e)return!1;try{return import.meta.url===o(e).href}catch{return!1}})();function g(e){let t=e.headers[`mcp-session-id`];return Array.isArray(t)?t[0]:t}const{values:_}=h?s({allowPositionals:!0,options:{transport:{type:`string`,default:m()},port:{type:`string`,default:process.env.AIKIT_PORT??`3210`}}}):{values:{transport:m(),port:process.env.AIKIT_PORT??`3210`}};async function v(){process.on(`unhandledRejection`,e=>{p.error(`Unhandled rejection`,l(e))}),process.on(`uncaughtException`,e=>{p.error(`Uncaught exception — exiting`,l(e)),process.exit(1)});let e=e=>{e.then(async()=>{try{let{shouldRunStartupPrune:e,prune:t,markPruneRun:n}=await import(`../../tools/dist/index.js`);if(!e())return;let r=await t({});n(),r.totalBytesFreed>0&&p.info(`Storage maintenance complete`,{forgeOrphans:r.forgeGroundOrphans.count,legacyLance:r.legacyLance.count,bytesFreed:r.totalBytesFreed})}catch(e){p.warn(`Startup maintenance failed (non-critical)`,l(e))}}).catch(()=>{})};if(p.info(`Starting MCP AI Kit server`,{version:f}),_.transport===`http`){let[{default:n},{loadConfig:r,resolveIndexMode:i},{registerDashboardRoutes:a,resolveDashboardDir:o},{registerSettingsRoutes:s,resolveSettingsDir:c},{createSettingsRouter:d},{authMiddleware:f,getOrCreateToken:m}]=await Promise.all([import(`express`),import(`./config-DAnAxrUW.js`),import(`./dashboard-static-FmfoS46e.js`),import(`./settings-static-BtvyIrza.js`),import(`./routes-CR3fI-HJ.js`),import(`./auth-BfqgawfR.js`).then(e=>e.t)]),h=r();p.info(`Config loaded`,{sourceCount:h.sources.length,storePath:h.store.path});let v=n();v.use(n.json());let y=Number(_.port);v.use((e,t,n)=>{if(t.setHeader(`Access-Control-Allow-Origin`,process.env.AIKIT_CORS_ORIGIN??`http://localhost:${y}`),t.setHeader(`Access-Control-Allow-Methods`,`GET, POST, PUT, PATCH, DELETE, OPTIONS`),t.setHeader(`Access-Control-Allow-Headers`,`Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID`),t.setHeader(`Access-Control-Expose-Headers`,`Mcp-Session-Id`),e.method===`OPTIONS`){t.status(204).end();return}n()});let b=m();console.error(`[aikit] Auth token: ~/.aikit/token`),v.use(f(b)),a(v,o(),p);let x=new Date().toISOString();v.use(`/settings/api`,d({log:p,mcpInfo:()=>({transport:`http`,port:y,pid:process.pid,startedAt:x})})),s(v,c(),p),v.get(`/health`,(e,t)=>{t.json({status:`ok`})});let S=!1,C=null,w=null,T=null,E=null,D=null,O=null,k=Promise.resolve(),A=async(e,n)=>{if(!S||!T||!E){n.status(503).json({jsonrpc:`2.0`,error:{code:-32603,message:`Server initializing — please retry in a few seconds`},id:null});return}let r=k,i;k=new Promise(e=>{i=e}),await r;try{let r=g(e);if(!D){if(r){n.status(404).json({jsonrpc:`2.0`,error:{code:-32001,message:`Session not found`},id:null});return}let e=new E({sessionIdGenerator:()=>t(),onsessioninitialized:async e=>{O=e,w?.onSessionStart(e,{transport:`http`})},onsessionclosed:async e=>{e&&w?.onSessionEnd(e),O=null}});e.onclose=()=>{D===e&&(D=null),O===e.sessionId&&(O=null)},D=e,await T.connect(e)}let i=D;await i.handleRequest(e,n,e.body),e.method!==`DELETE`&&(!r&&i.sessionId?(O=i.sessionId,w?.onSessionStart(i.sessionId,{transport:`http`}),w?.onSessionActivity(i.sessionId)):r&&w?.onSessionActivity(r))}catch(e){if(p.error(`MCP handler error`,l(e)),!n.headersSent){let t=e instanceof Error?e.message:String(e),r=t.includes(`Not Acceptable`);n.status(r?406:500).json({jsonrpc:`2.0`,error:{code:r?-32e3:-32603,message:r?t:`Internal server error`},id:null})}}finally{i()}};v.post(`/mcp`,A),v.get(`/mcp`,A),v.delete(`/mcp`,A);let j=v.listen(y,`127.0.0.1`,()=>{p.info(`MCP server listening`,{url:`http://127.0.0.1:${y}/mcp`,port:y}),setTimeout(async()=>{try{let[{createLazyServer:t,ALL_TOOL_NAMES:n},{StreamableHTTPServerTransport:r},{checkForUpdates:a,autoUpgradeScaffold:o}]=await Promise.all([import(`./server-DdnaotHn.js`),import(`@modelcontextprotocol/sdk/server/streamableHttp.js`),import(`./version-check-BgHzxxCW.js`)]);a(),o();let s=i(h),c=t(h,s);T=c.server,E=r,S=!0,p.info(`MCP server configured (lazy — AI Kit initializing in background)`,{toolCount:n.length,resourceCount:2}),c.startInit(),c.ready.then(()=>{if(!c.aikit)throw Error(`AI Kit components are not available after initialization`);w=new u(c.aikit.stateStore),w.startGC(),O&&(w.onSessionStart(O,{transport:`http`}),w.onSessionActivity(O))}).catch(e=>p.error(`Failed to start session manager`,l(e))),s===`auto`?c.ready.then(async()=>{try{let e=h.sources.map(e=>e.path).join(`, `);p.info(`Running initial index`,{sourcePaths:e}),await c.runInitialIndex(),p.info(`Initial index complete`)}catch(e){p.error(`Initial index failed; will retry on aikit_reindex`,l(e))}}).catch(e=>p.error(`AI Kit init or indexing failed`,l(e))):s===`smart`?c.ready.then(async()=>{try{if(!c.aikit)throw Error(`AI Kit components are not available after initialization`);let{SmartIndexScheduler:e}=await import(`../../indexer/dist/index.js`),t=new e(c.aikit.indexer,h,c.aikit.store),n=c.aikit.store;C=t,t.start(),n.onBeforeClose&&n.onBeforeClose(()=>t.stop()),c.setSmartScheduler(t),p.info(`Smart index scheduler started (HTTP mode)`)}catch(e){p.error(`Failed to start smart index scheduler`,l(e))}}).catch(e=>p.error(`AI Kit initialization failed`,l(e))):(c.ready.catch(e=>p.error(`AI Kit initialization failed`,l(e))),p.info(`Initial full indexing skipped in HTTP mode`,{indexMode:s})),e(c.ready)}catch(e){p.error(`Failed to load server modules`,l(e))}},100)}),M=async e=>{p.info(`Shutdown signal received`,{signal:e}),C?.stop(),w?.stop(),O&&w?.onSessionEnd(O),D&&(await D.close().catch(()=>void 0),D=null,O=null),j.close(),T&&await T.close(),process.exit(0)};process.on(`SIGINT`,()=>M(`SIGINT`)),process.on(`SIGTERM`,()=>M(`SIGTERM`))}else{let[{loadConfig:t,reconfigureForWorkspace:n,resolveIndexMode:r},{createLazyServer:i},{checkForUpdates:o,autoUpgradeScaffold:s},{RootsListChangedNotificationSchema:c}]=await Promise.all([import(`./config-DAnAxrUW.js`),import(`./server-DdnaotHn.js`),import(`./version-check-BgHzxxCW.js`),import(`@modelcontextprotocol/sdk/types.js`)]),u=t();p.info(`Config loaded`,{sourceCount:u.sources.length,storePath:u.store.path}),o(),s();let d=r(u),f=i(u,d),{server:m,startInit:h,ready:g,runInitialIndex:_}=f,{StdioServerTransport:v}=await import(`@modelcontextprotocol/sdk/server/stdio.js`),y=new v;await m.connect(y),p.info(`MCP server started`,{transport:`stdio`});let b=e=>{if(e.length===0)return!1;let t=e[0].uri,r=t.startsWith(`file://`)?a(t):t;return p.info(`MCP roots resolved`,{rootUri:t,rootPath:r,rootCount:e.length}),n(u,r),u.allRoots=e.map(e=>{let t=e.uri;return t.startsWith(`file://`)?a(t):t}),!0},x=!1;try{x=b((await m.server.listRoots()).roots),x||p.info(`No MCP roots yet; waiting for roots/list_changed notification`)}catch(e){p.warn(`MCP roots/list not supported by client; using cwd fallback`,{cwd:process.cwd(),...l(e)}),x=!0}x||=await new Promise(e=>{let t=setTimeout(()=>{p.warn(`Timed out waiting for MCP roots/list_changed; using cwd fallback`,{cwd:process.cwd()}),e(!1)},5e3);m.server.setNotificationHandler(c,async()=>{clearTimeout(t);try{e(b((await m.server.listRoots()).roots))}catch(t){p.warn(`roots/list retry failed after notification`,l(t)),e(!1)}})}),h();let S=null,C=()=>{S&&clearTimeout(S),S=setTimeout(async()=>{p.info(`Auto-shutdown: no activity for 30 minutes — shutting down gracefully`);try{let e=f.aikit;e&&await Promise.all([e.embedder.shutdown?.().catch(()=>{})??Promise.resolve(),e.graphStore.close().catch(()=>{}),e.store.close().catch(()=>{})])}catch{}process.exit(0)},18e5),S.unref&&S.unref()};C(),process.stdin.on(`data`,()=>C()),g.catch(e=>{p.error(`Initialization failed — server will continue with limited tools`,l(e))}),d===`auto`?_().catch(e=>p.error(`Initial index failed`,l(e))):d===`smart`?g.then(async()=>{try{if(!f.aikit)throw Error(`AI Kit components are not available after initialization`);let{SmartIndexScheduler:e}=await import(`../../indexer/dist/index.js`),t=new e(f.aikit.indexer,u,f.aikit.store),n=f.aikit.store;t.start(),n.onBeforeClose&&n.onBeforeClose(()=>t.stop()),f.setSmartScheduler(t),p.info(`Smart index scheduler started (stdio mode)`)}catch(e){p.error(`Failed to start smart index scheduler`,l(e))}}).catch(e=>p.error(`AI Kit init failed for smart scheduler`,l(e))):p.warn(`Initial full indexing skipped; use aikit_reindex to index manually`,{indexMode:d}),e(g)}}v().catch(e=>{p.error(`Fatal error`,l(e)),process.exit(1)});export{e as CuratedKnowledgeManager};
|
|
1
|
+
import{t as e}from"./curated-manager-BnP6VqvL.js";import{randomUUID as t}from"node:crypto";import{readFileSync as n}from"node:fs";import{dirname as r,resolve as i}from"node:path";import{fileURLToPath as a,pathToFileURL as o}from"node:url";import{parseArgs as s}from"node:util";import{createLogger as c,serializeError as l}from"../../core/dist/index.js";const u=`__pending__:`;function d(e){return e.startsWith(u)}function f(e){let t=e.headers[`mcp-session-id`];return Array.isArray(t)?t[0]:t}function p(e,t,n,r){e.status(t).json({jsonrpc:`2.0`,error:{code:n,message:r},id:null})}var m=class{options;runtimes=new Map;maxSessions;sessionTimeoutMs;now;constructor(e){this.options=e,this.maxSessions=e.maxSessions??8,this.sessionTimeoutMs=(e.sessionTimeoutMinutes??30)*60*1e3,this.now=e.now??(()=>Date.now())}hasSession(e){return this.runtimes.has(e)}getSessionCount(){return this.runtimes.size}async handleRequest(e,t,n=e.body){let r=f(e),i=r?this.runtimes.get(r):void 0;if(r&&!i){p(t,404,-32001,`Session not found`);return}if(!i){if(e.method!==`POST`){p(t,400,-32e3,`Session required`);return}if(i=await this.createRuntime(t),!i)return}await this.withRuntimeLock(i,async()=>{await i.transport.handleRequest(e,t,n),i.lastAccessAt=this.now();let r=i.transport.sessionId??i.id;e.method!==`DELETE`&&!d(r)&&this.options.onSessionActivity?.(r),e.method===`DELETE`&&!d(r)&&await this.closeSession(r,{closeTransport:!1})})}async closeExpiredSessions(){let e=[...this.runtimes.values()].filter(e=>this.now()-e.lastAccessAt>=this.sessionTimeoutMs).map(e=>e.id);for(let t of e)await this.closeSession(t);return e.length}async closeSession(e,t={}){let n=this.runtimes.get(e);return n?(this.runtimes.delete(e),t.notifySessionEnd!==!1&&!d(e)&&this.options.onSessionEnd?.(e),t.closeTransport!==!1&&await n.transport.close().catch(()=>void 0),await n.server.close().catch(()=>void 0),!0):!1}async closeAll(){let e=[...this.runtimes.keys()];for(let t of e)await this.closeSession(t)}async createRuntime(e){if(await this.closeExpiredSessions(),this.runtimes.size>=this.maxSessions){p(e,503,-32003,`Session capacity reached`);return}let n=this.now(),r=await this.options.createServer(),i={id:`${u}${t()}`,transport:void 0,createdAt:n,lastAccessAt:n,server:r,requestChain:Promise.resolve()},a=this.options.createTransport({sessionIdGenerator:()=>t(),onsessioninitialized:async e=>{this.runtimes.delete(i.id),i.id=e,this.runtimes.set(e,i),this.options.onSessionStart?.(e)},onsessionclosed:async e=>{e&&await this.closeSession(e,{closeTransport:!1})}});return i.transport=a,a.onclose=()=>{let e=i.transport.sessionId??i.id;this.closeSession(e,{closeTransport:!1})},this.runtimes.set(i.id,i),await r.connect(a),i}async withRuntimeLock(e,t){let n=e.requestChain,r;e.requestChain=new Promise(e=>{r=e}),await n;try{await t()}finally{r()}}};function h(e){let t=e.includes(`T`)?e:`${e.replace(` `,`T`)}Z`;return Date.parse(t)}var g=class{stateStore;options;gcTimer=null;constructor(e,t={}){this.stateStore=e,this.options=t}onSessionStart(e,t){this.stateStore.sessionCreate(e,t)}onSessionActivity(e){this.stateStore.sessionTouch(e)}onSessionEnd(e){this.stateStore.sessionDelete(e)}startGC(){if(this.gcTimer)return;let e=(this.options.gcIntervalMinutes??5)*60*1e3;this.gcTimer=setInterval(()=>{this.runGC()},e),this.gcTimer.unref()}runGC(){let e=this.getStaleSessionIds();for(let t of e)this.options.onBeforeSessionDelete?.(t),this.stateStore.sessionDelete(t);return e.length}stop(){this.gcTimer&&=(clearInterval(this.gcTimer),null)}getActiveSessions(){return this.stateStore.sessionList().length}listSessions(){return this.stateStore.sessionList()}getStaleSessionIds(e=Date.now()){let t=(this.options.staleTimeoutMinutes??30)*60*1e3;return this.stateStore.sessionList().filter(n=>{let r=h(n.lastActivity);return Number.isFinite(r)&&e-r>=t}).map(e=>e.sessionId)}};function _(){try{let e=i(r(a(import.meta.url)),`..`,`..`,`..`,`package.json`);return JSON.parse(n(e,`utf-8`)).version??`0.0.0`}catch{return`0.0.0`}}const v=c(`server`);function y(){return process.env.AIKIT_TRANSPORT?process.env.AIKIT_TRANSPORT:process.stdin.isTTY?`http`:`stdio`}function b(){let e=process.argv[1];if(!e)return!1;try{return import.meta.url===o(e).href}catch{return!1}}function x(e){let t=e.headers[`mcp-session-id`];return Array.isArray(t)?t[0]:t}function S(e,t){let n=process.env[e];if(!n)return t;let r=Number.parseInt(n,10);return Number.isFinite(r)&&r>0?r:t}function C(){return b()?s({allowPositionals:!0,options:{transport:{type:`string`,default:y()},port:{type:`string`,default:process.env.AIKIT_PORT??`3210`}}}).values:{transport:y(),port:process.env.AIKIT_PORT??`3210`}}async function w(){let e=_(),n=C();process.on(`unhandledRejection`,e=>{v.error(`Unhandled rejection`,l(e))}),process.on(`uncaughtException`,e=>{v.error(`Uncaught exception — exiting`,l(e)),process.exit(1)});let r=e=>{e.then(async()=>{try{let{shouldRunStartupPrune:e,prune:t,markPruneRun:n}=await import(`../../tools/dist/index.js`);if(!e())return;let r=await t({});n(),r.totalBytesFreed>0&&v.info(`Storage maintenance complete`,{forgeOrphans:r.forgeGroundOrphans.count,legacyLance:r.legacyLance.count,bytesFreed:r.totalBytesFreed})}catch(e){v.warn(`Startup maintenance failed (non-critical)`,l(e))}}).catch(()=>{})};if(v.info(`Starting MCP AI Kit server`,{version:e}),n.transport===`http`){let[{default:e},{loadConfig:i,resolveIndexMode:a},{registerDashboardRoutes:o,resolveDashboardDir:s},{registerSettingsRoutes:c,resolveSettingsDir:u},{createSettingsRouter:d},{authMiddleware:f,getOrCreateToken:p}]=await Promise.all([import(`express`),import(`./config-DAnAxrUW.js`),import(`./dashboard-static-FmfoS46e.js`),import(`./settings-static-BtvyIrza.js`),import(`./routes-CR3fI-HJ.js`),import(`./auth-BfqgawfR.js`).then(e=>e.t)]),h=i();v.info(`Config loaded`,{sourceCount:h.sources.length,storePath:h.store.path});let _=e();_.use(e.json());let y=Number(n.port),b=S(`AIKIT_HTTP_MAX_SESSIONS`,8),C=S(`AIKIT_HTTP_SESSION_TIMEOUT_MINUTES`,30),w=S(`AIKIT_HTTP_SESSION_GC_INTERVAL_MINUTES`,5);_.use((e,t,n)=>{if(t.setHeader(`Access-Control-Allow-Origin`,process.env.AIKIT_CORS_ORIGIN??`http://localhost:${y}`),t.setHeader(`Access-Control-Allow-Methods`,`GET, POST, PUT, PATCH, DELETE, OPTIONS`),t.setHeader(`Access-Control-Allow-Headers`,`Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID`),t.setHeader(`Access-Control-Expose-Headers`,`Mcp-Session-Id`),e.method===`OPTIONS`){t.status(204).end();return}n()});let T=p();console.error(`[aikit] Auth token: ~/.aikit/token`),_.use(f(T)),o(_,s(),v);let E=new Date().toISOString();_.use(`/settings/api`,d({log:v,mcpInfo:()=>({transport:`http`,port:y,pid:process.pid,startedAt:E})})),c(_,u(),v),_.get(`/health`,(e,t)=>{t.json({status:`ok`})});let D=!1,O=null,k=null,A=null,j=null,M=null,N=null,P=null,F=Promise.resolve(),I=async(e,n)=>{if(!D||!A||!j){n.status(503).json({jsonrpc:`2.0`,error:{code:-32603,message:`Server initializing — please retry in a few seconds`},id:null});return}let r=F,i;F=new Promise(e=>{i=e}),await r;try{let r=x(e);if(!N){if(r){n.status(404).json({jsonrpc:`2.0`,error:{code:-32001,message:`Session not found`},id:null});return}let e=new j({sessionIdGenerator:()=>t(),onsessioninitialized:async e=>{P=e,k?.onSessionStart(e,{transport:`http`})},onsessionclosed:async e=>{e&&k?.onSessionEnd(e),P=null}});e.onclose=()=>{N===e&&(N=null),P===e.sessionId&&(P=null)},N=e,await A.connect(e)}let i=N;await i.handleRequest(e,n,e.body),e.method!==`DELETE`&&(!r&&i.sessionId?(P=i.sessionId,k?.onSessionStart(i.sessionId,{transport:`http`}),k?.onSessionActivity(i.sessionId)):r&&k?.onSessionActivity(r))}catch(e){if(v.error(`MCP handler error`,l(e)),!n.headersSent){let t=e instanceof Error?e.message:String(e),r=t.includes(`Not Acceptable`);n.status(r?406:500).json({jsonrpc:`2.0`,error:{code:r?-32e3:-32603,message:r?t:`Internal server error`},id:null})}}finally{i()}},L=async(e,t)=>{let n=x(e);if(M&&(!N||n!==P)){await M.handleRequest(e,t,e.body);return}await I(e,t)};_.post(`/mcp`,L),_.get(`/mcp`,L),_.delete(`/mcp`,L);let R=_.listen(y,`127.0.0.1`,()=>{v.info(`MCP server listening`,{url:`http://127.0.0.1:${y}/mcp`,port:y}),setTimeout(async()=>{try{let[{createLazyServer:e,createMcpServer:t,ALL_TOOL_NAMES:n},{StreamableHTTPServerTransport:i},{checkForUpdates:o,autoUpgradeScaffold:s}]=await Promise.all([import(`./server-BaMsrcyc.js`),import(`@modelcontextprotocol/sdk/server/streamableHttp.js`),import(`./version-check-BgHzxxCW.js`)]);o(),s();let c=a(h),u=e(h,c);A=u.server,j=i,D=!0,v.info(`MCP server configured (lazy — AI Kit initializing in background)`,{toolCount:n.length,resourceCount:2}),u.startInit(),u.ready.then(()=>{if(!u.aikit)throw Error(`AI Kit components are not available after initialization`);k=new g(u.aikit.stateStore,{staleTimeoutMinutes:C,gcIntervalMinutes:w,onBeforeSessionDelete:e=>{if(P===e&&N){let e=N;N=null,P=null,e.close().catch(()=>void 0)}M?.closeSession(e,{notifySessionEnd:!1})}}),M=new m({createServer:()=>{if(!u.aikit)throw Error(`AI Kit components are not available after initialization`);return t(u.aikit,h)},createTransport:e=>new i(e),maxSessions:b,sessionTimeoutMinutes:C,onSessionStart:e=>k?.onSessionStart(e,{transport:`http`}),onSessionActivity:e=>k?.onSessionActivity(e),onSessionEnd:e=>k?.onSessionEnd(e)}),k.startGC(),P&&(k.onSessionStart(P,{transport:`http`}),k.onSessionActivity(P)),v.info(`HTTP session runtime ready`,{maxSessions:b,sessionTimeoutMinutes:C,gcIntervalMinutes:w})}).catch(e=>v.error(`Failed to start session manager`,l(e))),c===`auto`?u.ready.then(async()=>{try{let e=h.sources.map(e=>e.path).join(`, `);v.info(`Running initial index`,{sourcePaths:e}),await u.runInitialIndex(),v.info(`Initial index complete`)}catch(e){v.error(`Initial index failed; will retry on aikit_reindex`,l(e))}}).catch(e=>v.error(`AI Kit init or indexing failed`,l(e))):c===`smart`?u.ready.then(async()=>{try{if(!u.aikit)throw Error(`AI Kit components are not available after initialization`);let{SmartIndexScheduler:e}=await import(`../../indexer/dist/index.js`),t=new e(u.aikit.indexer,h,u.aikit.store),n=u.aikit.store;O=t,t.start(),n.onBeforeClose&&n.onBeforeClose(()=>t.stop()),u.setSmartScheduler(t),v.info(`Smart index scheduler started (HTTP mode)`)}catch(e){v.error(`Failed to start smart index scheduler`,l(e))}}).catch(e=>v.error(`AI Kit initialization failed`,l(e))):(u.ready.catch(e=>v.error(`AI Kit initialization failed`,l(e))),v.info(`Initial full indexing skipped in HTTP mode`,{indexMode:c})),r(u.ready)}catch(e){v.error(`Failed to load server modules`,l(e))}},100)}),z=async e=>{v.info(`Shutdown signal received`,{signal:e}),O?.stop(),k?.stop(),await M?.closeAll().catch(()=>void 0),P&&k?.onSessionEnd(P),N&&(await N.close().catch(()=>void 0),N=null,P=null),R.close(),A&&await A.close(),process.exit(0)};process.on(`SIGINT`,()=>z(`SIGINT`)),process.on(`SIGTERM`,()=>z(`SIGTERM`))}else{let[{loadConfig:e,reconfigureForWorkspace:t,resolveIndexMode:n},{createLazyServer:i},{checkForUpdates:o,autoUpgradeScaffold:s},{RootsListChangedNotificationSchema:c}]=await Promise.all([import(`./config-DAnAxrUW.js`),import(`./server-BaMsrcyc.js`),import(`./version-check-BgHzxxCW.js`),import(`@modelcontextprotocol/sdk/types.js`)]),u=e();v.info(`Config loaded`,{sourceCount:u.sources.length,storePath:u.store.path}),o(),s();let d=n(u),f=i(u,d),{server:p,startInit:m,ready:h,runInitialIndex:g}=f,{StdioServerTransport:_}=await import(`@modelcontextprotocol/sdk/server/stdio.js`),y=new _;await p.connect(y),v.info(`MCP server started`,{transport:`stdio`});let b=e=>{if(e.length===0)return!1;let n=e[0].uri,r=n.startsWith(`file://`)?a(n):n;return v.info(`MCP roots resolved`,{rootUri:n,rootPath:r,rootCount:e.length}),t(u,r),u.allRoots=e.map(e=>{let t=e.uri;return t.startsWith(`file://`)?a(t):t}),!0},x=!1;try{x=b((await p.server.listRoots()).roots),x||v.info(`No MCP roots yet; waiting for roots/list_changed notification`)}catch(e){v.warn(`MCP roots/list not supported by client; using cwd fallback`,{cwd:process.cwd(),...l(e)}),x=!0}x||=await new Promise(e=>{let t=setTimeout(()=>{v.warn(`Timed out waiting for MCP roots/list_changed; using cwd fallback`,{cwd:process.cwd()}),e(!1)},5e3);p.server.setNotificationHandler(c,async()=>{clearTimeout(t);try{e(b((await p.server.listRoots()).roots))}catch(t){v.warn(`roots/list retry failed after notification`,l(t)),e(!1)}})}),m();let S=null,C=()=>{S&&clearTimeout(S),S=setTimeout(async()=>{v.info(`Auto-shutdown: no activity for 30 minutes — shutting down gracefully`);try{let e=f.aikit;e&&await Promise.all([e.embedder.shutdown?.().catch(()=>{})??Promise.resolve(),e.graphStore.close().catch(()=>{}),e.store.close().catch(()=>{})])}catch{}process.exit(0)},18e5),S.unref&&S.unref()};C(),process.stdin.on(`data`,()=>C()),h.catch(e=>{v.error(`Initialization failed — server will continue with limited tools`,l(e))}),d===`auto`?g().catch(e=>v.error(`Initial index failed`,l(e))):d===`smart`?h.then(async()=>{try{if(!f.aikit)throw Error(`AI Kit components are not available after initialization`);let{SmartIndexScheduler:e}=await import(`../../indexer/dist/index.js`),t=new e(f.aikit.indexer,u,f.aikit.store),n=f.aikit.store;t.start(),n.onBeforeClose&&n.onBeforeClose(()=>t.stop()),f.setSmartScheduler(t),v.info(`Smart index scheduler started (stdio mode)`)}catch(e){v.error(`Failed to start smart index scheduler`,l(e))}}).catch(e=>v.error(`AI Kit init failed for smart scheduler`,l(e))):v.warn(`Initial full indexing skipped; use aikit_reindex to index manually`,{indexMode:d}),r(h)}}export{e as CuratedKnowledgeManager,w as main};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{createRequire as e}from"node:module";import{existsSync as t}from"node:fs";import{dirname as n,join as r}from"node:path";import{fileURLToPath as i}from"node:url";const a=e(import.meta.url),o=import.meta.dirname??n(i(import.meta.url)),s=new Map;function c(e){let i=s.get(e);if(i!==void 0)return i;let c=r(o,`..`,`..`,e);if(t(r(c,`package.json`)))return s.set(e,c),c;try{let t=n(a.resolve(`@aikit/${e}/package.json`));return s.set(e,t),t}catch{}let l=o;for(let i=0;i<6;i++){let i=r(l,`packages`,e);if(t(r(i,`package.json`)))return s.set(e,i),i;let a=n(l);if(a===l)break;l=a}return s.set(e,null),null}function l(e,n=`dist`){let i=c(e);if(!i)return null;let a=r(i,n);return t(a)?a:null}function u(e,n){let i=c(e);if(!i)return null;for(let e of n){let n=r(i,e);if(t(n))return n}return null}export{u as n,c as r,l as t};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync as e,mkdirSync as t,readFileSync as n,renameSync as r,writeFileSync as i}from"node:fs";import{dirname as a,resolve as o}from"node:path";import{getGlobalDataDir as s,isUserInstalled as c}from"../../core/dist/index.js";import{z as l}from"zod";import{Router as u}from"express";const d=[{key:`serverName`,type:`string`,default:`aikit`,description:`MCP server name advertised to clients.`,group:`general`,requiresRestart:!0},{key:`toolPrefix`,type:`string`,default:``,description:`Prefix prepended to every MCP tool name (e.g. "aikit_").`,group:`general`,requiresRestart:!0},{key:`toolProfile`,type:`string`,default:`full`,description:`Active tool profile.`,group:`general`,enum:[`full`,`safe`,`research`,`minimal`,`discovery`],requiresRestart:!0},{key:`autoIndex`,type:`boolean`,default:!1,description:`Deprecated. Prefer indexMode. true → auto, false → manual.`,group:`indexing`},{key:`indexMode`,type:`string`,default:`smart`,description:`Indexing strategy. auto = full on startup, manual = on demand, smart = on-demand + idle.`,group:`indexing`,enum:[`auto`,`manual`,`smart`]},{key:`sources`,type:`array`,description:`Source roots to index. Each entry has { path, excludePatterns }.`,group:`indexing`,requiresRestart:!0},{key:`indexing.chunkSize`,type:`number`,default:1500,description:`Maximum chunk size in characters.`,group:`indexing`},{key:`indexing.chunkOverlap`,type:`number`,default:200,description:`Overlap between adjacent chunks.`,group:`indexing`},{key:`indexing.minChunkSize`,type:`number`,default:100,description:`Minimum chunk size before merging into the next chunk.`,group:`indexing`},{key:`indexing.concurrency`,type:`number`,description:`Max files processed concurrently. Defaults to half of CPU cores.`,group:`indexing`},{key:`embedding.model`,type:`string`,default:`mixedbread-ai/mxbai-embed-large-v1`,description:`Embedding model identifier.`,group:`embedding`,requiresRestart:!0},{key:`embedding.dimensions`,type:`number`,default:1024,description:`Embedding vector dimensions. Must match the model.`,group:`embedding`,requiresRestart:!0},{key:`store.backend`,type:`string`,default:`sqlite-vec`,description:`Vector store backend (sqlite-vec | lancedb).`,group:`store`,requiresRestart:!0},{key:`store.path`,type:`string`,description:`Filesystem path for the vector store.`,group:`store`,requiresRestart:!0},{key:`curated.path`,type:`string`,description:`Path to curated knowledge directory.`,group:`store`,requiresRestart:!0},{key:`onboardDir`,type:`string`,description:`Directory for onboard / produce_knowledge output.`,group:`store`,requiresRestart:!0},{key:`stateDir`,type:`string`,description:`Directory for session state (stash, checkpoints, etc.).`,group:`store`,requiresRestart:!0},{key:`er.enabled`,type:`boolean`,default:!1,description:`Enable enterprise RAG bridge integration.`,group:`enterprise-bridge`,requiresRestart:!0},{key:`er.baseUrl`,type:`string`,description:`Base URL of the ER API.`,group:`enterprise-bridge`,requiresRestart:!0},{key:`er.timeoutMs`,type:`number`,default:5e3,description:`ER request timeout in milliseconds.`,group:`enterprise-bridge`},{key:`er.fallbackThreshold`,type:`number`,default:.45,description:`Vector similarity threshold below which ER fallback triggers.`,group:`enterprise-bridge`}],f=[{key:`AIKIT_TRANSPORT`,type:`string`,default:`stdio`,description:`MCP transport mode.`,group:`transport`,enum:[`stdio`,`http`],defaultScope:`workspace`},{key:`AIKIT_PORT`,type:`string`,default:`3210`,description:`HTTP port when transport = http.`,group:`transport`,defaultScope:`workspace`},{key:`AIKIT_CORS_ORIGIN`,type:`string`,description:`CORS Access-Control-Allow-Origin header for HTTP mode.`,group:`transport`,defaultScope:`workspace`},{key:`AIKIT_CONFIG_PATH`,type:`string`,description:`Override path to aikit.config.json.`,group:`paths`,defaultScope:`workspace`},{key:`AIKIT_WORKSPACE_ROOT`,type:`string`,description:`Override workspace root used when no config file is present.`,group:`paths`,defaultScope:`workspace`},{key:`AIKIT_GLOBAL_DATA_DIR`,type:`string`,description:`Override global data directory (default: ~/.aikit-data).`,group:`paths`,defaultScope:`global`},{key:`AIKIT_INDEX_MODE`,type:`string`,description:`Override config indexMode.`,group:`indexing`,enum:[`auto`,`manual`,`smart`],defaultScope:`workspace`},{key:`AIKIT_AUTO_INDEX`,type:`string`,description:`Legacy boolean override. Prefer AIKIT_INDEX_MODE.`,group:`indexing`,enum:[`true`,`false`],defaultScope:`workspace`},{key:`AIKIT_TOOLSET`,type:`string`,description:`Override active tool profile.`,group:`general`,defaultScope:`workspace`},{key:`AIKIT_BROWSER_DEFAULT_MODE`,type:`string`,default:`ui`,description:`Default browser launch mode for browser_* tools.`,group:`browser`,enum:[`ui`,`headless`,`panel`],defaultScope:`workspace`},{key:`AIKIT_BROWSER_PATH`,type:`string`,description:`Optional Playwright browser download path override.`,group:`browser`,defaultScope:`workspace`},{key:`AIKIT_BROWSER_IDLE_MINUTES`,type:`string`,default:`10`,description:`Idle shutdown timeout in minutes for the shared browser runtime.`,group:`browser`,defaultScope:`workspace`},{key:`AIKIT_BROWSER_EVAL_TIMEOUT_MS`,type:`string`,default:`5000`,description:`Default browser_eval timeout in milliseconds.`,group:`browser`,defaultScope:`workspace`},{key:`AIKIT_SEARCH_DEADLINE_MS`,type:`string`,default:`10000`,description:`Multi-provider web search deadline (milliseconds).`,group:`search-providers`,defaultScope:`global`},{key:`SEARXNG_URL`,type:`string`,description:`Optional SearXNG instance URL for the web_search tool.`,group:`search-providers`,defaultScope:`global`},{key:`GOOGLE_API_KEY`,type:`secret`,description:`Google Custom Search API key.`,group:`search-providers`,defaultScope:`global`},{key:`GOOGLE_CSE_ID`,type:`string`,description:`Google Custom Search Engine ID.`,group:`search-providers`,defaultScope:`global`},{key:`BRAVE_API_KEY`,type:`secret`,description:`Brave Search API key.`,group:`search-providers`,defaultScope:`global`},{key:`BING_API_KEY`,type:`secret`,description:`Bing Web Search API key.`,group:`search-providers`,defaultScope:`global`}];function p(e){return f.find(t=>t.key===e)}function m(e){let t=p(e);return t?t.type===`secret`:/(_KEY|_TOKEN|_SECRET|_PASSWORD|_PASS|_PWD)$/i.test(e)}const h=/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=(.*)$/;function g(e){let t=e.replace(/^\s+/,``);if(t.startsWith(`"`)){let e=1,n=``;for(;e<t.length;){let r=t[e];if(r===`\\`&&e+1<t.length){let r=t[e+1];r===`n`?n+=`
|
|
3
|
+
`:r===`r`?n+=`\r`:r===`t`?n+=` `:n+=r,e+=2;continue}if(r===`"`){let r=t.slice(e+1),i=r.indexOf(`#`),a=i===-1?``:r.slice(i);return{value:n,quoted:!0,inlineComment:a}}n+=r,e+=1}return{value:t.slice(1),quoted:!0,inlineComment:``}}let n=t.match(/(\s+#.*)$/);return n&&n.index!==void 0?{value:t.slice(0,n.index).trimEnd(),quoted:!1,inlineComment:n[1].trimStart()}:{value:t.trimEnd(),quoted:!1,inlineComment:``}}function _(e){let t;t=e.quoted||/[\s#"']/.test(e.value)||e.value.includes(`
|
|
4
|
+
`)||e.value.length===0?`"${e.value.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`).replace(/\n/g,`\\n`).replace(/\r/g,`\\r`).replace(/\t/g,`\\t`)}"`:e.value;let n=e.inlineComment?` ${e.inlineComment}`:``;return`${e.key}=${t}${n}`}function v(e){let t=[],n=e.split(/\r?\n/),r=e.endsWith(`
|
|
5
|
+
`)?n.slice(0,-1):n;for(let e of r){let n=e.match(h);if(!n){t.push({kind:`raw`,text:e});continue}let r=n[1],{value:i,quoted:a,inlineComment:o}=g(n[2]);t.push({kind:`assign`,key:r,value:i,quoted:a,inlineComment:o})}return{lines:t}}function y(e){return`${e.lines.map(e=>e.kind===`assign`?_(e):e.text).join(`
|
|
6
|
+
`)}\n`}function b(e){let t={};for(let n of e.lines)n.kind===`assign`&&(t[n.key]=n.value);return t}function x(e,t,n){let r=e.lines.map(e=>e.kind===`assign`&&e.key===t?{...e,value:n}:e);return e.lines.some(e=>e.kind===`assign`&&e.key===t)||r.push({kind:`assign`,key:t,value:n,quoted:!1,inlineComment:``}),{lines:r}}function S(e,t){return{lines:e.lines.filter(e=>!(e.kind===`assign`&&e.key===t))}}function C(e){return o(e,`.env`)}function w(){return o(s(),`global.env`)}function T(e,t){return e===`workspace`?C(t):w()}function E(t){return e(t)?v(n(t,`utf-8`)):{lines:[]}}function D(n,o){let s=a(n);e(s)||t(s,{recursive:!0});let c=`${n}.${process.pid}.${Date.now()}.tmp`;i(c,y(o),{encoding:`utf-8`,mode:384}),r(c,n)}function O(t,n,r={}){let i=T(t,n),a=e(i),o=b(E(i)),s={};for(let[e,t]of Object.entries(o))s[e]=r.reveal||!m(e)?t:k(t);return{scope:t,path:i,exists:a,vars:s}}function k(e){return e.length===0?``:e.length<=4?`•`.repeat(e.length):`${e.slice(0,2)}${`•`.repeat(Math.min(e.length-4,12))}${e.slice(-2)}`}function A(){return process.env.AIKIT_WORKSPACE_ROOT??process.cwd()}function j(){return c()?`user`:`workspace`}function M(){let n=s();return e(n)||t(n,{recursive:!0}),n}var N=class extends Error{details;constructor(e,t){super(e),this.name=`ConfigValidationError`,this.details=t}};function P(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function F(e=A()){let t=process.env.AIKIT_CONFIG_PATH;return t?o(t):o(e,`aikit.config.json`)}function I(t){if(!e(t))return null;let r=n(t,`utf-8`),i=JSON.parse(r);if(!P(i))throw new N(`config-not-object`,[`Config file must contain a JSON object at the root.`]);return i}function L(n,o){let s=a(n);e(s)||t(s,{recursive:!0});let c=`${JSON.stringify(o,null,2)}\n`,l=`${n}.${process.pid}.${Date.now()}.tmp`;i(l,c,{encoding:`utf-8`}),r(l,n)}function R(e){return e.length===0?[]:e.split(`.`)}function z(e,t,n){let r=R(t);if(r.length===0)throw new N(`invalid-path`,[`Patch path must not be empty.`]);let i={...e},a=i;for(let e=0;e<r.length-1;e+=1){let t=r[e],n=r[e+1],i=/^\d+$/.test(n);if(Array.isArray(a)){let n=Number(t);if(!Number.isInteger(n))throw new N(`invalid-path`,[`Path segment "${t}" must be an integer index for array at depth ${e}.`]);let r=a[n];if(i){let e=Array.isArray(r)?[...r]:[];a[n]=e,a=e}else{let e=P(r)?{...r}:{};a[n]=e,a=e}continue}let o=a[t];if(i){let e=Array.isArray(o)?[...o]:[];a[t]=e,a=e}else{let e=P(o)?{...o}:{};a[t]=e,a=e}}let o=r[r.length-1];if(Array.isArray(a)){let e=Number(o);if(!Number.isInteger(e))throw new N(`invalid-path`,[`Path segment "${o}" must be an integer index for array.`]);a[e]=n}else a[o]=n;return i}function B(e,t){let n=R(t);if(n.length===0)throw new N(`invalid-path`,[`Patch path must not be empty.`]);let r={...e},i=r;for(let e=0;e<n.length-1;e+=1){let t=n[e];if(Array.isArray(i)){let e=Number(t);if(!Number.isInteger(e))return r;let n=i[e];if(!P(n)&&!Array.isArray(n))return r;let a=Array.isArray(n)?[...n]:{...n};i[e]=a,i=a;continue}let a=i[t];if(!P(a)&&!Array.isArray(a))return r;let o=Array.isArray(a)?[...a]:{...a};i[t]=o,i=o}let a=n[n.length-1];if(Array.isArray(i)){let e=Number(a);Number.isInteger(e)&&i.splice(e,1)}else delete i[a];return r}function V(e,t){let n=e;for(let e of t)n=e.op===`set`?z(n,e.path,e.value):B(n,e.path);return n}function H(e){let t=[];if(`autoIndex`in e&&e.autoIndex!==void 0&&typeof e.autoIndex!=`boolean`&&t.push(`autoIndex must be a boolean.`),`serverName`in e&&e.serverName!==void 0&&typeof e.serverName!=`string`&&t.push(`serverName must be a string.`),`toolPrefix`in e&&e.toolPrefix!==void 0&&typeof e.toolPrefix!=`string`&&t.push(`toolPrefix must be a string.`),`indexMode`in e&&e.indexMode!==void 0&&![`auto`,`manual`,`smart`].includes(e.indexMode)&&t.push(`indexMode must be one of: auto, manual, smart.`),(!Array.isArray(e.sources)||e.sources.length===0)&&t.push(`sources must be a non-empty array.`),`indexing`in e&&e.indexing!==void 0)if(!P(e.indexing))t.push(`indexing must be an object.`);else for(let n of[`chunkSize`,`chunkOverlap`,`minChunkSize`,`concurrency`]){let r=e.indexing[n];r!==void 0&&(typeof r!=`number`||!Number.isFinite(r)||r<=0)&&t.push(`indexing.${n} must be a positive number.`)}return`store`in e&&e.store!==void 0&&(P(e.store)?(typeof e.store.backend!=`string`&&t.push(`store.backend must be a string.`),typeof e.store.path!=`string`&&t.push(`store.path must be a string.`)):t.push(`store must be an object.`)),t}const U=l.object({ops:l.array(l.object({op:l.enum([`set`,`unset`]),path:l.string().min(1),value:l.unknown().optional()})).min(1)}),W=l.string().min(1).regex(/^[A-Za-z_][A-Za-z0-9_]*$/,`Invalid env key`),G=l.object({value:l.string(),scope:l.enum([`workspace`,`global`])}),K=l.enum([`workspace`,`global`]);function q(e,t,n,r){e.status(t).json({error:n,details:r})}function J(t){let n=u(),r=t.workspaceRoot??A;return n.get(`/status`,(n,i)=>{let a=r(),o=F(a);i.json({mode:j(),cwd:process.cwd(),workspaceRoot:a,configPath:o,configExists:e(o),globalDataDir:M(),envPaths:{workspace:C(a),global:w()},mcp:t.mcpInfo?.()??{transport:`http`,pid:process.pid,startedAt:new Date(0).toISOString()}})}),n.get(`/schema`,(e,t)=>{t.json({config:d,env:f})}),n.get(`/config`,(e,t)=>{let n=F(r());try{let e=I(n);t.json({path:n,exists:e!==null,content:e??{}})}catch(e){if(e instanceof N){q(t,400,e.message,e.details);return}q(t,500,`config-read-failed`,e instanceof Error?e.message:String(e))}}),n.patch(`/config`,(e,n)=>{let i=U.safeParse(e.body);if(!i.success){q(n,400,`invalid-body`,i.error.issues);return}let a=F(r()),o;try{o=I(a)??{}}catch(e){if(e instanceof N){q(n,400,e.message,e.details);return}q(n,500,`config-read-failed`,e instanceof Error?e.message:String(e));return}let s;try{s=V(o,i.data.ops)}catch(e){if(e instanceof N){q(n,400,e.message,e.details);return}q(n,400,`patch-failed`,e instanceof Error?e.message:String(e));return}let c=H(s);if(c.length>0){q(n,400,`invalid-config`,c);return}try{L(a,s)}catch(e){q(n,500,`config-write-failed`,e instanceof Error?e.message:String(e));return}t.log.info(`Settings: config updated`,{path:a,ops:i.data.ops.length}),n.json({path:a,content:s,restartRecommended:!0})}),n.get(`/env`,(e,n)=>{let i=e.query.reveal===`true`||e.query.reveal===`1`;i&&t.log.warn(`Settings: env values revealed`,{ip:e.ip??`unknown`});let a=r();n.json({workspace:O(`workspace`,a,{reveal:i}),global:O(`global`,a,{reveal:i}),processOverrides:Object.keys(process.env).filter(e=>e.startsWith(`AIKIT_`)||/^(SEARXNG_URL|GOOGLE_|BRAVE_|BING_)/.test(e)).sort()})}),n.put(`/env/:key`,(e,n)=>{let i=W.safeParse(e.params.key);if(!i.success){q(n,400,`invalid-key`,i.error.issues);return}let a=G.safeParse(e.body);if(!a.success){q(n,400,`invalid-body`,a.error.issues);return}let o=a.data.scope,s=T(o,r());try{D(s,x(E(s),i.data,a.data.value)),t.log.info(`Settings: env set`,{key:i.data,scope:o,path:s}),n.json({ok:!0,scope:o,path:s,restartRecommended:!0})}catch(e){q(n,500,`env-write-failed`,e instanceof Error?e.message:String(e))}}),n.delete(`/env/:key`,(e,n)=>{let i=W.safeParse(e.params.key);if(!i.success){q(n,400,`invalid-key`,i.error.issues);return}let a=K.safeParse(e.query.scope);if(!a.success){q(n,400,`invalid-scope`,a.error.issues);return}let o=a.data,s=T(o,r());try{D(s,S(E(s),i.data)),t.log.info(`Settings: env unset`,{key:i.data,scope:o,path:s}),n.json({ok:!0,scope:o,path:s,restartRecommended:!0})}catch(e){q(n,500,`env-write-failed`,e instanceof Error?e.message:String(e))}}),n}export{J as createSettingsRouter};
|