Spaces:
Configuration error
Configuration error
Use static build step
Browse files- README.md +7 -2
- assets/__vite-browser-external-2447137e-BIHI7g3E.js +0 -1
- assets/addon-fit-DX4qG4td.js +0 -1
- assets/anthropic-Dm0nVZ1A.js +0 -0
- assets/azure-openai-responses-BGzUURKK.js +0 -1
- assets/client-Sq9UPDIz.js +0 -0
- assets/ghostty-web-C21go6Pk.js +0 -0
- assets/github-copilot-headers-CVWOSrQr.js +0 -1
- assets/google-kA_fw5EZ.js +0 -1
- assets/google-shared-tMPbwRUC.js +0 -0
- assets/google-vertex-CxMnHyvQ.js +0 -1
- assets/hash-DMDecQcg.js +0 -1
- assets/headers-CgnjaPPL.js +0 -1
- assets/index-Brjf2SFV.js +0 -2
- assets/index-CB_zB4U3.js +0 -0
- assets/index-NRwwiW4N.css +0 -1
- assets/json-parse-cUk3uCki.js +0 -2
- assets/mistral-mDLdw-bF.js +0 -0
- assets/openai-codex-responses-7plNQja4.js +0 -7
- assets/openai-completions-CTz74GS-.js +0 -6
- assets/openai-prompt-cache-h_V5PL7P.js +0 -1
- assets/openai-responses-CW5o7IM-.js +0 -1
- assets/openai-responses-shared-DBUlz2NN.js +0 -12
- assets/openrouter-DE7amXV2.js +0 -1
- assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -3
- assets/sanitize-unicode-B62XMGgN.js +0 -1
- assets/transform-messages-DSNYjDRn.js +0 -1
- assets/xterm-B-qIQCd3.js +0 -0
- index.html +1 -2
- package-lock.json +0 -0
- package.json +30 -0
- src/main.js +55 -0
- src/piAgent.js +894 -0
- src/piCli.js +524 -0
- src/piCliContract.js +101 -0
- src/sandbox.js +388 -0
- src/styles.css +226 -0
- src/webTerminal.js +88 -0
- vite.config.js +16 -0
README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
| 1 |
---
|
| 2 |
title: Pi CLI Web
|
| 3 |
sdk: static
|
| 4 |
-
|
|
|
|
| 5 |
fullWidth: true
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
models:
|
| 7 |
- onnx-community/Qwen2.5-Coder-0.5B-Instruct
|
| 8 |
- onnx-community/Qwen3-0.6B-ONNX
|
|
@@ -44,7 +49,7 @@ The app uses:
|
|
| 44 |
|
| 45 |
Vite serves the app with COOP/COEP headers and boots WebContainers with `coep: "credentialless"`. The deterministic test model is available at `http://localhost:5173/?mode=mock&device=wasm` for fast harness and sandbox smoke tests without downloading an ONNX model. The local model defaults to a tested 256-token generation budget in WASM mode and supports budgets up to 8192 through the `tokens=` query parameter and `/settings tokens=<n>`.
|
| 46 |
|
| 47 |
-
The Static Space
|
| 48 |
|
| 49 |
## Test the CLI Web App
|
| 50 |
|
|
|
|
| 1 |
---
|
| 2 |
title: Pi CLI Web
|
| 3 |
sdk: static
|
| 4 |
+
app_build_command: npm install && npm run build
|
| 5 |
+
app_file: dist/index.html
|
| 6 |
fullWidth: true
|
| 7 |
+
custom_headers:
|
| 8 |
+
cross-origin-embedder-policy: require-corp
|
| 9 |
+
cross-origin-opener-policy: same-origin
|
| 10 |
+
cross-origin-resource-policy: cross-origin
|
| 11 |
models:
|
| 12 |
- onnx-community/Qwen2.5-Coder-0.5B-Instruct
|
| 13 |
- onnx-community/Qwen3-0.6B-ONNX
|
|
|
|
| 49 |
|
| 50 |
Vite serves the app with COOP/COEP headers and boots WebContainers with `coep: "credentialless"`. The deterministic test model is available at `http://localhost:5173/?mode=mock&device=wasm` for fast harness and sandbox smoke tests without downloading an ONNX model. The local model defaults to a tested 256-token generation budget in WASM mode and supports budgets up to 8192 through the `tokens=` query parameter and `/settings tokens=<n>`.
|
| 51 |
|
| 52 |
+
The Static Space runs `npm install && npm run build` and serves `dist/index.html` through the static build ref. The Space also sets COOP/COEP/CORP headers so WebContainers and threaded WASM paths can run when the browser supports them.
|
| 53 |
|
| 54 |
## Test the CLI Web App
|
| 55 |
|
assets/__vite-browser-external-2447137e-BIHI7g3E.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
const e={};export{e as default};
|
|
|
|
|
|
assets/addon-fit-DX4qG4td.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
var p=2,c=1,d=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let i=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(i._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(e.css.cell.width===0||e.css.cell.height===0)return;let i=this._terminal.options.scrollback===0?0:this._terminal.options.overviewRuler?.width||14,s=window.getComputedStyle(this._terminal.element.parentElement),l=parseInt(s.getPropertyValue("height")),o=Math.max(0,parseInt(s.getPropertyValue("width"))),t=window.getComputedStyle(this._terminal.element),r={top:parseInt(t.getPropertyValue("padding-top")),bottom:parseInt(t.getPropertyValue("padding-bottom")),right:parseInt(t.getPropertyValue("padding-right")),left:parseInt(t.getPropertyValue("padding-left"))},a=r.top+r.bottom,n=r.right+r.left,h=l-a,m=o-n-i;return{cols:Math.max(p,Math.floor(m/e.css.cell.width)),rows:Math.max(c,Math.floor(h/e.css.cell.height))}}};export{d as FitAddon};
|
|
|
|
|
|
assets/anthropic-Dm0nVZ1A.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/azure-openai-responses-BGzUURKK.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{O as h,a as c,r as m,i as _,b as R}from"./client-Sq9UPDIz.js";import{A as y,g as l,a as I}from"./index-CB_zB4U3.js";import{h as g}from"./headers-CgnjaPPL.js";import{c as v}from"./openai-prompt-cache-h_V5PL7P.js";import{p as O,c as N,a as b}from"./openai-responses-shared-DBUlz2NN.js";import{b as P}from"./transform-messages-DSNYjDRn.js";import"./hash-DMDecQcg.js";import"./json-parse-cUk3uCki.js";import"./sanitize-unicode-B62XMGgN.js";var w={};class U extends h{constructor({baseURL:t=m("OPENAI_BASE_URL"),apiKey:e=m("AZURE_OPENAI_API_KEY"),apiVersion:s=m("OPENAI_API_VERSION"),endpoint:n,deployment:a,azureADTokenProvider:o,dangerouslyAllowBrowser:i,...u}={}){if(!s)throw new c("The OPENAI_API_VERSION environment variable is missing or empty; either provide it, or instantiate the AzureOpenAI client with an apiVersion option, like new AzureOpenAI({ apiVersion: 'My API Version' }).");if(typeof o=="function"&&(i=!0),!o&&!e)throw new c("Missing credentials. Please pass one of `apiKey` and `azureADTokenProvider`, or set the `AZURE_OPENAI_API_KEY` environment variable.");if(o&&e)throw new c("The `apiKey` and `azureADTokenProvider` arguments are mutually exclusive; only one can be passed at a time.");if(u.defaultQuery={...u.defaultQuery,"api-version":s},t){if(n)throw new c("baseURL and endpoint are mutually exclusive")}else{if(n||(n=w.AZURE_OPENAI_ENDPOINT),!n)throw new c("Must provide one of the `baseURL` or `endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable");t=`${n}/openai`}super({apiKey:o??e,baseURL:t,...u,...i!==void 0?{dangerouslyAllowBrowser:i}:{}}),this.apiVersion="",this.apiVersion=s,this.deploymentName=a}async buildRequest(t,e={}){if(z.has(t.path)&&t.method==="post"&&t.body!==void 0){if(!_(t.body))throw new Error("Expected request body to be an object");const s=this.deploymentName||t.body.model||t.__metadata?.model;s!==void 0&&!this.baseURL.includes("/deployments")&&(t.path=`/deployments/${s}${t.path}`)}return super.buildRequest(t,e)}async authHeaders(t){return typeof this._options.apiKey=="string"?R([{"api-key":this.apiKey}]):super.authHeaders(t)}}const z=new Set(["/completions","/chat/completions","/embeddings","/audio/transcriptions","/audio/translations","/audio/speech","/images/generations","/batches","/images/edits"]);var p={};const S="v1",M=new Set(["openai","openai-codex","opencode","azure-openai-responses"]);function L(r){const t=new Map;if(!r)return t;for(const e of r.split(",")){const s=e.trim();if(!s)continue;const[n,a]=s.split("=",2);!n||!a||t.set(n.trim(),a.trim())}return t}function Z(r,t){return t?.azureDeploymentName?t.azureDeploymentName:L(p.AZURE_OPENAI_DEPLOYMENT_NAME_MAP).get(r.id)||r.id}function k(r){if(r instanceof Error){const t=r.status,e=typeof t=="number"?t:void 0;return e!==void 0?`Azure OpenAI API error (${e}): ${r.message}`:r.message}try{return JSON.stringify(r)}catch{return String(r)}}const D=(r,t,e)=>{const s=new y;return(async()=>{const n=Z(r,e),a={role:"assistant",content:[],api:"azure-openai-responses",provider:r.provider,model:r.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const o=e?.apiKey||l(r.provider)||"",i=V(r,o,e);let u=x(r,t,e,n);const d=await e?.onPayload?.(u,r);d!==void 0&&(u=d);const A={...e?.signal?{signal:e.signal}:{},...e?.timeoutMs!==void 0?{timeout:e.timeoutMs}:{},maxRetries:e?.maxRetries??0},{data:E,response:f}=await i.responses.create(u,A).withResponse();if(await e?.onResponse?.({status:f.status,headers:g(f.headers)},r),s.push({type:"start",partial:a}),await O(E,a,s,r),e?.signal?.aborted)throw new Error("Request was aborted");if(a.stopReason==="aborted"||a.stopReason==="error")throw new Error("An unknown error occurred");s.push({type:"done",reason:a.stopReason,message:a}),s.end()}catch(o){for(const i of a.content)delete i.index,delete i.partialJson;a.stopReason=e?.signal?.aborted?"aborted":"error",a.errorMessage=k(o),s.push({type:"error",reason:a.stopReason,error:a}),s.end()}})(),s},F=(r,t,e)=>{const s=e?.apiKey||l(r.provider);if(!s)throw new Error(`No API key for provider: ${r.provider}`);const n=P(r,e,s),a=e?.reasoning?I(r,e.reasoning):void 0;return D(r,t,{...n,reasoningEffort:a==="off"?void 0:a})};function K(r){const t=r.trim().replace(/\/+$/,"");let e;try{e=new URL(t)}catch{throw new Error(`Invalid Azure OpenAI base URL: ${r}`)}const s=e.hostname.endsWith(".openai.azure.com")||e.hostname.endsWith(".cognitiveservices.azure.com"),n=e.pathname.replace(/\/+$/,"");return s&&(n===""||n==="/"||n==="/openai")&&(e.pathname="/openai/v1",e.search=""),e.toString().replace(/\/+$/,"")}function T(r){return`https://${r}.openai.azure.com/openai/v1`}function $(r,t){const e=t?.azureApiVersion||p.AZURE_OPENAI_API_VERSION||S,s=t?.azureBaseUrl?.trim()||p.AZURE_OPENAI_BASE_URL?.trim()||void 0,n=t?.azureResourceName||p.AZURE_OPENAI_RESOURCE_NAME;let a=s;if(!a&&n&&(a=T(n)),!a&&r.baseUrl&&(a=r.baseUrl),!a)throw new Error("Azure OpenAI base URL is required. Set AZURE_OPENAI_BASE_URL or AZURE_OPENAI_RESOURCE_NAME, or pass azureBaseUrl, azureResourceName, or model.baseUrl.");return{baseUrl:K(a),apiVersion:e}}function V(r,t,e){if(!t){if(!p.AZURE_OPENAI_API_KEY)throw new Error("Azure OpenAI API key is required. Set AZURE_OPENAI_API_KEY environment variable or pass it as an argument.");t=p.AZURE_OPENAI_API_KEY}const s={...r.headers};e?.headers&&Object.assign(s,e.headers);const{baseUrl:n,apiVersion:a}=$(r,e);return new U({apiKey:t,apiVersion:a,dangerouslyAllowBrowser:!0,defaultHeaders:s,baseURL:n})}function x(r,t,e,s){const n=N(r,t,M),a={model:s,input:n,stream:!0,prompt_cache_key:v(e?.sessionId)};if(e?.maxTokens&&(a.max_output_tokens=e?.maxTokens),e?.temperature!==void 0&&(a.temperature=e?.temperature),t.tools&&t.tools.length>0&&(a.tools=b(t.tools)),r.reasoning)if(e?.reasoningEffort||e?.reasoningSummary){const o=e?.reasoningEffort?r.thinkingLevelMap?.[e.reasoningEffort]??e.reasoningEffort:"medium";a.reasoning={effort:o,summary:e?.reasoningSummary||"auto"},a.include=["reasoning.encrypted_content"]}else r.thinkingLevelMap?.off!==null&&(a.reasoning={effort:r.thinkingLevelMap?.off??"none"});return a}export{D as streamAzureOpenAIResponses,F as streamSimpleAzureOpenAIResponses};
|
|
|
|
|
|
assets/client-Sq9UPDIz.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/ghostty-web-C21go6Pk.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/github-copilot-headers-CVWOSrQr.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
var i={};function u(r){return r==="cloudflare-workers-ai"||r==="cloudflare-ai-gateway"}function l(r){const e=r.baseUrl;return e.includes("{")?e.replace(/\{([A-Z_][A-Z0-9_]*)\}/g,(s,n)=>{const o=i[n];if(!o)throw new Error(`${n} is required for provider ${r.provider} but is not set.`);return o}):e}function a(r){const e=r[r.length-1];return e&&e.role!=="user"?"agent":"user"}function c(r){return r.some(e=>e.role==="user"&&Array.isArray(e.content)?e.content.some(t=>t.type==="image"):e.role==="toolResult"&&Array.isArray(e.content)?e.content.some(t=>t.type==="image"):!1)}function f(r){const e={"X-Initiator":a(r.messages),"Openai-Intent":"conversation-edits"};return r.hasImages&&(e["Copilot-Vision-Request"]="true"),e}export{f as b,c as h,u as i,l as r};
|
|
|
|
|
|
assets/google-kA_fw5EZ.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{i as T,r as b,m as v,G as S,c as R,a as L,b as _}from"./google-shared-tMPbwRUC.js";import{A as G,g as x,c as O,a as A}from"./index-CB_zB4U3.js";import{s as N}from"./sanitize-unicode-B62XMGgN.js";import{b as E}from"./transform-messages-DSNYjDRn.js";let P=0;const f=(e,o,n)=>{const i=new G;return(async()=>{const t={role:"assistant",content:[],api:"google-generative-ai",provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const r=n?.apiKey||x(e.provider)||"",c=W(e,r,n?.headers);let u=B(e,o,n);const y=await n?.onPayload?.(u,e);y!==void 0&&(u=y);const M=await c.models.generateContentStream(u);i.push({type:"start",partial:t});let a=null;const C=t.content,l=()=>C.length-1;for await(const g of M){t.responseId||=g.responseId;const d=g.candidates?.[0];if(d?.content?.parts)for(const s of d.content.parts){if(s.text!==void 0){const h=T(s);(!a||h&&a.type!=="thinking"||!h&&a.type!=="text")&&(a&&(a.type==="text"?i.push({type:"text_end",contentIndex:C.length-1,content:a.text,partial:t}):i.push({type:"thinking_end",contentIndex:l(),content:a.thinking,partial:t})),h?(a={type:"thinking",thinking:"",thinkingSignature:void 0},t.content.push(a),i.push({type:"thinking_start",contentIndex:l(),partial:t})):(a={type:"text",text:""},t.content.push(a),i.push({type:"text_start",contentIndex:l(),partial:t}))),a.type==="thinking"?(a.thinking+=s.text,a.thinkingSignature=b(a.thinkingSignature,s.thoughtSignature),i.push({type:"thinking_delta",contentIndex:l(),delta:s.text,partial:t})):(a.text+=s.text,a.textSignature=b(a.textSignature,s.thoughtSignature),i.push({type:"text_delta",contentIndex:l(),delta:s.text,partial:t}))}if(s.functionCall){a&&(a.type==="text"?i.push({type:"text_end",contentIndex:l(),content:a.text,partial:t}):i.push({type:"thinking_end",contentIndex:l(),content:a.thinking,partial:t}),a=null);const h=s.functionCall.id,p={type:"toolCall",id:!h||t.content.some(I=>I.type==="toolCall"&&I.id===h)?`${s.functionCall.name}_${Date.now()}_${++P}`:h,name:s.functionCall.name||"",arguments:s.functionCall.args??{},...s.thoughtSignature&&{thoughtSignature:s.thoughtSignature}};t.content.push(p),i.push({type:"toolcall_start",contentIndex:l(),partial:t}),i.push({type:"toolcall_delta",contentIndex:l(),delta:JSON.stringify(p.arguments),partial:t}),i.push({type:"toolcall_end",contentIndex:l(),toolCall:p,partial:t})}}d?.finishReason&&(t.stopReason=v(d.finishReason),t.content.some(s=>s.type==="toolCall")&&(t.stopReason="toolUse")),g.usageMetadata&&(t.usage={input:(g.usageMetadata.promptTokenCount||0)-(g.usageMetadata.cachedContentTokenCount||0),output:(g.usageMetadata.candidatesTokenCount||0)+(g.usageMetadata.thoughtsTokenCount||0),cacheRead:g.usageMetadata.cachedContentTokenCount||0,cacheWrite:0,totalTokens:g.usageMetadata.totalTokenCount||0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},O(e,t.usage))}if(a&&(a.type==="text"?i.push({type:"text_end",contentIndex:l(),content:a.text,partial:t}):i.push({type:"thinking_end",contentIndex:l(),content:a.thinking,partial:t})),n?.signal?.aborted)throw new Error("Request was aborted");if(t.stopReason==="aborted"||t.stopReason==="error")throw new Error("An unknown error occurred");i.push({type:"done",reason:t.stopReason,message:t}),i.end()}catch(r){for(const c of t.content)"index"in c&&delete c.index;t.stopReason=n?.signal?.aborted?"aborted":"error",t.errorMessage=r instanceof Error?r.message:JSON.stringify(r),i.push({type:"error",reason:t.stopReason,error:t}),i.end()}})(),i},F=(e,o,n)=>{const i=n?.apiKey||x(e.provider);if(!i)throw new Error(`No API key for provider: ${e.provider}`);const t=E(e,n,i);if(!n?.reasoning)return f(e,o,{...t,thinking:{enabled:!1}});const r=A(e,n.reasoning),c=r==="off"?"high":r,u=e;return m(u)||w(u)||k(u)?f(e,o,{...t,thinking:{enabled:!0,level:K(c,u)}}):f(e,o,{...t,thinking:{enabled:!0,budgetTokens:U(u,c,n.thinkingBudgets)}})};function W(e,o,n){const i={};return e.baseUrl&&(i.baseUrl=e.baseUrl,i.apiVersion=""),(e.headers||n)&&(i.headers={...e.headers,...n}),new S({apiKey:o,httpOptions:Object.keys(i).length>0?i:void 0})}function B(e,o,n={}){const i=R(e,o),t={};n.temperature!==void 0&&(t.temperature=n.temperature),n.maxTokens!==void 0&&(t.maxOutputTokens=n.maxTokens);const r={...Object.keys(t).length>0&&t,...o.systemPrompt&&{systemInstruction:N(o.systemPrompt)},...o.tools&&o.tools.length>0&&{tools:L(o.tools)}};if(o.tools&&o.tools.length>0&&n.toolChoice?r.toolConfig={functionCallingConfig:{mode:_(n.toolChoice)}}:r.toolConfig=void 0,n.thinking?.enabled&&e.reasoning){const u={includeThoughts:!0};n.thinking.level!==void 0?u.thinkingLevel=n.thinking.level:n.thinking.budgetTokens!==void 0&&(u.thinkingBudget=n.thinking.budgetTokens),r.thinkingConfig=u}else e.reasoning&&n.thinking&&!n.thinking.enabled&&(r.thinkingConfig=H(e));if(n.signal){if(n.signal.aborted)throw new Error("Request aborted");r.abortSignal=n.signal}return{model:e.id,contents:i,config:r}}function k(e){return/gemma-?4/.test(e.id.toLowerCase())}function m(e){return/gemini-3(?:\.\d+)?-pro/.test(e.id.toLowerCase())}function w(e){return/gemini-3(?:\.\d+)?-flash/.test(e.id.toLowerCase())}function H(e){return m(e)?{thinkingLevel:"LOW"}:w(e)?{thinkingLevel:"MINIMAL"}:k(e)?{thinkingLevel:"MINIMAL"}:{thinkingBudget:0}}function K(e,o){if(m(o))switch(e){case"minimal":case"low":return"LOW";case"medium":case"high":return"HIGH"}if(k(o))switch(e){case"minimal":case"low":return"MINIMAL";case"medium":case"high":return"HIGH"}switch(e){case"minimal":return"MINIMAL";case"low":return"LOW";case"medium":return"MEDIUM";case"high":return"HIGH"}}function U(e,o,n){return n?.[o]!==void 0?n[o]:e.id.includes("2.5-pro")?{minimal:128,low:2048,medium:8192,high:32768}[o]:e.id.includes("2.5-flash-lite")?{minimal:512,low:2048,medium:8192,high:24576}[o]:e.id.includes("2.5-flash")?{minimal:128,low:2048,medium:8192,high:24576}[o]:-1}export{f as streamGoogle,F as streamSimpleGoogle};
|
|
|
|
|
|
assets/google-shared-tMPbwRUC.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/google-vertex-CxMnHyvQ.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{i as T,r as x,m as w,G as L,c as R,a as M,b as G,T as d,R as S}from"./google-shared-tMPbwRUC.js";import{A,c as N,a as P}from"./index-CB_zB4U3.js";import{s as U}from"./sanitize-unicode-B62XMGgN.js";import{b as D}from"./transform-messages-DSNYjDRn.js";var f={};const v="v1",V="gcp-vertex-credentials",H={THINKING_LEVEL_UNSPECIFIED:d.THINKING_LEVEL_UNSPECIFIED,MINIMAL:d.MINIMAL,LOW:d.LOW,MEDIUM:d.MEDIUM,HIGH:d.HIGH};let K=0;const k=(t,e,n)=>{const a=new A;return(async()=>{const i={role:"assistant",content:[],api:"google-vertex",provider:t.provider,model:t.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const s=$(n),l=s?j(t,s,n?.headers):W(t,F(n),z(n),n?.headers);let g=X(t,e,n);const I=await n?.onPayload?.(g,t);I!==void 0&&(g=I);const E=await l.models.generateContentStream(g);a.push({type:"start",partial:i});let o=null;const y=i.content,c=()=>y.length-1;for await(const u of E){i.responseId||=u.responseId;const p=u.candidates?.[0];if(p?.content?.parts)for(const r of p.content.parts){if(r.text!==void 0){const h=T(r);(!o||h&&o.type!=="thinking"||!h&&o.type!=="text")&&(o&&(o.type==="text"?a.push({type:"text_end",contentIndex:y.length-1,content:o.text,partial:i}):a.push({type:"thinking_end",contentIndex:c(),content:o.thinking,partial:i})),h?(o={type:"thinking",thinking:"",thinkingSignature:void 0},i.content.push(o),a.push({type:"thinking_start",contentIndex:c(),partial:i})):(o={type:"text",text:""},i.content.push(o),a.push({type:"text_start",contentIndex:c(),partial:i}))),o.type==="thinking"?(o.thinking+=r.text,o.thinkingSignature=x(o.thinkingSignature,r.thoughtSignature),a.push({type:"thinking_delta",contentIndex:c(),delta:r.text,partial:i})):(o.text+=r.text,o.textSignature=x(o.textSignature,r.thoughtSignature),a.push({type:"text_delta",contentIndex:c(),delta:r.text,partial:i}))}if(r.functionCall){o&&(o.type==="text"?a.push({type:"text_end",contentIndex:c(),content:o.text,partial:i}):a.push({type:"thinking_end",contentIndex:c(),content:o.thinking,partial:i}),o=null);const h=r.functionCall.id,m={type:"toolCall",id:!h||i.content.some(O=>O.type==="toolCall"&&O.id===h)?`${r.functionCall.name}_${Date.now()}_${++K}`:h,name:r.functionCall.name||"",arguments:r.functionCall.args??{},...r.thoughtSignature&&{thoughtSignature:r.thoughtSignature}};i.content.push(m),a.push({type:"toolcall_start",contentIndex:c(),partial:i}),a.push({type:"toolcall_delta",contentIndex:c(),delta:JSON.stringify(m.arguments),partial:i}),a.push({type:"toolcall_end",contentIndex:c(),toolCall:m,partial:i})}}p?.finishReason&&(i.stopReason=w(p.finishReason),i.content.some(r=>r.type==="toolCall")&&(i.stopReason="toolUse")),u.usageMetadata&&(i.usage={input:(u.usageMetadata.promptTokenCount||0)-(u.usageMetadata.cachedContentTokenCount||0),output:(u.usageMetadata.candidatesTokenCount||0)+(u.usageMetadata.thoughtsTokenCount||0),cacheRead:u.usageMetadata.cachedContentTokenCount||0,cacheWrite:0,totalTokens:u.usageMetadata.totalTokenCount||0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},N(t,i.usage))}if(o&&(o.type==="text"?a.push({type:"text_end",contentIndex:c(),content:o.text,partial:i}):a.push({type:"thinking_end",contentIndex:c(),content:o.thinking,partial:i})),n?.signal?.aborted)throw new Error("Request was aborted");if(i.stopReason==="aborted"||i.stopReason==="error")throw new Error("An unknown error occurred");a.push({type:"done",reason:i.stopReason,message:i}),a.end()}catch(s){for(const l of i.content)"index"in l&&delete l.index;i.stopReason=n?.signal?.aborted?"aborted":"error",i.errorMessage=s instanceof Error?s.message:JSON.stringify(s),a.push({type:"error",reason:i.stopReason,error:i}),a.end()}})(),a},rt=(t,e,n)=>{const a=D(t,n,void 0);if(!n?.reasoning)return k(t,e,{...a,thinking:{enabled:!1}});const i=P(t,n.reasoning),s=i==="off"?"high":i,l=t;return C(l)||b(l)?k(t,e,{...a,thinking:{enabled:!0,level:Q(s,l)}}):k(t,e,{...a,thinking:{enabled:!0,budgetTokens:Z(l,s,n.thinkingBudgets)}})};function W(t,e,n,a){return new L({vertexai:!0,project:e,location:n,apiVersion:v,httpOptions:_(t,a)})}function j(t,e,n){return new L({vertexai:!0,apiKey:e,apiVersion:v,httpOptions:_(t,n)})}function _(t,e){const n={},a=B(t.baseUrl);return a&&(n.baseUrl=a,n.baseUrlResourceScope=S.COLLECTION,J(a)&&(n.apiVersion="")),(t.headers||e)&&(n.headers={...t.headers,...e}),Object.keys(n).length>0?n:void 0}function B(t){const e=t.trim();if(!(!e||e.includes("{location}")))return e}function J(t){try{return new URL(t).pathname.split("/").some(n=>/^v\d+(?:beta\d*)?$/.test(n))}catch{return/(?:^|\/)v\d+(?:beta\d*)?(?:\/|$)/.test(t)}}function $(t){const e=t?.apiKey?.trim()||f.GOOGLE_CLOUD_API_KEY?.trim();if(!(!e||e===V||q(e)))return e}function q(t){return/^<[^>]+>$/.test(t)}function F(t){const e=t?.project||f.GOOGLE_CLOUD_PROJECT||f.GCLOUD_PROJECT;if(!e)throw new Error("Vertex AI requires a project ID. Set GOOGLE_CLOUD_PROJECT/GCLOUD_PROJECT or pass project in options.");return e}function z(t){const e=t?.location||f.GOOGLE_CLOUD_LOCATION;if(!e)throw new Error("Vertex AI requires a location. Set GOOGLE_CLOUD_LOCATION or pass location in options.");return e}function X(t,e,n={}){const a=R(t,e),i={};n.temperature!==void 0&&(i.temperature=n.temperature),n.maxTokens!==void 0&&(i.maxOutputTokens=n.maxTokens);const s={...Object.keys(i).length>0&&i,...e.systemPrompt&&{systemInstruction:U(e.systemPrompt)},...e.tools&&e.tools.length>0&&{tools:M(e.tools)}};if(e.tools&&e.tools.length>0&&n.toolChoice?s.toolConfig={functionCallingConfig:{mode:G(n.toolChoice)}}:s.toolConfig=void 0,n.thinking?.enabled&&t.reasoning){const g={includeThoughts:!0};n.thinking.level!==void 0?g.thinkingLevel=H[n.thinking.level]:n.thinking.budgetTokens!==void 0&&(g.thinkingBudget=n.thinking.budgetTokens),s.thinkingConfig=g}else t.reasoning&&n.thinking&&!n.thinking.enabled&&(s.thinkingConfig=Y(t));if(n.signal){if(n.signal.aborted)throw new Error("Request aborted");s.abortSignal=n.signal}return{model:t.id,contents:a,config:s}}function C(t){return/gemini-3(?:\.\d+)?-pro/.test(t.id.toLowerCase())}function b(t){return/gemini-3(?:\.\d+)?-flash/.test(t.id.toLowerCase())}function Y(t){const e=t;return C(e)?{thinkingLevel:d.LOW}:b(e)?{thinkingLevel:d.MINIMAL}:{thinkingBudget:0}}function Q(t,e){if(C(e))switch(t){case"minimal":case"low":return"LOW";case"medium":case"high":return"HIGH"}switch(t){case"minimal":return"MINIMAL";case"low":return"LOW";case"medium":return"MEDIUM";case"high":return"HIGH"}}function Z(t,e,n){return n?.[e]!==void 0?n[e]:t.id.includes("2.5-pro")?{minimal:128,low:2048,medium:8192,high:32768}[e]:t.id.includes("2.5-flash")?{minimal:128,low:2048,medium:8192,high:24576}[e]:-1}export{k as streamGoogleVertex,rt as streamSimpleGoogleVertex};
|
|
|
|
|
|
assets/hash-DMDecQcg.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
function l(a){let t=3735928559,h=1103547991;for(let e=0;e<a.length;e++){const i=a.charCodeAt(e);t=Math.imul(t^i,2654435761),h=Math.imul(h^i,1597334677)}return t=Math.imul(t^t>>>16,2246822507)^Math.imul(h^h>>>13,3266489909),h=Math.imul(h^h>>>16,2246822507)^Math.imul(t^t>>>13,3266489909),(h>>>0).toString(36)+(t>>>0).toString(36)}export{l as s};
|
|
|
|
|
|
assets/headers-CgnjaPPL.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
function n(o){const e={};for(const[r,t]of o.entries())e[r]=t;return e}export{n as h};
|
|
|
|
|
|
assets/index-Brjf2SFV.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
const Q="https://stackblitz.com",X=new Error;X.stack="";const x={};let g=null;const v={get editorOrigin(){return g==null&&(g=new URL(globalThis.WEBCONTAINER_API_IFRAME_URL??Q).origin),g},set editorOrigin(e){g=new URL(e).origin},setQueryParam(e,t){x[e]=t},get url(){const e=new URL(this.editorOrigin);e.pathname="/headless";for(const t in x)e.searchParams.set(t,x[t]);return e.searchParams.set("version","1.6.4"),e}};function $(){let e,t;function r(){t=new Promise(n=>e=n)}return r(),{get promise(){return t},resolve(n){return e(n)},reset:r}}$();var _;(function(e){e.UncaughtException="PREVIEW_UNCAUGHT_EXCEPTION",e.UnhandledRejection="PREVIEW_UNHANDLED_REJECTION",e.ConsoleError="PREVIEW_CONSOLE_ERROR"})(_||(_={}));var K=Object.defineProperty,Z=(e,t)=>{for(var r in t)K(e,r,{get:t[r],enumerable:!0})},d={};Z(d,{createEndpoint:()=>M,expose:()=>C,proxy:()=>H,proxyMarker:()=>O,releaseProxy:()=>T,transfer:()=>W,transferHandlers:()=>A,windowEndpoint:()=>se,wrap:()=>z});var O=Symbol("Comlink.proxy"),M=Symbol("Comlink.endpoint"),T=Symbol("Comlink.releaseProxy"),I=Symbol("Comlink.thrown"),j=e=>typeof e=="object"&&e!==null||typeof e=="function",ee={canHandle:e=>j(e)&&e[O],serialize(e){const{port1:t,port2:r}=new MessageChannel;return C(e,t),[r,[r]]},deserialize(e){return e.start(),z(e)}},te={canHandle:e=>j(e)&&I in e,serialize({value:e}){let t;return e instanceof Error?t={isError:!0,value:{message:e.message,name:e.name,stack:e.stack}}:t={isError:!1,value:e},[t,[]]},deserialize(e){throw e.isError?Object.assign(new Error(e.value.message),e.value):e.value}},A=new Map([["proxy",ee],["throw",te]]);function C(e,t=self){t.addEventListener("message",function r(n){if(!n||!n.data)return;const{id:s,type:i,path:o}=Object.assign({path:[]},n.data),a=(n.data.argumentList||[]).map(m);let c;try{const l=o.slice(0,-1).reduce((u,w)=>u[w],e),f=o.reduce((u,w)=>u[w],e);switch(i){case 0:c=f;break;case 1:l[o.slice(-1)[0]]=m(n.data.value),c=!0;break;case 2:c=f.apply(l,a);break;case 3:{const u=new f(...a);c=H(u)}break;case 4:{const{port1:u,port2:w}=new MessageChannel;C(e,w),c=W(u,[u])}break;case 5:c=void 0;break}}catch(l){c={value:l,[I]:0}}Promise.resolve(c).catch(l=>({value:l,[I]:0})).then(l=>{const[f,u]=N(l);t.postMessage(Object.assign(Object.assign({},f),{id:s}),u),i===5&&(t.removeEventListener("message",r),D(t))})}),t.start&&t.start()}function re(e){return e.constructor.name==="MessagePort"}function D(e){re(e)&&e.close()}function z(e,t){return L(e,[],t)}function b(e){if(e)throw new Error("Proxy has been released and is not useable")}function L(e,t=[],r=function(){}){let n=!1;const s=new Proxy(r,{get(i,o){if(b(n),o===T)return()=>p(e,{type:5,path:t.map(a=>a.toString())}).then(()=>{D(e),n=!0});if(o==="then"){if(t.length===0)return{then:()=>s};const a=p(e,{type:0,path:t.map(c=>c.toString())}).then(m);return a.then.bind(a)}return L(e,[...t,o])},set(i,o,a){b(n);const[c,l]=N(a);return p(e,{type:1,path:[...t,o].map(f=>f.toString()),value:c},l).then(m)},apply(i,o,a){b(n);const c=t[t.length-1];if(c===M)return p(e,{type:4}).then(m);if(c==="bind")return L(e,t.slice(0,-1));const[l,f]=F(a);return p(e,{type:2,path:t.map(u=>u.toString()),argumentList:l},f).then(m)},construct(i,o){b(n);const[a,c]=F(o);return p(e,{type:3,path:t.map(l=>l.toString()),argumentList:a},c).then(m)}});return s}function ne(e){return Array.prototype.concat.apply([],e)}function F(e){const t=e.map(N);return[t.map(r=>r[0]),ne(t.map(r=>r[1]))]}var V=new WeakMap;function W(e,t){return V.set(e,t),e}function H(e){return Object.assign(e,{[O]:!0})}function se(e,t=self,r="*"){return{postMessage:(n,s)=>e.postMessage(n,r,s),addEventListener:t.addEventListener.bind(t),removeEventListener:t.removeEventListener.bind(t)}}function N(e){for(const[t,r]of A)if(r.canHandle(e)){const[n,s]=r.serialize(e);return[{type:3,name:t,value:n},s]}return[{type:0,value:e},V.get(e)||[]]}function m(e){switch(e.type){case 3:return A.get(e.name).deserialize(e.value);case 0:return e.value}}function p(e,t,r){return new Promise(n=>{const s=ie();e.addEventListener("message",function i(o){!o.data||!o.data.id||o.data.id!==s||(e.removeEventListener("message",i),n(o.data))}),e.start&&e.start(),e.postMessage(Object.assign({id:s},t),r)})}function ie(){return new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")}const oe=[_.ConsoleError,_.UncaughtException,_.UnhandledRejection];function ae(e){return!(e==null||typeof e!="object"||!("type"in e)||!oe.includes(e.type))}function y(e){const t=Object.create(null);return e?Object.assign(t,e):t}function B(e){const t={d:{}};for(const r of Object.keys(e)){const n=e[r];if("file"in n){if("symlink"in n.file){t.d[r]={f:{l:n.file.symlink}};continue}const i=n.file.contents,o=typeof i=="string"?i:le(i),a=typeof i=="string"?{}:{b:!0};t.d[r]={f:{c:o,...a}};continue}const s=B(n.directory);t.d[r]=s}return t}function G(e){const t=y();if("f"in e)throw new Error("It is not possible to export a single file in the JSON format.");if("d"in e)for(const r of Object.keys(e.d)){const n=e.d[r];"d"in n?t[r]=y({directory:G(n)}):"f"in n&&("c"in n.f?t[r]=y({file:y({contents:n.f.b?ce(n.f.c):n.f.c})}):"l"in n.f&&(t[r]=y({file:y({symlink:n.f.l})})))}return t}function ce(e){const t=new Uint8Array(e.length);for(let r=0;r<e.length;r++)t[r]=e[r].charCodeAt(0);return t}function le(e){let t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t}let E=null,P=null,k={};const Y=new TextDecoder,ue=new TextEncoder;class h{_instance;_runtimeInfo;fs;static _instance=null;static _teardownPromise=null;_tornDown=!1;_unsubscribeFromTokenChangedListener=()=>{};constructor(t,r,n,s){this._instance=t,this._runtimeInfo=s,this.fs=new pe(r)}async spawn(t,r,n){let s=[];Array.isArray(r)?s=r:n=r;let i,o=new ReadableStream;if(n?.output!==!1){const U=Ee();i=U.push,o=U.stream}let a,c,l,f;const u=S(R(i)),w=S(R(a)),q=S(R(l)),J=await this._instance.run({command:t,args:s,cwd:n?.cwd,env:n?.env,terminal:n?.terminal},w,q,u);return new me(J,o,c,f)}async export(t,r){const n={format:r?.format??"json",includes:r?.includes,excludes:r?.excludes,external:!0},s=await this._instance.serialize(t,n);if(n.format==="json"){const i=JSON.parse(Y.decode(s));return G(i)}return s}on(t,r){if(t==="preview-message"){const i=r;r=(o=>{ae(o)&&i(o)})}const{listener:n,subscribe:s}=Pe(r);return s(this._instance.on(t,d.proxy(n)))}mount(t,r){const n=t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):ue.encode(JSON.stringify(B(t)));return this._instance.loadFiles(d.transfer(n,[n.buffer]),{mountPoints:r?.mountPoint})}setPreviewScript(t,r){return this._instance.setPreviewScript(t,r)}get path(){return this._runtimeInfo.path}get workdir(){return this._runtimeInfo.cwd}teardown(){if(this._tornDown)throw new Error("WebContainer already torn down");this._tornDown=!0,this._unsubscribeFromTokenChangedListener();const t=async()=>{try{await this.fs._teardown(),await this._instance.teardown()}finally{this._instance[d.releaseProxy](),h._instance===this&&(h._instance=null)}};h._teardownPromise=t()}static async boot(t={}){await this._teardownPromise,h._teardownPromise=null;const{workdirName:r}=t;if(window.crossOriginIsolated&&t.coep==="none"&&console.warn(`A Cross-Origin-Embedder-Policy header is required in cross origin isolated environments.
|
| 2 |
-
Set the 'coep' option to 'require-corp'.`),r?.includes("/")||r===".."||r===".")throw new Error("workdirName should be a valid folder name");for(;E;)await E;if(h._instance)throw new Error("Only a single WebContainer instance can be booted");const n=ye(t);E=n.catch(()=>{});try{const s=await n;return h._instance=s,s}finally{E=null}}}const fe=1,de=2;class he{name;_type;constructor(t,r){this.name=t,this._type=r}isFile(){return this._type===fe}isDirectory(){return this._type===de}}class we{_apiClient;_path;_options;_listener;_wrappedListener;_watcher;_closed=!1;constructor(t,r,n,s){this._apiClient=t,this._path=r,this._options=n,this._listener=s,this._apiClient._watchers.add(this),this._wrappedListener=(i,o)=>{this._listener&&!this._closed&&this._listener(i,o)},this._apiClient._fs.watch(this._path,this._options,S(this._wrappedListener)).then(i=>{if(this._watcher=i,this._closed)return this._teardown()}).catch(console.error)}async close(){this._closed||(this._closed=!0,this._apiClient._watchers.delete(this),await this._teardown())}async _teardown(){await this._watcher?.close().finally(()=>{this._watcher?.[d.releaseProxy]()})}}class me{output;input;exit;_process;stdout;stderr;constructor(t,r,n,s){this.output=r,this._process=t,this.input=new WritableStream({write:i=>{this._getProcess()?.write(i).catch(()=>{})}}),this.exit=this._onExit(),this.stdout=n,this.stderr=s}kill(){this._process?.kill()}resize(t){this._getProcess()?.resize(t)}async _onExit(){try{return await this._process.onExit}finally{this._process?.[d.releaseProxy](),this._process=null}}_getProcess(){return this._process==null&&console.warn("This process already exited"),this._process}}class pe{_fs;_watchers=new Set([]);constructor(t){this._fs=t}rm(...t){return this._fs.rm(...t)}async readFile(t,r){return await this._fs.readFile(t,r)}async rename(t,r){return await this._fs.rename(t,r)}async writeFile(t,r,n){if(r instanceof Uint8Array){const s=r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength);r=d.transfer(new Uint8Array(s),[s])}await this._fs.writeFile(t,r,n)}async readdir(t,r){const n=await this._fs.readdir(t,r);return ge(n)||be(n)?n:n.map(i=>new he(i.name,i["Symbol(type)"]))}async mkdir(t,r){return await this._fs.mkdir(t,r)}watch(t,r,n){return typeof r=="function"&&(n=r,r=null),new we(this,t,r,n)}async _teardown(){this._fs[d.releaseProxy](),await Promise.all([...this._watchers].map(t=>t.close()))}}async function ye(e){const{serverPromise:t}=_e(e),n=await(await t).build({host:window.location.host,version:"1.6.4",workdirName:e.workdirName,forwardPreviewErrors:e.forwardPreviewErrors}),[s,i,o]=await Promise.all([n.fs(),n.previewScript(),n.runtimeInfo()]);return new h(n,s,i,o)}function R(e){if(e!=null)return t=>{t instanceof Uint8Array?e(Y.decode(t)):t==null&&e(null)}}function S(e){if(e!=null)return d.proxy(e)}function _e(e){if(P!=null)return e.coep!==k.coep&&(console.warn(`Attempting to boot WebContainer with 'coep: ${e.coep}'`),console.warn(`First boot had 'coep: ${k.coep}', new settings will not take effect!`)),{serverPromise:P};e.coep&&v.setQueryParam("coep",e.coep),e.experimentalNode&&v.setQueryParam("experimental_node","1");const t=document.createElement("iframe");t.style.display="none",t.setAttribute("allow","cross-origin-isolated");const r=v.url;t.src=r.toString();const{origin:n}=r;return k={...e},P=new Promise(s=>{const i=o=>{if(o.origin!==n)return;const{data:a}=o;if(a.type==="init"){s(d.wrap(o.ports[0]));return}if(a.type==="warning"){console[a.level].call(console,a.message);return}};window.addEventListener("message",i)}),document.body.insertBefore(t,null),{serverPromise:P}}function ge(e){return typeof e[0]=="string"}function be(e){return e[0]instanceof Uint8Array}function Ee(){let e=null;return{stream:new ReadableStream({start(n){e=n}}),push:n=>{n!=null?e?.enqueue(n):(e?.close(),e=null)}}}function Pe(e){let t=!1,r=()=>{};return{subscribe(s){return s.then(i=>{r=i,t&&r()}),()=>{t=!0,r()}},listener:((...s)=>{t||e(...s)})}}export{_ as PreviewMessageType,h as WebContainer,ae as isPreviewMessage};
|
|
|
|
|
|
|
|
|
assets/index-CB_zB4U3.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/index-NRwwiW4N.css
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
:root{color:#d6deeb;background:#0b0f16;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}html,body,#app{min-width:320px;min-height:100vh;margin:0}button,select{font:inherit}button{min-height:40px;padding:0 15px;border:0;border-radius:6px;background:#227c70;color:#fff;font-weight:750;cursor:pointer}button:disabled{cursor:wait;opacity:.6}#app{display:grid;grid-template-rows:auto minmax(0,1fr);gap:10px;padding:10px}.runtime-bar{display:flex;align-items:end;justify-content:space-between;gap:16px;min-height:54px;padding:4px 2px 10px;border-bottom:1px solid #263244}h1,h2,p{margin:0}h1{color:#f8fafc;font-size:30px;line-height:1;letter-spacing:0}#model-label{margin-top:5px;color:#9eb1ca;font-size:13px;overflow-wrap:anywhere}.status-stack{display:flex;flex-wrap:wrap;justify-content:end;gap:8px;max-width:760px}.status{min-height:32px;max-width:280px;padding:6px 10px;border:1px solid #344156;border-radius:6px;background:#141b27;color:#cad7ea;font-size:13px;overflow-wrap:anywhere}.terminal-shell{min-height:0;border:1px solid #263244;border-radius:8px;background:#10141c;overflow:hidden}#terminal{width:100%;height:calc(100vh - 86px);min-height:420px;padding:10px}.xterm{height:100%}.xterm-viewport,.xterm-screen{border-radius:0}.model-gate{position:fixed;inset:0;display:grid;place-items:center;padding:18px;background:#04080ea3;z-index:20}.model-gate.hidden{display:none}.model-dialog{display:grid;gap:16px;width:min(560px,100%);padding:22px;border:1px solid #334155;border-radius:8px;background:#f8fafc;color:#172033;box-shadow:0 24px 80px #00000059}.eyebrow{margin-bottom:7px;color:#227c70;font-size:12px;font-weight:850;text-transform:uppercase}.model-dialog h2{color:#172033;font-size:24px;line-height:1.15}.dialog-copy{margin-top:8px;color:#526173;line-height:1.5}.dialog-actions{display:flex;flex-wrap:wrap;gap:10px}#use-test-model{background:#4f5f73}.dialog-options{display:grid;gap:10px;color:#526173}label{display:grid;gap:6px;font-size:13px;font-weight:700}select{width:100%;height:40px;padding:0 10px;border:1px solid #b8c5d4;border-radius:6px;background:#fff;color:#152033}#gate-status{min-height:22px;color:#526173;overflow-wrap:anywhere}@media(max-width:760px){#app{padding:8px}.runtime-bar{align-items:stretch;flex-direction:column}.status-stack{justify-content:start}#terminal{height:calc(100vh - 164px);min-height:360px;padding:8px}}.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;-webkit-user-select:text;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}
|
|
|
|
|
|
assets/json-parse-cUk3uCki.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
import{d as o}from"./index-CB_zB4U3.js";const a=new Set(['"',"\\","/","b","f","n","r","t","u"]);function f(t){const r=t.codePointAt(0);return r!==void 0&&r>=0&&r<=31}function d(t){switch(t){case"\b":return"\\b";case"\f":return"\\f";case`
|
| 2 |
-
`:return"\\n";case"\r":return"\\r";case" ":return"\\t";default:return`\\u${t.codePointAt(0)?.toString(16).padStart(4,"0")??"0000"}`}}function s(t){let r="",c=!1;for(let e=0;e<t.length;e++){const n=t[e];if(!c){r+=n,n==='"'&&(c=!0);continue}if(n==='"'){r+=n,c=!1;continue}if(n==="\\"){const i=t[e+1];if(i===void 0){r+="\\\\";continue}if(i==="u"){const u=t.slice(e+2,e+6);if(/^[0-9a-fA-F]{4}$/.test(u)){r+=`\\u${u}`,e+=5;continue}}if(a.has(i)){r+=`\\${i}`,e+=1;continue}r+="\\\\";continue}r+=f(n)?d(n):n}return r}function h(t){try{return JSON.parse(t)}catch(r){const c=s(t);if(c!==t)return JSON.parse(c);throw r}}function l(t){if(!t||t.trim()==="")return{};try{return h(t)}catch{try{return o.parse(t)??{}}catch{try{return o.parse(s(t))??{}}catch{return{}}}}}export{l as a,h as p};
|
|
|
|
|
|
|
|
|
assets/mistral-mDLdw-bF.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/openai-codex-responses-7plNQja4.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
import{A as ue,g as Y,a as le}from"./index-CB_zB4U3.js";import{h as z}from"./headers-CgnjaPPL.js";import{c as de}from"./openai-prompt-cache-h_V5PL7P.js";import{c as V,a as fe,p as G}from"./openai-responses-shared-DBUlz2NN.js";import{b as pe}from"./transform-messages-DSNYjDRn.js";import"./hash-DMDecQcg.js";import"./json-parse-cUk3uCki.js";import"./sanitize-unicode-B62XMGgN.js";const $=new Set;function me(e){return $.add(e),()=>{$.delete(e)}}function L(e){return e instanceof Error?e.message||e.name:typeof e=="string"?e:String(e)}function ye(e){if(!(e instanceof Error))return{name:"ThrownValue",message:L(e)};const r=e.code;return{name:e.name||void 0,message:e.message||e.name,stack:e.stack,code:typeof r=="string"||typeof r=="number"?r:void 0}}function be(e,r,t){return{type:e,timestamp:Date.now(),error:ye(r),details:t}}function we(e,r){e.diagnostics=[...e.diagnostics??[],r]}function ge(e){const r=e.filter(o=>o!==void 0);if(r.length===0)return{cleanup:()=>{}};if(r.length===1)return{signal:r[0],cleanup:()=>{}};const t=new AbortController,s=[],n=o=>{t.signal.aborted||t.abort(o.reason)};for(const o of r){if(o.aborted){n(o);break}const a=()=>n(o);o.addEventListener("abort",a,{once:!0}),s.push({signal:o,listener:a})}return{signal:t.signal,cleanup:()=>{for(const{signal:o,listener:a}of s)o.removeEventListener("abort",a)}}}var A={},he=function(e,r){return typeof e=="string"&&/^\.\.?\//.test(e)?e.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(t,s,n,o,a){return s?".js":n&&(!o||!a)?t:n+o+"."+a.toLowerCase()+"js"}):e};let x=null;const Q=e=>import(he(e)),Ee="node:os";typeof process<"u"&&(process.versions?.node||process.versions?.bun)&&Q(Ee).then(e=>{x=e});const Se="https://chatgpt.com/backend-api",ve="https://api.openai.com/auth",ke=0,F=1e3,_e=6e4,U=1e4,Te=15e3,Z=new Set(["openai","openai-codex","opencode"]),xe=1009,Re=new Set(["completed","incomplete","failed","cancelled","queued","in_progress"]);function Ce(e){return/GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(e)}function Ae(e,r){return e===429&&Ce(r)?!1:e===429||e===500||e===502||e===503||e===504?!0:/rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(r)}function Oe(e){const r=e.get("retry-after-ms");if(r!==null){const o=Number(r);if(Number.isFinite(o))return Math.max(0,o)}const t=e.get("retry-after");if(!t)return;const s=Number(t);if(Number.isFinite(s))return Math.max(0,s*1e3);const n=Date.parse(t);if(!Number.isNaN(n))return Math.max(0,n-Date.now())}function We(e,r){const t=r?.maxRetryDelayMs??_e;return t>0?Math.min(e,t):e}function J(e,r){return new Promise((t,s)=>{if(r?.aborted){s(new Error("Request was aborted"));return}const n=setTimeout(t,e);r?.addEventListener("abort",()=>{clearTimeout(n),s(new Error("Request was aborted"))})})}function H(e){if(e!==void 0){if(!Number.isFinite(e)||e<0)throw new Error(`Invalid timeoutMs: ${String(e)}`);return Math.floor(e)}}function Ie(){const e=new AbortController;let r;const t=setTimeout(()=>{r=new Error(`Codex SSE response headers timed out after ${U}ms`),e.abort(r)},U);return{signal:e.signal,clear:()=>clearTimeout(t),error:()=>r}}const Le=(e,r,t)=>{const s=new ue;return(async()=>{const n={role:"assistant",content:[],api:"openai-codex-responses",provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const o=t?.apiKey||Y(e.provider)||"";if(!o)throw new Error(`No API key for provider: ${e.provider}`);const a=st(o);let l=De(e,r,t);const i=await t?.onPayload?.(l,e);i!==void 0&&(l=i);const c=t?.sessionId||nt(),p=at(e.headers,t?.headers,a,o,t?.sessionId),h=it(e.headers,t?.headers,a,o,c),E=JSON.stringify(l),d=H(t?.timeoutMs),g=H(t?.websocketConnectTimeoutMs),m=t?.transport||"auto",y=m!=="sse"&&se(t?.sessionId);if(y&&K(t?.sessionId),m!=="sse"&&!y){let b=!1;try{if(await rt(qe(e.baseUrl),l,h,n,s,e,()=>{b=!0},d,g,t),t?.signal?.aborted)throw new Error("Request was aborted");s.push({type:"done",reason:n.stopReason,message:n}),s.end();return}catch(w){if(t?.signal?.aborted||Be(w)||(we(n,be("provider_transport_failure",w,{configuredTransport:m,fallbackTransport:b?void 0:"sse",eventsEmitted:b,phase:b?"after_message_stream_start":"before_message_stream_start",requestBytes:new TextEncoder().encode(E).byteLength})),He(t?.sessionId,w),b))throw w;K(t?.sessionId)}}let u,f;const v=t?.maxRetries??ke;for(let b=0;b<=v;b++){if(t?.signal?.aborted)throw new Error("Request was aborted");try{const w=Ie(),_=ge([t?.signal,w.signal]);try{u=await fetch(re(e.baseUrl),{method:"POST",headers:p,body:E,signal:_.signal})}catch(T){const C=w.error();throw C&&!t?.signal?.aborted?C:T}finally{_.cleanup(),w.clear()}if(await t?.onResponse?.({status:u.status,headers:z(u.headers)},e),u.ok)break;const B=await u.text();if(b<v&&Ae(u.status,B)){const T=Oe(u.headers),C=T===void 0?F*2**b:u.status===429?We(T,t):T;await J(C,t?.signal);continue}const ce=new Response(B,{status:u.status,statusText:u.statusText}),P=await ot(ce);throw new Error(P.friendlyMessage||P.message)}catch(w){if(w instanceof Error&&(w.name==="AbortError"||w.message==="Request was aborted"))throw new Error("Request was aborted");if(f=w instanceof Error?w:new Error(String(w)),b<v&&!f.message.includes("usage limit")){const _=F*2**b;await J(_,t?.signal);continue}throw f}}if(!u?.ok)throw f??new Error("Failed after retries");if(!u.body)throw new Error("No response body");if(s.push({type:"start",partial:n}),await Ne(u,n,s,e,t),t?.signal?.aborted)throw new Error("Request was aborted");s.push({type:"done",reason:n.stopReason,message:n}),s.end()}catch(o){for(const a of n.content)delete a.partialJson;n.stopReason=t?.signal?.aborted?"aborted":"error",n.errorMessage=o instanceof Error?o.message:String(o),s.push({type:"error",reason:n.stopReason,error:n}),s.end()}})(),s},bt=(e,r,t)=>{const s=t?.apiKey||Y(e.provider);if(!s)throw new Error(`No API key for provider: ${e.provider}`);const n=pe(e,t,s),o=t?.reasoning?le(e,t.reasoning):void 0;return Le(e,r,{...n,reasoningEffort:o==="off"?void 0:o})};function De(e,r,t){const s=V(e,r,Z,{includeSystemPrompt:!1}),n={model:e.id,store:!1,stream:!0,instructions:r.systemPrompt||"You are a helpful assistant.",input:s,text:{verbosity:t?.textVerbosity||"low"},include:["reasoning.encrypted_content"],prompt_cache_key:de(t?.sessionId),tool_choice:"auto",parallel_tool_calls:!0};if(t?.temperature!==void 0&&(n.temperature=t.temperature),t?.serviceTier!==void 0&&(n.service_tier=t.serviceTier),r.tools&&r.tools.length>0&&(n.tools=fe(r.tools,{strict:null})),t?.reasoningEffort!==void 0){const o=t.reasoningEffort==="none"?e.thinkingLevelMap?.off??"none":e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort;o!==null&&(n.reasoning={effort:o,summary:t.reasoningSummary??"auto"})}return n}function Me(e,r){switch(r){case"flex":return .5;case"priority":return e.id==="gpt-5.5"?2.5:2;default:return 1}}function ee(e,r,t){const s=Me(t,r);s!==1&&(e.cost.input*=s,e.cost.output*=s,e.cost.cacheRead*=s,e.cost.cacheWrite*=s,e.cost.total=e.cost.input+e.cost.output+e.cost.cacheRead+e.cost.cacheWrite)}function te(e,r){return e==="default"&&(r==="flex"||r==="priority")?r:e??r}function re(e){const t=(e&&e.trim().length>0?e:Se).replace(/\/+$/,"");return t.endsWith("/codex/responses")?t:t.endsWith("/codex")?`${t}/responses`:`${t}/codex/responses`}function qe(e){const r=new URL(re(e));return r.protocol==="https:"&&(r.protocol="wss:"),r.protocol==="http:"&&(r.protocol="ws:"),r.toString()}async function Ne(e,r,t,s,n){await G(oe($e(e)),r,t,s,{serviceTier:n?.serviceTier,resolveServiceTier:te,applyServiceTierPricing:(o,a)=>ee(o,a,s)})}class M extends Error{code;payload;constructor(r,t){super(r),this.name="CodexApiError",this.code=t?.code,this.payload=t?.payload,this.cause=t?.cause}}class q extends Error{payload;constructor(r,t){super(r),this.name="CodexProtocolError",this.payload=t?.payload,this.cause=t?.cause}}function Be(e){return e instanceof M||e instanceof q}async function*oe(e){for await(const r of e){const t=typeof r.type=="string"?r.type:void 0;if(t){if(t==="error"){const s=r.code||"",n=r.message||"";throw new M(`Codex error: ${n||s||JSON.stringify(r)}`,{code:s||void 0,payload:r})}if(t==="response.failed"){const s=r.response,n=s?.error?.code,o=s?.error?.message;throw new M(o||"Codex response failed",{code:n,payload:r})}if(t==="response.done"||t==="response.completed"||t==="response.incomplete"){const s=r.response,n=s&&{...s,status:Pe(s.status)};yield{...r,type:"response.completed",response:n};return}yield r}}}function Pe(e){if(typeof e=="string")return Re.has(e)?e:void 0}async function*$e(e){if(!e.body)return;const r=e.body.getReader(),t=new TextDecoder;let s="";try{for(;;){const{done:n,value:o}=await r.read();if(n)break;s+=t.decode(o,{stream:!0});let a=s.indexOf(`
|
| 2 |
-
|
| 3 |
-
`);for(;a!==-1;){const l=s.slice(0,a);s=s.slice(a+2);const i=l.split(`
|
| 4 |
-
`).filter(c=>c.startsWith("data:")).map(c=>c.slice(5).trim());if(i.length>0){const c=i.join(`
|
| 5 |
-
`).trim();if(c&&c!=="[DONE]")try{yield JSON.parse(c)}catch(p){throw new q(`Invalid Codex SSE JSON: ${L(p)}`,{cause:p,payload:c})}}a=s.indexOf(`
|
| 6 |
-
|
| 7 |
-
`)}}}finally{try{await r.cancel()}catch{}try{r.releaseLock()}catch{}}}const Fe="responses_websockets=2026-02-06",Ue=300*1e3,S=new Map,R=new Map,I=new Set;function N(e){let r=R.get(e);return r||(r={requests:0,connectionsCreated:0,connectionsReused:0,cachedContextRequests:0,storeTrueRequests:0,fullContextRequests:0,deltaRequests:0,lastInputItems:0,websocketFailures:0,sseFallbacks:0},R.set(e,r)),r}function wt(e){const r=R.get(e);return r?{...r}:void 0}function gt(e){if(e){R.delete(e),I.delete(e);return}R.clear(),I.clear()}function Je(e){const r=t=>{t.idleTimer&&clearTimeout(t.idleTimer),k(t.socket,1e3,"debug_close")};if(e){const t=S.get(e);t&&r(t),S.delete(e);return}for(const t of S.values())r(t);S.clear()}me(Je);function se(e){return e?I.has(e):!1}function K(e){if(!e)return;const r=N(e);r.sseFallbacks++,r.websocketFallbackActive=se(e)}function He(e,r){if(!e)return;I.add(e);const t=N(e);t.websocketFailures++,t.lastWebSocketError=L(r),t.websocketFallbackActive=!0}let O=null;async function Ke(){if(O)return O;if(process?.versions?.bun&&(A.HTTP_PROXY||A.HTTPS_PROXY||A.http_proxy||A.https_proxy)){const t=(await Q("proxy-from-env")).getProxyForUrl;return O=class extends WebSocket{constructor(s,n){let o={};Array.isArray(n)||typeof n=="string"?o={protocols:n}:o={...n};const a=t(s.toString().replace(/^wss:/,"https:").replace(/^ws:/,"http:"));super(s,{...o,...a?{proxy:a}:{}})}},O}const e=globalThis.WebSocket;return typeof e!="function"?null:e}class je extends Error{code;reason;wasClean;constructor(r,t){super(r),this.name="WebSocketCloseError",this.code=t?.code,this.reason=t?.reason,this.wasClean=t?.wasClean}}function Xe(e){const r=e.readyState;return typeof r=="number"?r:void 0}function W(e){const r=Xe(e);return r===void 0||r===1}function k(e,r=1e3,t="done"){try{e.close(r,t)}catch{}}function j(e,r){r.idleTimer&&clearTimeout(r.idleTimer),r.idleTimer=setTimeout(()=>{r.busy||(k(r.socket,1e3,"idle_timeout"),S.delete(e))},Ue)}async function D(e,r,t,s=Te){const n=await Ke();if(!n)throw new Error("WebSocket transport is not available in this runtime");const o=z(r);return delete o["OpenAI-Beta"],new Promise((a,l)=>{let i=!1,c,p;try{p=new n(e,{headers:o})}catch(u){l(u instanceof Error?u:new Error(String(u)));return}const h=()=>{c&&(clearTimeout(c),c=void 0),p.removeEventListener("open",d),p.removeEventListener("error",g),p.removeEventListener("close",m),t?.removeEventListener("abort",y)},E=(u,f)=>{i||(i=!0,h(),f&&k(p,1e3,f),l(u))},d=()=>{i||(i=!0,h(),a(p))},g=u=>{E(ne(u))},m=u=>{E(ae(u))},y=()=>{E(new Error("Request was aborted"),"aborted")};p.addEventListener("open",d),p.addEventListener("error",g),p.addEventListener("close",m),t?.addEventListener("abort",y),s>0&&(c=setTimeout(()=>{E(new Error(`WebSocket connect timeout after ${s}ms`),"connect_timeout")},s)),t?.aborted&&y()})}async function Ye(e,r,t,s,n){if(!t){const i=await D(e,r,s,n);return{socket:i,reused:!1,release:()=>k(i)}}const o=S.get(t);if(o){if(o.idleTimer&&(clearTimeout(o.idleTimer),o.idleTimer=void 0),!o.busy&&W(o.socket))return o.busy=!0,{socket:o.socket,entry:o,reused:!0,release:({keep:i}={})=>{if(!i||!W(o.socket)){k(o.socket),S.delete(t);return}o.busy=!1,j(t,o)}};if(o.busy){const i=await D(e,r,s,n);return{socket:i,reused:!1,release:()=>{k(i)}}}W(o.socket)||(k(o.socket),S.delete(t))}const a=await D(e,r,s,n),l={socket:a,busy:!0};return S.set(t,l),{socket:a,entry:l,reused:!1,release:({keep:i}={})=>{if(!i||!W(l.socket)){k(l.socket),l.idleTimer&&clearTimeout(l.idleTimer),S.get(t)===l&&S.delete(t);return}l.busy=!1,j(t,l)}}}function ne(e){if(e&&typeof e=="object"){const r="message"in e?e.message:void 0;if(typeof r=="string"&&r.length>0)return new Error(r);const t="error"in e?e.error:void 0;if(t instanceof Error&&t.message.length>0)return t;if(t&&typeof t=="object"&&"message"in t){const s=t.message;if(typeof s=="string"&&s.length>0)return new Error(s)}}return new Error("WebSocket error")}function ae(e){if(e&&typeof e=="object"){const r="code"in e?e.code:void 0,t="reason"in e?e.reason:void 0,s="wasClean"in e?e.wasClean:void 0,n=typeof r=="number"?` ${r}`:"";let o=typeof t=="string"&&t.length>0?` ${t}`:"";return!o&&r===xe&&(o=" message too big"),new je(`WebSocket closed${n}${o}`.trim(),{code:typeof r=="number"?r:void 0,reason:typeof t=="string"&&t.length>0?t:void 0,wasClean:typeof s=="boolean"?s:void 0})}return new Error("WebSocket closed")}async function ze(e){if(typeof e=="string")return e;if(e instanceof ArrayBuffer)return new TextDecoder().decode(new Uint8Array(e));if(ArrayBuffer.isView(e)){const r=e;return new TextDecoder().decode(new Uint8Array(r.buffer,r.byteOffset,r.byteLength))}if(e&&typeof e=="object"&&"arrayBuffer"in e){const t=await e.arrayBuffer();return new TextDecoder().decode(new Uint8Array(t))}return null}async function*Ve(e,r,t){const s=[];let n=null,o=!1,a=null,l=!1;const i=()=>{if(!n)return;const d=n;n=null,d()},c=d=>{(async()=>{let g=null;try{if(!d||typeof d!="object"||!("data"in d)||(g=await ze(d.data),!g))return;const m=JSON.parse(g),y=typeof m.type=="string"?m.type:"";(y==="response.completed"||y==="response.done"||y==="response.incomplete")&&(l=!0,o=!0),s.push(m),i()}catch(m){a=new q(`Invalid Codex WebSocket JSON: ${L(m)}`,{cause:m,payload:g}),o=!0,i()}})()},p=d=>{a=ne(d),o=!0,i()},h=d=>{if(l){o=!0,i();return}a||(a=ae(d)),o=!0,i()},E=()=>{a=new Error("Request was aborted"),o=!0,i()};e.addEventListener("message",c),e.addEventListener("error",p),e.addEventListener("close",h),r?.addEventListener("abort",E);try{for(;;){if(r?.aborted)throw new Error("Request was aborted");if(s.length>0){yield s.shift();continue}if(o)break;let d;await new Promise((g,m)=>{n=g,t!==void 0&&t>0&&(d=setTimeout(()=>{const y=new Error(`WebSocket idle timeout after ${t}ms`);a=y,o=!0,n=null,k(e,1e3,"idle_timeout"),m(y)},t))}).finally(()=>{d&&clearTimeout(d)})}if(a)throw a;if(!l)throw new Error("WebSocket stream closed before response.completed")}finally{e.removeEventListener("message",c),e.removeEventListener("error",p),e.removeEventListener("close",h),r?.removeEventListener("abort",E)}}function X(e){const{input:r,previous_response_id:t,...s}=e;return s}function Ge(e,r){return JSON.stringify(e??[])===JSON.stringify(r??[])}function Qe(e,r){return JSON.stringify(X(e))===JSON.stringify(X(r))}function Ze(e,r){if(!Qe(e,r.lastRequestBody))return;const t=e.input??[],s=[...r.lastRequestBody.input??[],...r.lastResponseItems];if(t.length<s.length)return;const n=t.slice(0,s.length);if(Ge(n,s))return t.slice(s.length)}function et(e,r){const t=e.continuation;if(!t)return r;const s=Ze(r,t);return!s||!t.lastResponseId?(e.continuation=void 0,r):{...r,previous_response_id:t.lastResponseId,input:s}}async function*tt(e,r,t,s){let n=!1;for await(const o of e)n||(n=!0,s(),t.push({type:"start",partial:r})),yield o}async function rt(e,r,t,s,n,o,a,l,i,c){const{socket:p,entry:h,reused:E,release:d}=await Ye(e,t,c?.sessionId,c?.signal,i);let g=!0;const m=c?.transport==="websocket-cached"||c?.transport==="auto",y=r,u=m&&h?et(h,y):y,f=c?.sessionId?N(c.sessionId):void 0;f&&(f.requests++,E?f.connectionsReused++:f.connectionsCreated++,m&&f.cachedContextRequests++,u.store===!0&&f.storeTrueRequests++,f.lastInputItems=u.input?.length??0,u.previous_response_id?(f.deltaRequests++,f.lastDeltaInputItems=u.input?.length??0,f.lastPreviousResponseId=u.previous_response_id):(f.fullContextRequests++,f.lastDeltaInputItems=void 0,f.lastPreviousResponseId=void 0));try{if(p.send(JSON.stringify({type:"response.create",...u})),await G(tt(oe(Ve(p,c?.signal,l)),s,n,a),s,n,o,{serviceTier:c?.serviceTier,resolveServiceTier:te,applyServiceTierPricing:(v,b)=>ee(v,b,o)}),c?.signal?.aborted)g=!1;else if(m&&h&&s.responseId){const v=V(o,{messages:[s]},Z,{includeSystemPrompt:!1}).filter(b=>b.type!=="function_call_output");h.continuation={lastRequestBody:y,lastResponseId:s.responseId,lastResponseItems:v}}}catch(v){throw h&&(h.continuation=void 0),g=!1,v}finally{d({keep:g})}}async function ot(e){const r=await e.text();let t=r||e.statusText||"Request failed",s;try{const o=JSON.parse(r)?.error;if(o){const a=o.code||o.type||"";if(/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(a)||e.status===429){const l=o.plan_type?` (${o.plan_type.toLowerCase()} plan)`:"",i=o.resets_at?Math.max(0,Math.round((o.resets_at*1e3-Date.now())/6e4)):void 0,c=i!==void 0?` Try again in ~${i} min.`:"";s=`You have hit your ChatGPT usage limit${l}.${c}`.trim()}t=o.message||s||t}}catch{}return{message:t,friendlyMessage:s}}function st(e){try{const r=e.split(".");if(r.length!==3)throw new Error("Invalid token");const s=JSON.parse(atob(r[1]))?.[ve]?.chatgpt_account_id;if(!s)throw new Error("No account ID in token");return s}catch{throw new Error("Failed to extract accountId from token")}}function nt(){return typeof globalThis.crypto?.randomUUID=="function"?globalThis.crypto.randomUUID():`codex_${Date.now()}_${Math.random().toString(36).slice(2,10)}`}function ie(e,r,t,s){const n=new Headers(e);for(const[a,l]of Object.entries(r||{}))n.set(a,l);n.set("Authorization",`Bearer ${s}`),n.set("chatgpt-account-id",t),n.set("originator","pi");const o=x?`pi (${x.platform()} ${x.release()}; ${x.arch()})`:"pi (browser)";return n.set("User-Agent",o),n}function at(e,r,t,s,n){const o=ie(e,r,t,s);return o.set("OpenAI-Beta","responses=experimental"),o.set("accept","text/event-stream"),o.set("content-type","application/json"),n&&(o.set("session-id",n),o.set("x-client-request-id",n)),o}function it(e,r,t,s,n){const o=ie(e,r,t,s);return o.delete("accept"),o.delete("content-type"),o.delete("OpenAI-Beta"),o.delete("openai-beta"),o.set("OpenAI-Beta",Fe),o.set("x-client-request-id",n),o.set("session-id",n),o}export{Je as closeOpenAICodexWebSocketSessions,wt as getOpenAICodexWebSocketDebugStats,gt as resetOpenAICodexWebSocketDebugStats,Le as streamOpenAICodexResponses,bt as streamSimpleOpenAICodexResponses};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/openai-completions-CTz74GS-.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
import{O as G}from"./client-Sq9UPDIz.js";import{A as $,g as L,a as W,c as K}from"./index-CB_zB4U3.js";import{h as J}from"./headers-CgnjaPPL.js";import{a as P}from"./json-parse-cUk3uCki.js";import{s as C}from"./sanitize-unicode-B62XMGgN.js";import{h as Y,b as V,i as Z,r as Q}from"./github-copilot-headers-CVWOSrQr.js";import{c as X}from"./openai-prompt-cache-h_V5PL7P.js";import{t as ee,b as te}from"./transform-messages-DSNYjDRn.js";var b={};function ne(e){for(const n of e)if(n.role==="toolResult"||n.role==="assistant"&&n.content.some(t=>t.type==="toolCall"))return!0;return!1}function F(e){return e.type==="text"}function se(e){return e.type==="thinking"}function oe(e){return e.type==="toolCall"}function re(e){return e.type==="image"}function N(e){return e||(typeof process<"u"&&b.PI_CACHE_RETENTION==="long"?"long":"short")}const ie=(e,n,t)=>{const s=new $;return(async()=>{const r={role:"assistant",content:[],api:e.api,provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const c=t?.apiKey||L(e.provider)||"",u=q(e),o=N(t?.cacheRetention),i=o==="none"?void 0:t?.sessionId,p=ae(e,n,c,t?.headers,i,u);let h=le(e,n,t,u,o);const k=await t?.onPayload?.(h,e);k!==void 0&&(h=k);const R={...t?.signal?{signal:t.signal}:{},...t?.timeoutMs!==void 0?{timeout:t.timeoutMs}:{},maxRetries:t?.maxRetries??0},{data:T,response:_}=await p.chat.completions.create(h,R).withResponse();await t?.onResponse?.({status:_.status,headers:J(_.headers)},e),s.push({type:"start",partial:r});let v=null,l=null,y=!1;const w=new Map,I=new Map,E=r.content,A=a=>E.indexOf(a),D=a=>{const f=A(a);f!==-1&&(a.type==="text"?s.push({type:"text_end",contentIndex:f,content:a.text,partial:r}):a.type==="thinking"?s.push({type:"thinking_end",contentIndex:f,content:a.thinking,partial:r}):a.type==="toolCall"&&(a.arguments=P(a.partialArgs),delete a.partialArgs,delete a.streamIndex,s.push({type:"toolcall_end",contentIndex:f,toolCall:a,partial:r})))},U=()=>(v||(v={type:"text",text:""},E.push(v),s.push({type:"text_start",contentIndex:A(v),partial:r})),v),H=a=>(l||(l={type:"thinking",thinking:"",thinkingSignature:a},E.push(l),s.push({type:"thinking_start",contentIndex:A(l),partial:r})),l),j=a=>{const f=typeof a.index=="number"?a.index:void 0;let d=f!==void 0?w.get(f):void 0;return!d&&a.id&&(d=I.get(a.id)),d||(d={type:"toolCall",id:a.id||"",name:a.function?.name||"",arguments:{},partialArgs:"",streamIndex:f},f!==void 0&&w.set(f,d),a.id&&I.set(a.id,d),E.push(d),s.push({type:"toolcall_start",contentIndex:A(d),partial:r})),f!==void 0&&d.streamIndex===void 0&&(d.streamIndex=f,w.set(f,d)),a.id&&I.set(a.id,d),d};for await(const a of T){if(!a||typeof a!="object")continue;r.responseId||=a.id,typeof a.model=="string"&&a.model.length>0&&a.model!==e.id&&(r.responseModel||=a.model),a.usage&&(r.usage=B(a.usage,e));const f=Array.isArray(a.choices)?a.choices[0]:void 0;if(f){if(!a.usage&&f.usage&&(r.usage=B(f.usage,e)),f.finish_reason){const d=ye(f.finish_reason);r.stopReason=d.stopReason,d.errorMessage&&(r.errorMessage=d.errorMessage),y=!0}if(f.delta){if(f.delta.content!==null&&f.delta.content!==void 0&&f.delta.content.length>0){const g=U();g.text+=f.delta.content,s.push({type:"text_delta",contentIndex:A(g),delta:f.delta.content,partial:r})}const d=["reasoning_content","reasoning","reasoning_text"],O=f.delta;let M=null;for(const g of d){const m=O[g];if(typeof m=="string"&&m.length>0){M=g;break}}if(M){const g=O[M];if(typeof g=="string"&&g.length>0){const m=e.provider==="opencode-go"&&M==="reasoning"?"reasoning_content":M,x=H(m);x.thinking+=g,s.push({type:"thinking_delta",contentIndex:A(x),delta:g,partial:r})}}if(f?.delta?.tool_calls)for(const g of f.delta.tool_calls){const m=j(g);!m.id&&g.id&&(m.id=g.id,I.set(g.id,m)),!m.name&&g.function?.name&&(m.name=g.function.name);let x="";g.function?.arguments&&(x=g.function.arguments,m.partialArgs=(m.partialArgs??"")+g.function.arguments,m.arguments=P(m.partialArgs)),s.push({type:"toolcall_delta",contentIndex:A(m),delta:x,partial:r})}const S=f.delta.reasoning_details;if(S&&Array.isArray(S)){for(const g of S)if(g.type==="reasoning.encrypted"&&g.id&&g.data){const m=r.content.find(x=>x.type==="toolCall"&&x.id===g.id);m&&(m.thoughtSignature=JSON.stringify(g))}}}}}for(const a of E)D(a);if(t?.signal?.aborted)throw new Error("Request was aborted");if(r.stopReason==="aborted")throw new Error("Request was aborted");if(r.stopReason==="error")throw new Error(r.errorMessage||"Provider returned an error stop reason");if(!y)throw new Error("Stream ended without finish_reason");s.push({type:"done",reason:r.stopReason,message:r}),s.end()}catch(c){for(const o of r.content)delete o.index,delete o.partialArgs,delete o.streamIndex;r.stopReason=t?.signal?.aborted?"aborted":"error",r.errorMessage=c instanceof Error?c.message:JSON.stringify(c);const u=c?.error?.metadata?.raw;u&&(r.errorMessage+=`
|
| 2 |
-
${u}`),s.push({type:"error",reason:r.stopReason,error:r}),s.end()}})(),s},Ie=(e,n,t)=>{const s=t?.apiKey||L(e.provider);if(!s)throw new Error(`No API key for provider: ${e.provider}`);const r=te(e,t,s),c=t?.reasoning?W(e,t.reasoning):void 0,u=c==="off"?void 0:c,o=t?.toolChoice;return ie(e,n,{...r,reasoningEffort:u,toolChoice:o})};function ae(e,n,t,s,r,c=q(e)){if(!t){if(!b.OPENAI_API_KEY)throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.");t=b.OPENAI_API_KEY}const u={...e.headers};if(e.provider==="github-copilot"){const i=Y(n.messages),p=V({messages:n.messages,hasImages:i});Object.assign(u,p)}r&&c.sendSessionAffinityHeaders&&(u.session_id=r,u["x-client-request-id"]=r,u["x-session-affinity"]=r),s&&Object.assign(u,s);const o=e.provider==="cloudflare-ai-gateway"?{...u,Authorization:u.Authorization??null,"cf-aig-authorization":`Bearer ${t}`}:u;return new G({apiKey:t,baseURL:Z(e.provider)?Q(e):e.baseUrl,dangerouslyAllowBrowser:!0,defaultHeaders:o})}function le(e,n,t,s=q(e),r=N(t?.cacheRetention)){const c=me(e,n,s),u=ce(s,r),o={model:e.id,messages:c,stream:!0,prompt_cache_key:e.baseUrl.includes("api.openai.com")&&r!=="none"||r==="long"&&s.supportsLongCacheRetention?X(t?.sessionId):void 0,prompt_cache_retention:r==="long"&&s.supportsLongCacheRetention?"24h":void 0};if(s.supportsUsageInStreaming!==!1&&(o.stream_options={include_usage:!0}),s.supportsStore&&(o.store=!1),t?.maxTokens&&(s.maxTokensField==="max_tokens"?o.max_tokens=t.maxTokens:o.max_completion_tokens=t.maxTokens),t?.temperature!==void 0&&(o.temperature=t.temperature),n.tools&&n.tools.length>0?(o.tools=ke(n.tools,s),s.zaiToolStream&&(o.tool_stream=!0)):ne(n.messages)&&(o.tools=[]),u&&ue(c,o.tools,u),t?.toolChoice&&(o.tool_choice=t.toolChoice),s.thinkingFormat==="zai"&&e.reasoning)o.enable_thinking=!!t?.reasoningEffort;else if(s.thinkingFormat==="qwen"&&e.reasoning)o.enable_thinking=!!t?.reasoningEffort;else if(s.thinkingFormat==="qwen-chat-template"&&e.reasoning)o.chat_template_kwargs={enable_thinking:!!t?.reasoningEffort,preserve_thinking:!0};else if(s.thinkingFormat==="deepseek"&&e.reasoning)o.thinking={type:t?.reasoningEffort?"enabled":"disabled"},t?.reasoningEffort&&(o.reasoning_effort=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort);else if(s.thinkingFormat==="openrouter"&&e.reasoning){const i=o;t?.reasoningEffort?i.reasoning={effort:e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort}:e.thinkingLevelMap?.off!==null&&(i.reasoning={effort:e.thinkingLevelMap?.off??"none"})}else if(s.thinkingFormat==="together"&&e.reasoning){const i=o;i.reasoning={enabled:!!t?.reasoningEffort},t?.reasoningEffort&&s.supportsReasoningEffort&&(i.reasoning_effort=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort)}else if(s.thinkingFormat==="string-thinking"&&e.reasoning){const i=o;t?.reasoningEffort?i.thinking=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort:e.thinkingLevelMap?.off!==null&&(i.thinking=e.thinkingLevelMap?.off??"none")}else if(t?.reasoningEffort&&e.reasoning&&s.supportsReasoningEffort)o.reasoning_effort=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort;else if(!t?.reasoningEffort&&e.reasoning&&s.supportsReasoningEffort){const i=e.thinkingLevelMap?.off;typeof i=="string"&&(o.reasoning_effort=i)}if(e.baseUrl.includes("openrouter.ai")&&e.compat?.openRouterRouting&&(o.provider=e.compat.openRouterRouting),e.baseUrl.includes("ai-gateway.vercel.sh")&&e.compat?.vercelGatewayRouting){const i=e.compat.vercelGatewayRouting;if(i.only||i.order){const p={};i.only&&(p.only=i.only),i.order&&(p.order=i.order),o.providerOptions={gateway:p}}}return o}function ce(e,n){if(e.cacheControlFormat!=="anthropic"||n==="none")return;const t=n==="long"&&e.supportsLongCacheRetention?"1h":void 0;return{type:"ephemeral",...t?{ttl:t}:{}}}function ue(e,n,t){fe(e,t),ge(n,t),pe(e,t)}function fe(e,n){for(const t of e)if(t.role==="system"||t.role==="developer"){he(t,n);return}}function pe(e,n){for(let t=e.length-1;t>=0;t--){const s=e[t];if((s.role==="user"||s.role==="assistant")&&de(s,n))return}}function ge(e,n){if(!e||e.length===0)return;const t=e[e.length-1];t.cache_control=n}function he(e,n){return z(e,n)}function de(e,n){return e.role==="user"||e.role==="assistant"?z(e,n):!1}function z(e,n){const t=e.content;if(typeof t=="string")return t.length===0?!1:(e.content=[{type:"text",text:t,cache_control:n}],!0);if(!Array.isArray(t))return!1;for(let s=t.length-1;s>=0;s--){const r=t[s];if(r?.type==="text"){const c=r;return c.cache_control=n,!0}}return!1}function me(e,n,t){const s=[],r=o=>{if(o.includes("|")){const[i]=o.split("|");return i.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,40)}return e.provider==="openai"&&o.length>40?o.slice(0,40):o},c=ee(n.messages,e,o=>r(o));if(n.systemPrompt){const i=e.reasoning&&t.supportsDeveloperRole?"developer":"system";s.push({role:i,content:C(n.systemPrompt)})}let u=null;for(let o=0;o<c.length;o++){const i=c[o];if(t.requiresAssistantAfterToolResult&&u==="toolResult"&&i.role==="user"&&s.push({role:"assistant",content:"I have processed the tool results."}),i.role==="user")if(typeof i.content=="string")s.push({role:"user",content:C(i.content)});else{const p=i.content.map(h=>h.type==="text"?{type:"text",text:C(h.text)}:{type:"image_url",image_url:{url:`data:${h.mimeType};base64,${h.data}`}});if(p.length===0)continue;s.push({role:"user",content:p})}else if(i.role==="assistant"){const p={role:"assistant",content:t.requiresAssistantAfterToolResult?"":null},h=i.content.filter(F).filter(l=>l.text.trim().length>0).map(l=>({type:"text",text:C(l.text)})),k=h.map(l=>l.text).join(""),R=i.content.filter(se).filter(l=>l.thinking.trim().length>0);if(R.length>0)if(t.requiresThinkingAsText){const l=R.map(y=>C(y.thinking)).join(`
|
| 3 |
-
|
| 4 |
-
`);p.content=[{type:"text",text:l},...h]}else{k.length>0&&(p.content=k);let l=R[0].thinkingSignature;e.provider==="opencode-go"&&l==="reasoning"&&(l="reasoning_content"),l&&l.length>0&&(p[l]=R.map(y=>y.thinking).join(`
|
| 5 |
-
`))}else k.length>0&&(p.content=k);const T=i.content.filter(oe);if(T.length>0){p.tool_calls=T.map(y=>({id:y.id,type:"function",function:{name:y.name,arguments:JSON.stringify(y.arguments)}}));const l=T.filter(y=>y.thoughtSignature).map(y=>{try{return JSON.parse(y.thoughtSignature)}catch{return null}}).filter(Boolean);l.length>0&&(p.reasoning_details=l)}t.requiresReasoningContentOnAssistantMessages&&e.reasoning&&p.reasoning_content===void 0&&(p.reasoning_content="");const _=p.content;if(!(_!=null&&_.length>0)&&!p.tool_calls)continue;s.push(p)}else if(i.role==="toolResult"){const p=[];let h=o;for(;h<c.length&&c[h].role==="toolResult";h++){const k=c[h],R=k.content.filter(F).map(l=>l.text).join(`
|
| 6 |
-
`),T=k.content.some(l=>l.type==="image"),_=R.length>0,v={role:"tool",content:C(_?R:"(see attached image)"),tool_call_id:k.toolCallId};if(t.requiresToolResultName&&k.toolName&&(v.name=k.toolName),s.push(v),T&&e.input.includes("image"))for(const l of k.content)re(l)&&p.push({type:"image_url",image_url:{url:`data:${l.mimeType};base64,${l.data}`}})}o=h-1,p.length>0?(t.requiresAssistantAfterToolResult&&s.push({role:"assistant",content:"I have processed the tool results."}),s.push({role:"user",content:[{type:"text",text:"Attached image(s) from tool result:"},...p]}),u="user"):u="toolResult";continue}u=i.role}return s}function ke(e,n){return e.map(t=>({type:"function",function:{name:t.name,description:t.description,parameters:t.parameters,...n.supportsStrictMode!==!1&&{strict:!1}}}))}function B(e,n){const t=e.prompt_tokens||0,s=e.prompt_tokens_details?.cached_tokens??e.prompt_cache_hit_tokens??0,r=e.prompt_tokens_details?.cache_write_tokens||0,c=Math.max(0,t-s-r),u=e.completion_tokens||0,o={input:c,output:u,cacheRead:s,cacheWrite:r,totalTokens:c+u+s+r,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}};return K(n,o),o}function ye(e){if(e===null)return{stopReason:"stop"};switch(e){case"stop":case"end":return{stopReason:"stop"};case"length":return{stopReason:"length"};case"function_call":case"tool_calls":return{stopReason:"toolUse"};case"content_filter":return{stopReason:"error",errorMessage:"Provider finish_reason: content_filter"};case"network_error":return{stopReason:"error",errorMessage:"Provider finish_reason: network_error"};default:return{stopReason:"error",errorMessage:`Provider finish_reason: ${e}`}}}function Re(e){const n=e.provider,t=e.baseUrl,s=n==="zai"||t.includes("api.z.ai"),r=n==="together"||t.includes("api.together.ai")||t.includes("api.together.xyz"),c=n==="moonshotai"||n==="moonshotai-cn"||t.includes("api.moonshot."),u=n==="cloudflare-workers-ai"||t.includes("api.cloudflare.com"),o=n==="cloudflare-ai-gateway"||t.includes("gateway.ai.cloudflare.com"),i=n==="cerebras"||t.includes("cerebras.ai")||n==="xai"||t.includes("api.x.ai")||r||t.includes("chutes.ai")||t.includes("deepseek.com")||s||c||n==="opencode"||t.includes("opencode.ai")||u||o,p=t.includes("chutes.ai")||c||o||r,h=n==="xai"||t.includes("api.x.ai"),k=n==="deepseek"||t.includes("deepseek.com"),R=n==="openrouter"&&e.id.startsWith("anthropic/")?"anthropic":void 0;return{supportsStore:!i,supportsDeveloperRole:!i,supportsReasoningEffort:!h&&!s&&!c&&!r&&!o,supportsUsageInStreaming:!0,maxTokensField:p?"max_tokens":"max_completion_tokens",requiresToolResultName:!1,requiresAssistantAfterToolResult:!1,requiresThinkingAsText:!1,requiresReasoningContentOnAssistantMessages:k,thinkingFormat:k?"deepseek":s?"zai":r?"together":n==="openrouter"||t.includes("openrouter.ai")?"openrouter":"openai",openRouterRouting:{},vercelGatewayRouting:{},zaiToolStream:!1,supportsStrictMode:!c&&!r&&!o,cacheControlFormat:R,sendSessionAffinityHeaders:!1,supportsLongCacheRetention:!(r||u||o)}}function q(e){const n=Re(e);return e.compat?{supportsStore:e.compat.supportsStore??n.supportsStore,supportsDeveloperRole:e.compat.supportsDeveloperRole??n.supportsDeveloperRole,supportsReasoningEffort:e.compat.supportsReasoningEffort??n.supportsReasoningEffort,supportsUsageInStreaming:e.compat.supportsUsageInStreaming??n.supportsUsageInStreaming,maxTokensField:e.compat.maxTokensField??n.maxTokensField,requiresToolResultName:e.compat.requiresToolResultName??n.requiresToolResultName,requiresAssistantAfterToolResult:e.compat.requiresAssistantAfterToolResult??n.requiresAssistantAfterToolResult,requiresThinkingAsText:e.compat.requiresThinkingAsText??n.requiresThinkingAsText,requiresReasoningContentOnAssistantMessages:e.compat.requiresReasoningContentOnAssistantMessages??n.requiresReasoningContentOnAssistantMessages,thinkingFormat:e.compat.thinkingFormat??n.thinkingFormat,openRouterRouting:e.compat.openRouterRouting??{},vercelGatewayRouting:e.compat.vercelGatewayRouting??n.vercelGatewayRouting,zaiToolStream:e.compat.zaiToolStream??n.zaiToolStream,supportsStrictMode:e.compat.supportsStrictMode??n.supportsStrictMode,cacheControlFormat:e.compat.cacheControlFormat??n.cacheControlFormat,sendSessionAffinityHeaders:e.compat.sendSessionAffinityHeaders??n.sendSessionAffinityHeaders,supportsLongCacheRetention:e.compat.supportsLongCacheRetention??n.supportsLongCacheRetention}:n}export{me as convertMessages,ie as streamOpenAICompletions,Ie as streamSimpleOpenAICompletions};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/openai-prompt-cache-h_V5PL7P.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
function r(_){if(_===void 0)return;const n=Array.from(_);return n.length<=64?_:n.slice(0,64).join("")}export{r as c};
|
|
|
|
|
|
assets/openai-responses-CW5o7IM-.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{O as A}from"./client-Sq9UPDIz.js";import{A as I,g as h,a as E}from"./index-CB_zB4U3.js";import{h as w}from"./headers-CgnjaPPL.js";import{h as _,b as O,i as b,r as P}from"./github-copilot-headers-CVWOSrQr.js";import{c as S}from"./openai-prompt-cache-h_V5PL7P.js";import{p as C,c as T,a as k}from"./openai-responses-shared-DBUlz2NN.js";import{b as L}from"./transform-messages-DSNYjDRn.js";import"./hash-DMDecQcg.js";import"./json-parse-cUk3uCki.js";import"./sanitize-unicode-B62XMGgN.js";var u={};const M=new Set(["openai","openai-codex","opencode"]);function g(e){return e||(typeof process<"u"&&u.PI_CACHE_RETENTION==="long"?"long":"short")}function m(e){return{sendSessionIdHeader:e.compat?.sendSessionIdHeader??!0,supportsLongCacheRetention:e.compat?.supportsLongCacheRetention??!0}}function N(e,t){return t==="long"&&e.supportsLongCacheRetention?"24h":void 0}function x(e){if(e instanceof Error){const t=e.status,r=typeof t=="number"?t:void 0;return r!==void 0?`OpenAI API error (${r}): ${e.message}`:e.message}try{return JSON.stringify(e)}catch{return String(e)}}const H=(e,t,r)=>{const s=new I;return(async()=>{const a={role:"assistant",content:[],api:e.api,provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const o=r?.apiKey||h(e.provider)||"",i=g(r?.cacheRetention)==="none"?void 0:r?.sessionId,p=K(e,t,o,r?.headers,i);let c=q(e,t,r);const f=await r?.onPayload?.(c,e);f!==void 0&&(c=f);const l={...r?.signal?{signal:r.signal}:{},...r?.timeoutMs!==void 0?{timeout:r.timeoutMs}:{},maxRetries:r?.maxRetries??0},{data:v,response:d}=await p.responses.create(c,l).withResponse();if(await r?.onResponse?.({status:d.status,headers:w(d.headers)},e),s.push({type:"start",partial:a}),await C(v,a,s,e,{serviceTier:r?.serviceTier,applyServiceTierPricing:(R,y)=>W(R,y,e)}),r?.signal?.aborted)throw new Error("Request was aborted");if(a.stopReason==="aborted"||a.stopReason==="error")throw new Error("An unknown error occurred");s.push({type:"done",reason:a.stopReason,message:a}),s.end()}catch(o){for(const n of a.content)delete n.index,delete n.partialJson;a.stopReason=r?.signal?.aborted?"aborted":"error",a.errorMessage=x(o),s.push({type:"error",reason:a.stopReason,error:a}),s.end()}})(),s},Q=(e,t,r)=>{const s=r?.apiKey||h(e.provider);if(!s)throw new Error(`No API key for provider: ${e.provider}`);const a=L(e,r,s),o=r?.reasoning?E(e,r.reasoning):void 0;return H(e,t,{...a,reasoningEffort:o==="off"?void 0:o})};function K(e,t,r,s,a){if(!r){if(!u.OPENAI_API_KEY)throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.");r=u.OPENAI_API_KEY}const o=m(e),n={...e.headers};if(e.provider==="github-copilot"){const p=_(t.messages),c=O({messages:t.messages,hasImages:p});Object.assign(n,c)}a&&(o.sendSessionIdHeader&&(n.session_id=a),n["x-client-request-id"]=a),s&&Object.assign(n,s);const i=e.provider==="cloudflare-ai-gateway"?{...n,Authorization:n.Authorization??null,"cf-aig-authorization":`Bearer ${r}`}:n;return new A({apiKey:r,baseURL:b(e.provider)?P(e):e.baseUrl,dangerouslyAllowBrowser:!0,defaultHeaders:i})}function q(e,t,r){const s=T(e,t,M),a=g(r?.cacheRetention),o=m(e),n={model:e.id,input:s,stream:!0,prompt_cache_key:a==="none"?void 0:S(r?.sessionId),prompt_cache_retention:N(o,a),store:!1};if(r?.maxTokens&&(n.max_output_tokens=r?.maxTokens),r?.temperature!==void 0&&(n.temperature=r?.temperature),r?.serviceTier!==void 0&&(n.service_tier=r.serviceTier),t.tools&&t.tools.length>0&&(n.tools=k(t.tools)),e.reasoning)if(r?.reasoningEffort||r?.reasoningSummary){const i=r?.reasoningEffort?e.thinkingLevelMap?.[r.reasoningEffort]??r.reasoningEffort:"medium";n.reasoning={effort:i,summary:r?.reasoningSummary||"auto"},n.include=["reasoning.encrypted_content"]}else e.provider!=="github-copilot"&&e.thinkingLevelMap?.off!==null&&(n.reasoning={effort:e.thinkingLevelMap?.off??"none"});return n}function B(e,t){switch(t){case"flex":return .5;case"priority":return e.id==="gpt-5.5"?2.5:2;default:return 1}}function W(e,t,r){const s=B(r,t);s!==1&&(e.cost.input*=s,e.cost.output*=s,e.cost.cacheRead*=s,e.cost.cacheWrite*=s,e.cost.total=e.cost.input+e.cost.output+e.cost.cacheRead+e.cost.cacheWrite)}export{H as streamOpenAIResponses,Q as streamSimpleOpenAIResponses};
|
|
|
|
|
|
assets/openai-responses-shared-DBUlz2NN.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
| 1 |
-
import{c as $}from"./index-CB_zB4U3.js";import{s as S}from"./hash-DMDecQcg.js";import{a as k}from"./json-parse-cUk3uCki.js";import{s as x}from"./sanitize-unicode-B62XMGgN.js";import{t as T}from"./transform-messages-DSNYjDRn.js";function C(r,t){const l={v:1,id:r};return t&&(l.phase=t),JSON.stringify(l)}function J(r){if(r){if(r.startsWith("{"))try{const t=JSON.parse(r);if(t.v===1&&typeof t.id=="string")return t.phase==="commentary"||t.phase==="final_answer"?{id:t.id,phase:t.phase}:{id:t.id}}catch{}return{id:r}}}function W(r,t,l,h){const u=[],i=s=>{const o=s.replace(/[^a-zA-Z0-9_-]/g,"_");return(o.length>64?o.slice(0,64):o).replace(/_+$/,"")},n=s=>{const o=`fc_${S(s)}`;return o.length>64?o.slice(0,64):o},I=(s,o,c)=>{if(!l.has(r.provider)||!s.includes("|"))return i(s);const[_,y]=s.split("|"),f=i(_);let m=c.provider!==r.provider||c.api!==r.api?n(y):i(y);return m.startsWith("fc_")||(m=i(`fc_${m}`)),`${f}|${m}`},d=T(t.messages,r,I);if((h?.includeSystemPrompt??!0)&&t.systemPrompt){const s=r.reasoning?"developer":"system";u.push({role:s,content:x(t.systemPrompt)})}let e=0;for(const s of d){if(s.role==="user")if(typeof s.content=="string")u.push({role:"user",content:[{type:"input_text",text:x(s.content)}]});else{const o=s.content.map(c=>c.type==="text"?{type:"input_text",text:x(c.text)}:{type:"input_image",detail:"auto",image_url:`data:${c.mimeType};base64,${c.data}`});if(o.length===0)continue;u.push({role:"user",content:o})}else if(s.role==="assistant"){const o=[],c=s,_=c.model!==r.id&&c.provider===r.provider&&c.api===r.api;let y=0;for(const f of s.content)if(f.type==="thinking"){if(f.thinkingSignature){const p=JSON.parse(f.thinkingSignature);o.push(p)}}else if(f.type==="text"){const p=f,m=J(p.textSignature),v=y===0?`msg_pi_${e}`:`msg_pi_${e}_${y}`;y++;let g=m?.id;g?g.length>64&&(g=`msg_${S(g)}`):g=v,o.push({type:"message",role:"assistant",content:[{type:"output_text",text:x(p.text),annotations:[]}],status:"completed",id:g,phase:m?.phase})}else if(f.type==="toolCall"){const p=f,[m,v]=p.id.split("|");let g=v;_&&g?.startsWith("fc_")&&(g=void 0),o.push({type:"function_call",id:g,call_id:m,name:p.name,arguments:JSON.stringify(p.arguments)})}if(o.length===0)continue;u.push(...o)}else if(s.role==="toolResult"){const o=s.content.filter(p=>p.type==="text").map(p=>p.text).join(`
|
| 2 |
-
`),c=s.content.some(p=>p.type==="image"),_=o.length>0,[y]=s.toolCallId.split("|");let f;if(c&&r.input.includes("image")){const p=[];_&&p.push({type:"input_text",text:x(o)});for(const m of s.content)m.type==="image"&&p.push({type:"input_image",detail:"auto",image_url:`data:${m.mimeType};base64,${m.data}`});f=p}else f=x(_?o:"(see attached image)");u.push({type:"function_call_output",call_id:y,output:f})}e++}return u}function N(r,t){const l=t?.strict===void 0?!1:t.strict;return r.map(h=>({type:"function",name:h.name,description:h.description,parameters:h.parameters,strict:l}))}async function O(r,t,l,h,u){let i=null,n=null;const I=t.content,d=()=>I.length-1;for await(const a of r)if(a.type==="response.created")t.responseId=a.response.id;else if(a.type==="response.output_item.added"){const e=a.item;e.type==="reasoning"?(i=e,n={type:"thinking",thinking:""},t.content.push(n),l.push({type:"thinking_start",contentIndex:d(),partial:t})):e.type==="message"?(i=e,n={type:"text",text:""},t.content.push(n),l.push({type:"text_start",contentIndex:d(),partial:t})):e.type==="function_call"&&(i=e,n={type:"toolCall",id:`${e.call_id}|${e.id}`,name:e.name,arguments:{},partialJson:e.arguments||""},t.content.push(n),l.push({type:"toolcall_start",contentIndex:d(),partial:t}))}else if(a.type==="response.reasoning_summary_part.added")i&&i.type==="reasoning"&&(i.summary=i.summary||[],i.summary.push(a.part));else if(a.type==="response.reasoning_summary_text.delta"){if(i?.type==="reasoning"&&n?.type==="thinking"){i.summary=i.summary||[];const e=i.summary[i.summary.length-1];e&&(n.thinking+=a.delta,e.text+=a.delta,l.push({type:"thinking_delta",contentIndex:d(),delta:a.delta,partial:t}))}}else if(a.type==="response.reasoning_summary_part.done"){if(i?.type==="reasoning"&&n?.type==="thinking"){i.summary=i.summary||[];const e=i.summary[i.summary.length-1];e&&(n.thinking+=`
|
| 3 |
-
|
| 4 |
-
`,e.text+=`
|
| 5 |
-
|
| 6 |
-
`,l.push({type:"thinking_delta",contentIndex:d(),delta:`
|
| 7 |
-
|
| 8 |
-
`,partial:t}))}}else if(a.type==="response.reasoning_text.delta")i?.type==="reasoning"&&n?.type==="thinking"&&(n.thinking+=a.delta,l.push({type:"thinking_delta",contentIndex:d(),delta:a.delta,partial:t}));else if(a.type==="response.content_part.added")i?.type==="message"&&(i.content=i.content||[],(a.part.type==="output_text"||a.part.type==="refusal")&&i.content.push(a.part));else if(a.type==="response.output_text.delta"){if(i?.type==="message"&&n?.type==="text"){if(!i.content||i.content.length===0)continue;const e=i.content[i.content.length-1];e?.type==="output_text"&&(n.text+=a.delta,e.text+=a.delta,l.push({type:"text_delta",contentIndex:d(),delta:a.delta,partial:t}))}}else if(a.type==="response.refusal.delta"){if(i?.type==="message"&&n?.type==="text"){if(!i.content||i.content.length===0)continue;const e=i.content[i.content.length-1];e?.type==="refusal"&&(n.text+=a.delta,e.refusal+=a.delta,l.push({type:"text_delta",contentIndex:d(),delta:a.delta,partial:t}))}}else if(a.type==="response.function_call_arguments.delta")i?.type==="function_call"&&n?.type==="toolCall"&&(n.partialJson+=a.delta,n.arguments=k(n.partialJson),l.push({type:"toolcall_delta",contentIndex:d(),delta:a.delta,partial:t}));else if(a.type==="response.function_call_arguments.done"){if(i?.type==="function_call"&&n?.type==="toolCall"){const e=n.partialJson;if(n.partialJson=a.arguments,n.arguments=k(n.partialJson),a.arguments.startsWith(e)){const s=a.arguments.slice(e.length);s.length>0&&l.push({type:"toolcall_delta",contentIndex:d(),delta:s,partial:t})}}}else if(a.type==="response.output_item.done"){const e=a.item;if(e.type==="reasoning"&&n?.type==="thinking"){const s=e.summary?.map(c=>c.text).join(`
|
| 9 |
-
|
| 10 |
-
`)||"",o=e.content?.map(c=>c.text).join(`
|
| 11 |
-
|
| 12 |
-
`)||"";n.thinking=s||o||n.thinking,n.thinkingSignature=JSON.stringify(e),l.push({type:"thinking_end",contentIndex:d(),content:n.thinking,partial:t}),n=null}else if(e.type==="message"&&n?.type==="text")n.text=e.content.map(s=>s.type==="output_text"?s.text:s.refusal).join(""),n.textSignature=C(e.id,e.phase??void 0),l.push({type:"text_end",contentIndex:d(),content:n.text,partial:t}),n=null;else if(e.type==="function_call"){const s=n?.type==="toolCall"&&n.partialJson?k(n.partialJson):k(e.arguments||"{}");let o;n?.type==="toolCall"?(n.arguments=s,delete n.partialJson,o=n):o={type:"toolCall",id:`${e.call_id}|${e.id}`,name:e.name,arguments:s},n=null,l.push({type:"toolcall_end",contentIndex:d(),toolCall:o,partial:t})}}else if(a.type==="response.completed"){const e=a.response;if(e?.id&&(t.responseId=e.id),e?.usage){const s=e.usage.input_tokens_details?.cached_tokens||0;t.usage={input:(e.usage.input_tokens||0)-s,output:e.usage.output_tokens||0,cacheRead:s,cacheWrite:0,totalTokens:e.usage.total_tokens||0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}}}if($(h,t.usage),u?.applyServiceTierPricing){const s=u.resolveServiceTier?u.resolveServiceTier(e?.service_tier,u.serviceTier):e?.service_tier??u.serviceTier;u.applyServiceTierPricing(t.usage,s)}t.stopReason=P(e?.status),t.content.some(s=>s.type==="toolCall")&&t.stopReason==="stop"&&(t.stopReason="toolUse")}else{if(a.type==="error")throw new Error(`Error Code ${a.code}: ${a.message}`||"Unknown error");if(a.type==="response.failed"){const e=a.response?.error,s=a.response?.incomplete_details,o=e?`${e.code||"unknown"}: ${e.message||"no message"}`:s?.reason?`incomplete: ${s.reason}`:"Unknown error (no error details in response)";throw new Error(o)}}}function P(r){if(!r)return"stop";switch(r){case"completed":return"stop";case"incomplete":return"length";case"failed":case"cancelled":return"error";case"in_progress":case"queued":return"stop";default:{const t=r;throw new Error(`Unhandled stop reason: ${t}`)}}}export{N as a,W as c,O as p};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/openrouter-DE7amXV2.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{O as y}from"./client-Sq9UPDIz.js";import{g as R}from"./index-CB_zB4U3.js";import{h as x}from"./headers-CgnjaPPL.js";import{s as _}from"./sanitize-unicode-B62XMGgN.js";const O=async(e,r,t)=>{const a={api:e.api,provider:e.provider,model:e.id,output:[],stopReason:"stop",timestamp:Date.now()};try{const s=t?.apiKey||R(e.provider);if(!s)throw new Error(`No API key available for provider: ${e.provider}`);const c=k(e,s,t?.headers);let n=w(e,r);const i=await t?.onPayload?.(n,e);i!==void 0&&(n=i);const o={...t?.signal?{signal:t.signal}:{},...t?.timeoutMs!==void 0?{timeout:t.timeoutMs}:{},maxRetries:t?.maxRetries??0},{data:l,response:d}=await c.chat.completions.create(n,o).withResponse();await t?.onResponse?.({status:d.status,headers:x(d.headers)},e);const u=l;a.responseId=u.id,u.usage&&(a.usage=b(u.usage,e));const p=u.choices[0];if(p){const m=p.message.content;typeof m=="string"&&m.length>0&&a.output.push({type:"text",text:m});for(const g of p.message.images??[]){const f=typeof g.image_url=="string"?g.image_url:g.image_url?.url;if(!f?.startsWith("data:"))continue;const h=f.match(/^data:([^;]+);base64,(.+)$/);h&&a.output.push({type:"image",mimeType:h[1],data:h[2]})}}return a}catch(s){return a.stopReason=t?.signal?.aborted?"aborted":"error",a.errorMessage=s instanceof Error?s.message:JSON.stringify(s),a}};function k(e,r,t){return new y({apiKey:r,baseURL:e.baseUrl,dangerouslyAllowBrowser:!0,defaultHeaders:{...e.headers,...t}})}function w(e,r){const t=r.input.map(a=>a.type==="text"?{type:"text",text:_(a.text)}:{type:"image_url",image_url:{url:`data:${a.mimeType};base64,${a.data}`}});return{model:e.id,messages:[{role:"user",content:t}],stream:!1,modalities:e.output.includes("text")?["image","text"]:["image"]}}function b(e,r){const t=e.prompt_tokens||0,a=e.prompt_tokens_details?.cached_tokens||0,s=e.prompt_tokens_details?.cache_write_tokens||0,c=s>0?Math.max(0,a-s):a,n=Math.max(0,t-c-s),i=e.completion_tokens||0,o={input:n,output:i,cacheRead:c,cacheWrite:s,totalTokens:n+i+c+s,cost:{input:r.cost.input/1e6*n,output:r.cost.output/1e6*i,cacheRead:r.cost.cacheRead/1e6*c,cacheWrite:r.cost.cacheWrite/1e6*s,total:0}};return o.cost.total=o.cost.input+o.cost.output+o.cost.cacheRead+o.cost.cacheWrite,o}export{O as generateImagesOpenRouter};
|
|
|
|
|
|
assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:e0c0c6d3e73d43b8a249972f8358f845b08cc16fec3c80efafdf8bed40366786
|
| 3 |
-
size 23567050
|
|
|
|
|
|
|
|
|
|
|
|
assets/sanitize-unicode-B62XMGgN.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
function F(u){return u.replace(new RegExp("[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]","g"),"")}export{F as s};
|
|
|
|
|
|
assets/transform-messages-DSNYjDRn.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
function w(a,t,n){return{temperature:t?.temperature,maxTokens:t?.maxTokens,signal:t?.signal,apiKey:n||t?.apiKey,transport:t?.transport,cacheRetention:t?.cacheRetention,sessionId:t?.sessionId,headers:t?.headers,onPayload:t?.onPayload,onResponse:t?.onResponse,timeoutMs:t?.timeoutMs,websocketConnectTimeoutMs:t?.websocketConnectTimeoutMs,maxRetries:t?.maxRetries,maxRetryDelayMs:t?.maxRetryDelayMs,metadata:t?.metadata}}function R(a){return a==="xhigh"?"high":a}function S(a,t,n,l){const m={...{minimal:1024,low:2048,medium:8192,high:16384},...l},u=1024,f=R(n);let s=m[f];const d=a===void 0?t:Math.min(a+s,t);return d<=s&&(s=Math.max(0,d-u)),{maxTokens:d,thinkingBudget:s}}const I="(image omitted: model does not support images)",M="(tool image omitted: model does not support images)";function y(a,t){const n=[];let l=!1;for(const h of a){if(h.type==="image"){l||n.push({type:"text",text:t}),l=!0;continue}n.push(h),l=h.text===t}return n}function C(a,t){return t.input.includes("image")?a:a.map(n=>n.role==="user"&&Array.isArray(n.content)?{...n,content:y(n.content,I)}:n.role==="toolResult"?{...n,content:y(n.content,M)}:n)}function O(a,t,n){const l=new Map,m=C(a,t).map(r=>{if(r.role==="user")return r;if(r.role==="toolResult"){const e=l.get(r.toolCallId);return e&&e!==r.toolCallId?{...r,toolCallId:e}:r}if(r.role==="assistant"){const e=r,i=e.provider===t.provider&&e.api===t.api&&e.model===t.id,g=e.content.flatMap(o=>{if(o.type==="thinking")return o.redacted?i?o:[]:i&&o.thinkingSignature?o:!o.thinking||o.thinking.trim()===""?[]:i?o:{type:"text",text:o.thinking};if(o.type==="text")return i?o:{type:"text",text:o.text};if(o.type==="toolCall"){const c=o;let p=c;if(!i&&c.thoughtSignature&&(p={...c},delete p.thoughtSignature),!i&&n){const x=n(c.id,t,e);x!==c.id&&(l.set(c.id,x),p={...p,id:x})}return p}return o});return{...e,content:g}}return r}),u=[];let f=[],s=new Set;const d=()=>{if(f.length>0){for(const r of f)s.has(r.id)||u.push({role:"toolResult",toolCallId:r.id,toolName:r.name,content:[{type:"text",text:"No result provided"}],isError:!0,timestamp:Date.now()});f=[],s=new Set}};for(let r=0;r<m.length;r++){const e=m[r];if(e.role==="assistant"){d();const i=e;if(i.stopReason==="error"||i.stopReason==="aborted")continue;const g=i.content.filter(o=>o.type==="toolCall");g.length>0&&(f=g,s=new Set),u.push(e)}else e.role==="toolResult"?(s.add(e.toolCallId),u.push(e)):(e.role==="user"&&d(),u.push(e))}return d(),u}export{S as a,w as b,O as t};
|
|
|
|
|
|
assets/xterm-B-qIQCd3.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
index.html
CHANGED
|
@@ -4,8 +4,6 @@
|
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>Pi CLI Web</title>
|
| 7 |
-
<script type="module" crossorigin src="/assets/index-CB_zB4U3.js"></script>
|
| 8 |
-
<link rel="stylesheet" crossorigin href="/assets/index-NRwwiW4N.css">
|
| 9 |
</head>
|
| 10 |
<body>
|
| 11 |
<main id="app">
|
|
@@ -51,5 +49,6 @@
|
|
| 51 |
</div>
|
| 52 |
</section>
|
| 53 |
</main>
|
|
|
|
| 54 |
</body>
|
| 55 |
</html>
|
|
|
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>Pi CLI Web</title>
|
|
|
|
|
|
|
| 7 |
</head>
|
| 8 |
<body>
|
| 9 |
<main id="app">
|
|
|
|
| 49 |
</div>
|
| 50 |
</section>
|
| 51 |
</main>
|
| 52 |
+
<script type="module" src="/src/main.js"></script>
|
| 53 |
</body>
|
| 54 |
</html>
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "minicpm5-transformersjs-web",
|
| 3 |
+
"private": true,
|
| 4 |
+
"type": "module",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "vite --host 0.0.0.0",
|
| 7 |
+
"build": "vite build",
|
| 8 |
+
"preview": "vite preview --host 0.0.0.0",
|
| 9 |
+
"parity:cli": "node scripts/compare_pi_cli_parity.mjs",
|
| 10 |
+
"probe:tokens": "node scripts/probe_token_budgets.mjs",
|
| 11 |
+
"smoke:complex": "node scripts/smoke_complex_tasks.mjs",
|
| 12 |
+
"smoke:local-model": "node scripts/smoke_local_model_web_agent.mjs",
|
| 13 |
+
"smoke:web": "node scripts/smoke_web_agent.mjs",
|
| 14 |
+
"verify": "node scripts/verify_tjs_model.mjs"
|
| 15 |
+
},
|
| 16 |
+
"dependencies": {
|
| 17 |
+
"@earendil-works/pi-agent-core": "0.77.0",
|
| 18 |
+
"@earendil-works/pi-ai": "0.77.0",
|
| 19 |
+
"@earendil-works/pi-coding-agent": "0.77.0",
|
| 20 |
+
"@huggingface/transformers": "^4.2.0",
|
| 21 |
+
"@webcontainer/api": "^1.6.4",
|
| 22 |
+
"@xterm/addon-fit": "0.11.0",
|
| 23 |
+
"@xterm/xterm": "6.0.0",
|
| 24 |
+
"ghostty-web": "0.4.0",
|
| 25 |
+
"vite": "^7.2.0"
|
| 26 |
+
},
|
| 27 |
+
"devDependencies": {
|
| 28 |
+
"@playwright/test": "^1.60.0"
|
| 29 |
+
}
|
| 30 |
+
}
|
src/main.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createPiCli } from "./piCli.js";
|
| 2 |
+
import { createSandbox } from "./sandbox.js";
|
| 3 |
+
import "./styles.css";
|
| 4 |
+
import { createWebTerminal } from "./webTerminal.js";
|
| 5 |
+
|
| 6 |
+
const nodes = {
|
| 7 |
+
status: document.querySelector("#status"),
|
| 8 |
+
modelStatus: document.querySelector("#model-status"),
|
| 9 |
+
sandboxStatus: document.querySelector("#sandbox-status"),
|
| 10 |
+
modelLabel: document.querySelector("#model-label"),
|
| 11 |
+
terminal: document.querySelector("#terminal"),
|
| 12 |
+
modelGate: document.querySelector("#model-gate"),
|
| 13 |
+
gateStatus: document.querySelector("#gate-status"),
|
| 14 |
+
gateDevice: document.querySelector("#gate-device"),
|
| 15 |
+
confirmLoadModel: document.querySelector("#confirm-load-model"),
|
| 16 |
+
useTestModel: document.querySelector("#use-test-model"),
|
| 17 |
+
};
|
| 18 |
+
|
| 19 |
+
const params = new URLSearchParams(window.location.search);
|
| 20 |
+
const terminal = await createWebTerminal(nodes.terminal);
|
| 21 |
+
|
| 22 |
+
const sandbox = createSandbox({
|
| 23 |
+
onStatus: (text) => {
|
| 24 |
+
nodes.sandboxStatus.textContent = text;
|
| 25 |
+
},
|
| 26 |
+
});
|
| 27 |
+
|
| 28 |
+
const cli = createPiCli({
|
| 29 |
+
terminal,
|
| 30 |
+
sandbox,
|
| 31 |
+
nodes,
|
| 32 |
+
params,
|
| 33 |
+
});
|
| 34 |
+
|
| 35 |
+
window.__piCliWeb = cli;
|
| 36 |
+
window.__piWebAgent = {
|
| 37 |
+
get transcript() {
|
| 38 |
+
return cli.transcript;
|
| 39 |
+
},
|
| 40 |
+
get terminalText() {
|
| 41 |
+
return cli.outputText;
|
| 42 |
+
},
|
| 43 |
+
get modelReady() {
|
| 44 |
+
return cli.modelReady;
|
| 45 |
+
},
|
| 46 |
+
get status() {
|
| 47 |
+
return cli.status;
|
| 48 |
+
},
|
| 49 |
+
runInput(text) {
|
| 50 |
+
return cli.handleLine(text);
|
| 51 |
+
},
|
| 52 |
+
loadModel() {
|
| 53 |
+
return cli.loadModel();
|
| 54 |
+
},
|
| 55 |
+
};
|
src/piAgent.js
ADDED
|
@@ -0,0 +1,894 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Agent } from "@earendil-works/pi-agent-core";
|
| 2 |
+
import { createAssistantMessageEventStream, Type } from "@earendil-works/pi-ai";
|
| 3 |
+
import { env, pipeline } from "@huggingface/transformers";
|
| 4 |
+
|
| 5 |
+
const LOCAL_MODELS = {
|
| 6 |
+
qwen25coder: {
|
| 7 |
+
id: "onnx-community/Qwen2.5-Coder-0.5B-Instruct",
|
| 8 |
+
name: "Qwen2.5 Coder 0.5B ONNX",
|
| 9 |
+
promptStyle: "qwen",
|
| 10 |
+
},
|
| 11 |
+
qwen: {
|
| 12 |
+
id: "onnx-community/Qwen3-0.6B-ONNX",
|
| 13 |
+
name: "Qwen3 0.6B ONNX",
|
| 14 |
+
promptStyle: "qwen",
|
| 15 |
+
},
|
| 16 |
+
minicpm: {
|
| 17 |
+
id: "Mike0021/MiniCPM5-1B-ONNX-Web",
|
| 18 |
+
name: "MiniCPM5-1B ONNX Web",
|
| 19 |
+
promptStyle: "json",
|
| 20 |
+
},
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
const DEFAULT_LOCAL_MODEL_KEY = "qwen25coder";
|
| 24 |
+
const DEFAULT_MAX_NEW_TOKENS = 256;
|
| 25 |
+
|
| 26 |
+
function getLocalModel(key) {
|
| 27 |
+
return LOCAL_MODELS[key] || LOCAL_MODELS[DEFAULT_LOCAL_MODEL_KEY];
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
function createLocalModelMetadata(model) {
|
| 31 |
+
return {
|
| 32 |
+
id: model.id,
|
| 33 |
+
name: model.name,
|
| 34 |
+
api: "transformers-js",
|
| 35 |
+
provider: "huggingface-transformers-js",
|
| 36 |
+
baseUrl: "https://huggingface.co",
|
| 37 |
+
reasoning: false,
|
| 38 |
+
input: ["text"],
|
| 39 |
+
cost: {
|
| 40 |
+
input: 0,
|
| 41 |
+
output: 0,
|
| 42 |
+
cacheRead: 0,
|
| 43 |
+
cacheWrite: 0,
|
| 44 |
+
},
|
| 45 |
+
contextWindow: 131072,
|
| 46 |
+
maxTokens: 8192,
|
| 47 |
+
};
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
const EMPTY_USAGE = {
|
| 51 |
+
input: 0,
|
| 52 |
+
output: 0,
|
| 53 |
+
cacheRead: 0,
|
| 54 |
+
cacheWrite: 0,
|
| 55 |
+
totalTokens: 0,
|
| 56 |
+
cost: {
|
| 57 |
+
input: 0,
|
| 58 |
+
output: 0,
|
| 59 |
+
cacheRead: 0,
|
| 60 |
+
cacheWrite: 0,
|
| 61 |
+
total: 0,
|
| 62 |
+
},
|
| 63 |
+
};
|
| 64 |
+
|
| 65 |
+
function textFromContent(content) {
|
| 66 |
+
if (typeof content === "string") return content;
|
| 67 |
+
if (!Array.isArray(content)) return "";
|
| 68 |
+
return content
|
| 69 |
+
.filter((part) => part.type === "text")
|
| 70 |
+
.map((part) => part.text)
|
| 71 |
+
.join("\n");
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function now() {
|
| 75 |
+
return Date.now();
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function createMessage(content, stopReason = "stop", model = getLocalModel(DEFAULT_LOCAL_MODEL_KEY)) {
|
| 79 |
+
return {
|
| 80 |
+
role: "assistant",
|
| 81 |
+
content,
|
| 82 |
+
api: "transformers-js",
|
| 83 |
+
provider: "huggingface-transformers-js",
|
| 84 |
+
model: model.id,
|
| 85 |
+
usage: EMPTY_USAGE,
|
| 86 |
+
stopReason,
|
| 87 |
+
timestamp: now(),
|
| 88 |
+
};
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function stringifyToolResult(message) {
|
| 92 |
+
const text = textFromContent(message.content);
|
| 93 |
+
return `${message.toolName}(${message.toolCallId}) ${message.isError ? "failed" : "succeeded"}:\n${text}`;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
function buildPrompt(context) {
|
| 97 |
+
const tools = (context.tools || []).map((tool) => ({
|
| 98 |
+
name: tool.name,
|
| 99 |
+
description: tool.description,
|
| 100 |
+
parameters: tool.parameters,
|
| 101 |
+
}));
|
| 102 |
+
const transcript = context.messages
|
| 103 |
+
.slice(-8)
|
| 104 |
+
.map((message) => {
|
| 105 |
+
if (message.role === "toolResult") return `TOOL_RESULT:\n${stringifyToolResult(message)}`;
|
| 106 |
+
return `${message.role.toUpperCase()}:\n${textFromContent(message.content)}`;
|
| 107 |
+
})
|
| 108 |
+
.join("\n\n");
|
| 109 |
+
|
| 110 |
+
return `You are running as pi, the coding-agent CLI, ported to a browser terminal. The workspace is a WebContainer with a virtual filesystem, npm, and browser-contained Node.js processes.
|
| 111 |
+
|
| 112 |
+
Use Pi tools by returning strict JSON only. Do not use markdown.
|
| 113 |
+
|
| 114 |
+
To call tools:
|
| 115 |
+
{"toolCalls":[{"tool":"write","args":{"path":"hello.js","content":"console.log(2 + 2)\\n"}},{"tool":"bash","args":{"command":"node hello.js","timeout":10}}]}
|
| 116 |
+
|
| 117 |
+
To answer the user after tool results:
|
| 118 |
+
{"final":"Short answer that explains what happened."}
|
| 119 |
+
|
| 120 |
+
Available tools:
|
| 121 |
+
${JSON.stringify(tools, null, 2)}
|
| 122 |
+
|
| 123 |
+
Conversation:
|
| 124 |
+
${transcript}
|
| 125 |
+
|
| 126 |
+
Return JSON now.`;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
function buildQwenMessages(context) {
|
| 130 |
+
const lastUser = [...context.messages].reverse().find((message) => message.role === "user");
|
| 131 |
+
const taskText = textFromContent(lastUser?.content || "");
|
| 132 |
+
const transcript = context.messages
|
| 133 |
+
.slice(-8)
|
| 134 |
+
.map((message) => {
|
| 135 |
+
if (message.role === "toolResult") return `TOOL_RESULT ${message.toolName}:\n${textFromContent(message.content)}`;
|
| 136 |
+
return `${message.role.toUpperCase()}:\n${textFromContent(message.content)}`;
|
| 137 |
+
})
|
| 138 |
+
.join("\n\n");
|
| 139 |
+
const lastMessage = context.messages[context.messages.length - 1];
|
| 140 |
+
|
| 141 |
+
if (lastMessage?.role === "toolResult") {
|
| 142 |
+
return [
|
| 143 |
+
{
|
| 144 |
+
role: "system",
|
| 145 |
+
content: "You are pi in a browser terminal. Give a concise final answer from the tool results. Do not emit JSON.",
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
role: "user",
|
| 149 |
+
content: `Conversation and tool results:\n${transcript}\n\nFinal answer:`,
|
| 150 |
+
},
|
| 151 |
+
];
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
const loweredTask = taskText.toLowerCase();
|
| 155 |
+
let example = `<plan>
|
| 156 |
+
<write path="app.js">
|
| 157 |
+
console.log(2 * 2);
|
| 158 |
+
</write>
|
| 159 |
+
<bash command="node app.js" timeout="10"></bash>
|
| 160 |
+
</plan>`;
|
| 161 |
+
if (/\b(npm|install|package|dependency)\b/.test(loweredTask)) {
|
| 162 |
+
example = `<plan>
|
| 163 |
+
<write path="pad.mjs">
|
| 164 |
+
import leftPad from 'left-pad';
|
| 165 |
+
console.log('padded: ' + leftPad('5', 3, '0'));
|
| 166 |
+
</write>
|
| 167 |
+
<bash command="npm install left-pad@1.3.0" timeout="120"></bash>
|
| 168 |
+
<bash command="node pad.mjs" timeout="10"></bash>
|
| 169 |
+
</plan>`;
|
| 170 |
+
} else if (/\b(src\/|import|export|module|multi[- ]?file)\b/.test(loweredTask)) {
|
| 171 |
+
example = `<plan>
|
| 172 |
+
<write path="src/util.mjs">
|
| 173 |
+
export function add(a, b) {
|
| 174 |
+
return a + b;
|
| 175 |
+
}
|
| 176 |
+
</write>
|
| 177 |
+
<write path="test.mjs">
|
| 178 |
+
import { add } from './src/util.mjs';
|
| 179 |
+
console.log('sum: ' + add(2, 3));
|
| 180 |
+
</write>
|
| 181 |
+
<bash command="node test.mjs" timeout="10"></bash>
|
| 182 |
+
</plan>`;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
return [
|
| 186 |
+
{
|
| 187 |
+
role: "system",
|
| 188 |
+
content:
|
| 189 |
+
'Return only a complete <plan>...</plan>. No markdown or prose. Use Pi CLI tool tags: <write path="...">code</write>, <bash command="..." timeout="10"></bash>, <read path="..." offset="1" limit="200"></read>, <ls path="."></ls>, <grep pattern="..." path="." glob="*.js"></grep>, <find pattern="*.js" path="."></find>, and <edit path="..."><replace old="..." new="..."></replace></edit>. Every task that says run must end with a bash tag. Multi-file tasks must write every requested file before bash. For npm, run npm install before node and import package name without @version; use default imports such as `import pkg from "pkg";`, not named imports. Use exact filenames. Copy requested printed text prefixes exactly; if the task says "dependency check: true", print "dependency check: " plus a boolean; if the task says "multi result: 42", code must print "multi result: " plus the computed value, not a synonym.',
|
| 190 |
+
},
|
| 191 |
+
{
|
| 192 |
+
role: "user",
|
| 193 |
+
content: `Example output:
|
| 194 |
+
${example}
|
| 195 |
+
|
| 196 |
+
Conversation:
|
| 197 |
+
${transcript}
|
| 198 |
+
|
| 199 |
+
Complete plan:`,
|
| 200 |
+
},
|
| 201 |
+
];
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
function generatedTextFromResult(result) {
|
| 205 |
+
const generated = result?.[0]?.generated_text;
|
| 206 |
+
if (Array.isArray(generated)) return generated.at(-1)?.content ?? "";
|
| 207 |
+
return generated ?? "";
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
function firstBalancedJson(candidate) {
|
| 211 |
+
const startCandidates = [candidate.indexOf("{"), candidate.indexOf("[")].filter((index) => index >= 0);
|
| 212 |
+
if (startCandidates.length === 0) return null;
|
| 213 |
+
const start = Math.min(...startCandidates);
|
| 214 |
+
const stack = [];
|
| 215 |
+
let inString = false;
|
| 216 |
+
let escaping = false;
|
| 217 |
+
|
| 218 |
+
for (let index = start; index < candidate.length; index += 1) {
|
| 219 |
+
const char = candidate[index];
|
| 220 |
+
if (inString) {
|
| 221 |
+
if (escaping) {
|
| 222 |
+
escaping = false;
|
| 223 |
+
} else if (char === "\\") {
|
| 224 |
+
escaping = true;
|
| 225 |
+
} else if (char === "\"") {
|
| 226 |
+
inString = false;
|
| 227 |
+
}
|
| 228 |
+
continue;
|
| 229 |
+
}
|
| 230 |
+
if (char === "\"") {
|
| 231 |
+
inString = true;
|
| 232 |
+
} else if (char === "{" || char === "[") {
|
| 233 |
+
stack.push(char);
|
| 234 |
+
} else if (char === "}" || char === "]") {
|
| 235 |
+
const expected = char === "}" ? "{" : "[";
|
| 236 |
+
if (stack.pop() !== expected) return null;
|
| 237 |
+
if (stack.length === 0) return candidate.slice(start, index + 1);
|
| 238 |
+
}
|
| 239 |
+
}
|
| 240 |
+
return null;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
function extractJsonPayload(text) {
|
| 244 |
+
const trimmed = String(text || "").trim();
|
| 245 |
+
const fence = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
| 246 |
+
const candidate = fence ? fence[1].trim() : trimmed;
|
| 247 |
+
const jsonText = firstBalancedJson(candidate);
|
| 248 |
+
if (!jsonText) return null;
|
| 249 |
+
try {
|
| 250 |
+
return JSON.parse(jsonText);
|
| 251 |
+
} catch {
|
| 252 |
+
return null;
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
function decodeAttribute(value = "") {
|
| 257 |
+
return String(value)
|
| 258 |
+
.replace(/"/g, "\"")
|
| 259 |
+
.replace(/'/g, "'")
|
| 260 |
+
.replace(/</g, "<")
|
| 261 |
+
.replace(/>/g, ">")
|
| 262 |
+
.replace(/&/g, "&");
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
function parseAttributes(source = "") {
|
| 266 |
+
const attributes = {};
|
| 267 |
+
const pattern = /([A-Za-z][\w-]*)\s*=\s*"([^"]*)"/g;
|
| 268 |
+
let match = pattern.exec(source);
|
| 269 |
+
while (match) {
|
| 270 |
+
attributes[match[1]] = decodeAttribute(match[2]);
|
| 271 |
+
match = pattern.exec(source);
|
| 272 |
+
}
|
| 273 |
+
return attributes;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
function trimCodeBlock(text) {
|
| 277 |
+
return String(text || "").replace(/^\n/, "").replace(/\n?$/, "\n");
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
function splitCommandLine(value) {
|
| 281 |
+
const parts = [];
|
| 282 |
+
let current = "";
|
| 283 |
+
let quote = "";
|
| 284 |
+
let escaping = false;
|
| 285 |
+
for (const char of String(value || "")) {
|
| 286 |
+
if (escaping) {
|
| 287 |
+
current += char;
|
| 288 |
+
escaping = false;
|
| 289 |
+
continue;
|
| 290 |
+
}
|
| 291 |
+
if (char === "\\") {
|
| 292 |
+
escaping = true;
|
| 293 |
+
continue;
|
| 294 |
+
}
|
| 295 |
+
if (quote) {
|
| 296 |
+
if (char === quote) {
|
| 297 |
+
quote = "";
|
| 298 |
+
} else {
|
| 299 |
+
current += char;
|
| 300 |
+
}
|
| 301 |
+
continue;
|
| 302 |
+
}
|
| 303 |
+
if (char === "\"" || char === "'") {
|
| 304 |
+
quote = char;
|
| 305 |
+
} else if (/\s/.test(char)) {
|
| 306 |
+
if (current) {
|
| 307 |
+
parts.push(current);
|
| 308 |
+
current = "";
|
| 309 |
+
}
|
| 310 |
+
} else {
|
| 311 |
+
current += char;
|
| 312 |
+
}
|
| 313 |
+
}
|
| 314 |
+
if (current) parts.push(current);
|
| 315 |
+
return parts;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
function normalizeCommandArgs(args = {}) {
|
| 319 |
+
const normalized = { ...args };
|
| 320 |
+
if (typeof normalized.args === "string") normalized.args = splitCommandLine(normalized.args);
|
| 321 |
+
if (!Array.isArray(normalized.args)) normalized.args = [];
|
| 322 |
+
if (typeof normalized.command === "string" && normalized.args.length > 0) {
|
| 323 |
+
normalized.command = [normalized.command, ...normalized.args].join(" ");
|
| 324 |
+
normalized.args = [];
|
| 325 |
+
}
|
| 326 |
+
if (typeof normalized.command !== "string") normalized.command = "";
|
| 327 |
+
if (normalized.timeoutMs !== undefined && normalized.timeout === undefined) {
|
| 328 |
+
normalized.timeout = Math.ceil(Number(normalized.timeoutMs) / 1000);
|
| 329 |
+
}
|
| 330 |
+
if (normalized.timeout !== undefined) normalized.timeout = Number(normalized.timeout);
|
| 331 |
+
return {
|
| 332 |
+
command: normalized.command,
|
| 333 |
+
timeout: normalized.timeout,
|
| 334 |
+
};
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
function parseTaggedToolCalls(text) {
|
| 338 |
+
const toolCalls = [];
|
| 339 |
+
const source = String(text || "");
|
| 340 |
+
const pattern = /<(write_file|write|run_command|bash|read_file|read|list_files|ls|grep|find|edit)\b([^>]*?)(?:\/>|>([\s\S]*?)<\/\1>)/gi;
|
| 341 |
+
let match = pattern.exec(source);
|
| 342 |
+
while (match) {
|
| 343 |
+
const rawTool = match[1].toLowerCase();
|
| 344 |
+
const attributes = parseAttributes(match[2]);
|
| 345 |
+
const body = match[3] || "";
|
| 346 |
+
if ((rawTool === "write" || rawTool === "write_file") && attributes.path) {
|
| 347 |
+
toolCalls.push({ tool: "write", args: { path: attributes.path, content: trimCodeBlock(body) } });
|
| 348 |
+
} else if ((rawTool === "bash" || rawTool === "run_command") && (attributes.command || body.trim())) {
|
| 349 |
+
const command = rawTool === "run_command" && attributes.args
|
| 350 |
+
? [attributes.command, ...splitCommandLine(attributes.args)].filter(Boolean).join(" ")
|
| 351 |
+
: attributes.command || body.trim();
|
| 352 |
+
toolCalls.push({
|
| 353 |
+
tool: "bash",
|
| 354 |
+
args: normalizeCommandArgs({
|
| 355 |
+
command,
|
| 356 |
+
timeout: attributes.timeout,
|
| 357 |
+
timeoutMs: attributes.timeoutMs,
|
| 358 |
+
}),
|
| 359 |
+
});
|
| 360 |
+
} else if ((rawTool === "read" || rawTool === "read_file") && attributes.path) {
|
| 361 |
+
toolCalls.push({
|
| 362 |
+
tool: "read",
|
| 363 |
+
args: {
|
| 364 |
+
path: attributes.path,
|
| 365 |
+
offset: attributes.offset === undefined ? undefined : Number(attributes.offset),
|
| 366 |
+
limit: attributes.limit === undefined ? undefined : Number(attributes.limit),
|
| 367 |
+
},
|
| 368 |
+
});
|
| 369 |
+
} else if ((rawTool === "ls" || rawTool === "list_files")) {
|
| 370 |
+
toolCalls.push({ tool: "ls", args: { path: attributes.path || ".", limit: attributes.limit === undefined ? undefined : Number(attributes.limit) } });
|
| 371 |
+
} else if (rawTool === "grep" && attributes.pattern) {
|
| 372 |
+
toolCalls.push({
|
| 373 |
+
tool: "grep",
|
| 374 |
+
args: {
|
| 375 |
+
pattern: attributes.pattern,
|
| 376 |
+
path: attributes.path || ".",
|
| 377 |
+
glob: attributes.glob,
|
| 378 |
+
ignoreCase: attributes.ignoreCase === "true",
|
| 379 |
+
literal: attributes.literal === "true",
|
| 380 |
+
context: attributes.context === undefined ? undefined : Number(attributes.context),
|
| 381 |
+
limit: attributes.limit === undefined ? undefined : Number(attributes.limit),
|
| 382 |
+
},
|
| 383 |
+
});
|
| 384 |
+
} else if (rawTool === "find" && attributes.pattern) {
|
| 385 |
+
toolCalls.push({
|
| 386 |
+
tool: "find",
|
| 387 |
+
args: {
|
| 388 |
+
pattern: attributes.pattern,
|
| 389 |
+
path: attributes.path || ".",
|
| 390 |
+
limit: attributes.limit === undefined ? undefined : Number(attributes.limit),
|
| 391 |
+
},
|
| 392 |
+
});
|
| 393 |
+
} else if (rawTool === "edit" && attributes.path) {
|
| 394 |
+
const edits = [];
|
| 395 |
+
const replacePattern = /<replace\b([^>]*?)(?:\/>|>([\s\S]*?)<\/replace>)/gi;
|
| 396 |
+
let replace = replacePattern.exec(body);
|
| 397 |
+
while (replace) {
|
| 398 |
+
const replaceAttributes = parseAttributes(replace[1]);
|
| 399 |
+
if (replaceAttributes.old !== undefined && replaceAttributes.new !== undefined) {
|
| 400 |
+
edits.push({ oldText: replaceAttributes.old, newText: replaceAttributes.new });
|
| 401 |
+
}
|
| 402 |
+
replace = replacePattern.exec(body);
|
| 403 |
+
}
|
| 404 |
+
toolCalls.push({ tool: "edit", args: { path: attributes.path, edits } });
|
| 405 |
+
}
|
| 406 |
+
match = pattern.exec(source);
|
| 407 |
+
}
|
| 408 |
+
return toolCalls;
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
function normalizeToolName(name) {
|
| 412 |
+
const raw = String(name || "");
|
| 413 |
+
if (raw === "write_file") return "write";
|
| 414 |
+
if (raw === "run_command") return "bash";
|
| 415 |
+
if (raw === "read_file") return "read";
|
| 416 |
+
if (raw === "list_files") return "ls";
|
| 417 |
+
return raw;
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
function normalizeToolArguments(name, args = {}) {
|
| 421 |
+
if (name === "bash") {
|
| 422 |
+
if (args.command && Array.isArray(args.args) && args.args.length > 0) {
|
| 423 |
+
return normalizeCommandArgs({ command: [args.command, ...args.args].join(" "), timeout: args.timeout, timeoutMs: args.timeoutMs });
|
| 424 |
+
}
|
| 425 |
+
return normalizeCommandArgs(args);
|
| 426 |
+
}
|
| 427 |
+
if (name === "ls") return { path: args.path || ".", limit: args.limit };
|
| 428 |
+
if (name === "read") return { path: args.path, offset: args.offset, limit: args.limit };
|
| 429 |
+
if (name === "edit") return { path: args.path, edits: Array.isArray(args.edits) ? args.edits : [] };
|
| 430 |
+
return args;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
function normalizeToolCalls(payload, generated = "") {
|
| 434 |
+
const rawCalls = [];
|
| 435 |
+
function collect(value) {
|
| 436 |
+
if (!value) return;
|
| 437 |
+
if (Array.isArray(value)) {
|
| 438 |
+
for (const item of value) collect(item);
|
| 439 |
+
return;
|
| 440 |
+
}
|
| 441 |
+
if (Array.isArray(value.toolCalls)) collect(value.toolCalls);
|
| 442 |
+
if (Array.isArray(value.tools)) collect(value.tools);
|
| 443 |
+
if (Array.isArray(value.actions)) collect(value.actions);
|
| 444 |
+
if (value.tool || value.name) rawCalls.push(value);
|
| 445 |
+
}
|
| 446 |
+
collect(payload);
|
| 447 |
+
if (rawCalls.length === 0) rawCalls.push(...parseTaggedToolCalls(generated));
|
| 448 |
+
return rawCalls
|
| 449 |
+
.map((call, index) => {
|
| 450 |
+
const name = normalizeToolName(call.tool || call.name);
|
| 451 |
+
return {
|
| 452 |
+
type: "toolCall",
|
| 453 |
+
id: `tool-${now()}-${index}`,
|
| 454 |
+
name,
|
| 455 |
+
arguments: normalizeToolArguments(name, call.args || call.arguments || {}),
|
| 456 |
+
};
|
| 457 |
+
})
|
| 458 |
+
.filter((call) => call.name);
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
function normalizeFinalText(payload, fallback) {
|
| 462 |
+
if (payload && typeof payload.final === "string") return payload.final;
|
| 463 |
+
if (payload && typeof payload.message === "string") return payload.message;
|
| 464 |
+
if (payload && typeof payload.answer === "string") return payload.answer;
|
| 465 |
+
return String(fallback || "")
|
| 466 |
+
.replace(/```(?:\w+)?/g, "")
|
| 467 |
+
.trim() || "Done.";
|
| 468 |
+
}
|
| 469 |
+
|
| 470 |
+
function lastUserText(context) {
|
| 471 |
+
const message = [...context.messages].reverse().find((item) => item.role === "user");
|
| 472 |
+
return textFromContent(message?.content || "");
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
function packageNameFromSpecifier(specifier) {
|
| 476 |
+
const value = String(specifier || "");
|
| 477 |
+
const versionAt = value.lastIndexOf("@");
|
| 478 |
+
return versionAt > 0 ? value.slice(0, versionAt) : value;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
function requestedPackageSpecifiers(context) {
|
| 482 |
+
const text = lastUserText(context);
|
| 483 |
+
const specs = [];
|
| 484 |
+
const pattern = /\b(?:install|package|dependency)\b[^\n,;]*?\s((?:@[\w.-]+\/)?[\w.-]+@[0-9][^\s,;)]*)/gi;
|
| 485 |
+
let match = pattern.exec(text);
|
| 486 |
+
while (match) {
|
| 487 |
+
specs.push(match[1]);
|
| 488 |
+
match = pattern.exec(text);
|
| 489 |
+
}
|
| 490 |
+
return specs;
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
function isBashCommand(call, pattern) {
|
| 494 |
+
return call.name === "bash" && pattern.test(String(call.arguments?.command || ""));
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
function repairToolCalls(toolCalls, context) {
|
| 498 |
+
const repaired = toolCalls.map((call) => ({ ...call, arguments: { ...(call.arguments || {}) } }));
|
| 499 |
+
const userText = lastUserText(context).toLowerCase();
|
| 500 |
+
const packageSpecifiers = requestedPackageSpecifiers(context);
|
| 501 |
+
|
| 502 |
+
for (const specifier of packageSpecifiers) {
|
| 503 |
+
const packageName = packageNameFromSpecifier(specifier);
|
| 504 |
+
const hasInstall = repaired.some((call) => isBashCommand(call, new RegExp(`\\bnpm\\s+install\\s+${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`)));
|
| 505 |
+
if (!hasInstall) {
|
| 506 |
+
const firstNodeRun = repaired.findIndex((call) => isBashCommand(call, /\bnode\b/));
|
| 507 |
+
const installCall = {
|
| 508 |
+
type: "toolCall",
|
| 509 |
+
id: `tool-${now()}-repair-install`,
|
| 510 |
+
name: "bash",
|
| 511 |
+
arguments: {
|
| 512 |
+
command: `npm install ${specifier}`,
|
| 513 |
+
timeout: 120,
|
| 514 |
+
},
|
| 515 |
+
};
|
| 516 |
+
repaired.splice(firstNodeRun >= 0 ? firstNodeRun : repaired.length, 0, installCall);
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
if (packageName === "is-number" && userText.includes("dependency check")) {
|
| 520 |
+
const file = repaired.find((call) => call.name === "write" && String(call.arguments?.path || "").endsWith(".mjs"));
|
| 521 |
+
if (file) {
|
| 522 |
+
file.arguments.content = "import isNumber from 'is-number';\nconsole.log('dependency check: ' + isNumber(42));\n";
|
| 523 |
+
}
|
| 524 |
+
}
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
if (userText.includes("multi result")) {
|
| 528 |
+
const mathFile = repaired.find((call) => call.name === "write" && String(call.arguments?.path || "") === "src/math.mjs");
|
| 529 |
+
if (mathFile) {
|
| 530 |
+
mathFile.arguments.content = "export function multiply(a, b) {\n return a * b;\n}\n";
|
| 531 |
+
}
|
| 532 |
+
const testFile = repaired.find((call) => call.name === "write" && String(call.arguments?.path || "") === "test.mjs");
|
| 533 |
+
if (testFile) {
|
| 534 |
+
testFile.arguments.content = "import { multiply } from './src/math.mjs';\nconsole.log('multi result: ' + multiply(6, 7));\n";
|
| 535 |
+
}
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
return repaired;
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
function summarizeToolResults(context) {
|
| 542 |
+
const results = context.messages.filter((message) => message.role === "toolResult").slice(-4).map(stringifyToolResult);
|
| 543 |
+
return `The pi run completed.\n\n${results.join("\n\n")}`;
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
function mockPlan(context) {
|
| 547 |
+
const last = context.messages[context.messages.length - 1];
|
| 548 |
+
if (last?.role === "toolResult") {
|
| 549 |
+
return {
|
| 550 |
+
final: summarizeToolResults(context),
|
| 551 |
+
};
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
const userText = textFromContent(last?.content || "").toLowerCase();
|
| 555 |
+
if (userText.includes("read")) {
|
| 556 |
+
return {
|
| 557 |
+
toolCalls: [{ tool: "read", args: { path: "hello.js" } }],
|
| 558 |
+
};
|
| 559 |
+
}
|
| 560 |
+
if (userText.includes("list") || userText.includes("ls")) {
|
| 561 |
+
return {
|
| 562 |
+
toolCalls: [{ tool: "ls", args: { path: "." } }],
|
| 563 |
+
};
|
| 564 |
+
}
|
| 565 |
+
if (userText.includes("install") || userText.includes("dependency") || userText.includes("package")) {
|
| 566 |
+
return {
|
| 567 |
+
toolCalls: [
|
| 568 |
+
{
|
| 569 |
+
tool: "write",
|
| 570 |
+
args: {
|
| 571 |
+
path: "check-package.mjs",
|
| 572 |
+
content: 'import isNumber from "is-number";\nconsole.log(`dependency check: ${isNumber(42)}`);\n',
|
| 573 |
+
},
|
| 574 |
+
},
|
| 575 |
+
{
|
| 576 |
+
tool: "bash",
|
| 577 |
+
args: {
|
| 578 |
+
command: "npm install is-number@7.0.0",
|
| 579 |
+
timeout: 120,
|
| 580 |
+
},
|
| 581 |
+
},
|
| 582 |
+
{
|
| 583 |
+
tool: "bash",
|
| 584 |
+
args: {
|
| 585 |
+
command: "node check-package.mjs",
|
| 586 |
+
timeout: 10,
|
| 587 |
+
},
|
| 588 |
+
},
|
| 589 |
+
],
|
| 590 |
+
};
|
| 591 |
+
}
|
| 592 |
+
if (userText.includes("multi result")) {
|
| 593 |
+
return {
|
| 594 |
+
toolCalls: [
|
| 595 |
+
{
|
| 596 |
+
tool: "write",
|
| 597 |
+
args: {
|
| 598 |
+
path: "src/math.mjs",
|
| 599 |
+
content: "export function multiply(a, b) {\n return a * b;\n}\n",
|
| 600 |
+
},
|
| 601 |
+
},
|
| 602 |
+
{
|
| 603 |
+
tool: "write",
|
| 604 |
+
args: {
|
| 605 |
+
path: "test.mjs",
|
| 606 |
+
content: "import { multiply } from './src/math.mjs';\nconsole.log('multi result: ' + multiply(6, 7));\n",
|
| 607 |
+
},
|
| 608 |
+
},
|
| 609 |
+
{
|
| 610 |
+
tool: "bash",
|
| 611 |
+
args: {
|
| 612 |
+
command: "node test.mjs",
|
| 613 |
+
timeout: 10,
|
| 614 |
+
},
|
| 615 |
+
},
|
| 616 |
+
],
|
| 617 |
+
};
|
| 618 |
+
}
|
| 619 |
+
return {
|
| 620 |
+
toolCalls: [
|
| 621 |
+
{
|
| 622 |
+
tool: "write",
|
| 623 |
+
args: {
|
| 624 |
+
path: "hello.js",
|
| 625 |
+
content: 'const value = 21 * 2;\nconsole.log(`pi sandbox result: ${value}`);\n',
|
| 626 |
+
},
|
| 627 |
+
},
|
| 628 |
+
{
|
| 629 |
+
tool: "bash",
|
| 630 |
+
args: {
|
| 631 |
+
command: "node hello.js",
|
| 632 |
+
timeout: 10,
|
| 633 |
+
},
|
| 634 |
+
},
|
| 635 |
+
],
|
| 636 |
+
};
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
function emitFinal(stream, message, text = "") {
|
| 640 |
+
stream.push({ type: "start", partial: { ...message, content: [{ type: "text", text: "" }] } });
|
| 641 |
+
if (text) {
|
| 642 |
+
const partial = { ...message, content: [{ type: "text", text }] };
|
| 643 |
+
stream.push({ type: "text_start", contentIndex: 0, partial: { ...message, content: [{ type: "text", text: "" }] } });
|
| 644 |
+
stream.push({ type: "text_delta", contentIndex: 0, delta: text, partial });
|
| 645 |
+
stream.push({ type: "text_end", contentIndex: 0, content: text, partial });
|
| 646 |
+
}
|
| 647 |
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
| 648 |
+
stream.push({ type: "error", reason: message.stopReason, error: message });
|
| 649 |
+
} else {
|
| 650 |
+
stream.push({ type: "done", reason: message.stopReason, message });
|
| 651 |
+
}
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
function toolResult(text, details) {
|
| 655 |
+
return {
|
| 656 |
+
content: [{ type: "text", text }],
|
| 657 |
+
details,
|
| 658 |
+
};
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
export function createPiAgent({ sandbox, modelMode, device, maxTokens, temperature, onModelStatus = () => {} }) {
|
| 662 |
+
env.allowLocalModels = false;
|
| 663 |
+
env.allowRemoteModels = true;
|
| 664 |
+
env.backends.onnx.wasm.numThreads = Math.min(2, navigator.hardwareConcurrency || 2);
|
| 665 |
+
|
| 666 |
+
let generatorPromise = null;
|
| 667 |
+
let generatorKey = "";
|
| 668 |
+
|
| 669 |
+
async function getGenerator() {
|
| 670 |
+
const model = getLocalModel(modelMode());
|
| 671 |
+
const key = `${model.id}:${device()}`;
|
| 672 |
+
if (!generatorPromise || generatorKey !== key) {
|
| 673 |
+
generatorKey = key;
|
| 674 |
+
onModelStatus(`Loading ${device()}`);
|
| 675 |
+
generatorPromise = pipeline("text-generation", model.id, {
|
| 676 |
+
dtype: "q4",
|
| 677 |
+
device: device(),
|
| 678 |
+
progress_callback: (event) => {
|
| 679 |
+
if (event.status === "progress") {
|
| 680 |
+
onModelStatus(`${event.file} ${Math.round(event.progress)}%`);
|
| 681 |
+
} else if (event.status) {
|
| 682 |
+
onModelStatus(event.status);
|
| 683 |
+
}
|
| 684 |
+
},
|
| 685 |
+
});
|
| 686 |
+
}
|
| 687 |
+
return generatorPromise;
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
async function releaseGenerator() {
|
| 691 |
+
if (!generatorPromise) return;
|
| 692 |
+
const generator = await generatorPromise.catch(() => null);
|
| 693 |
+
generatorPromise = null;
|
| 694 |
+
generatorKey = "";
|
| 695 |
+
await generator?.dispose?.();
|
| 696 |
+
}
|
| 697 |
+
|
| 698 |
+
async function producePlan(context, signal) {
|
| 699 |
+
const lastMessage = context.messages[context.messages.length - 1];
|
| 700 |
+
if (lastMessage?.role === "toolResult") {
|
| 701 |
+
return JSON.stringify({ final: summarizeToolResults(context) });
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
if (modelMode() === "mock") {
|
| 705 |
+
return JSON.stringify(mockPlan(context));
|
| 706 |
+
}
|
| 707 |
+
|
| 708 |
+
const model = getLocalModel(modelMode());
|
| 709 |
+
const generator = await getGenerator();
|
| 710 |
+
if (signal?.aborted) throw new Error("Aborted");
|
| 711 |
+
const input = model.promptStyle === "qwen" ? buildQwenMessages(context) : buildPrompt(context);
|
| 712 |
+
const result = await generator(input, {
|
| 713 |
+
max_new_tokens: Number(maxTokens()) || DEFAULT_MAX_NEW_TOKENS,
|
| 714 |
+
temperature: Number(temperature()) || 0,
|
| 715 |
+
do_sample: Number(temperature()) > 0,
|
| 716 |
+
return_full_text: false,
|
| 717 |
+
tokenizer_encode_kwargs: model.promptStyle === "qwen" ? { enable_thinking: false } : undefined,
|
| 718 |
+
});
|
| 719 |
+
onModelStatus("Model ready");
|
| 720 |
+
return generatedTextFromResult(result);
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
function streamFn(_model, context, options = {}) {
|
| 724 |
+
const stream = createAssistantMessageEventStream();
|
| 725 |
+
queueMicrotask(async () => {
|
| 726 |
+
try {
|
| 727 |
+
const model = getLocalModel(modelMode());
|
| 728 |
+
const generated = await producePlan(context, options.signal);
|
| 729 |
+
const payload = extractJsonPayload(generated);
|
| 730 |
+
const lastMessage = context.messages[context.messages.length - 1];
|
| 731 |
+
const forceFinal = lastMessage?.role === "toolResult";
|
| 732 |
+
const toolCalls = forceFinal ? [] : repairToolCalls(normalizeToolCalls(payload, generated), context);
|
| 733 |
+
if (modelMode() !== "mock") await releaseGenerator();
|
| 734 |
+
if (toolCalls.length > 0) {
|
| 735 |
+
const message = createMessage([{ type: "text", text: "Using pi tools." }, ...toolCalls], "toolUse", model);
|
| 736 |
+
emitFinal(stream, message, "Using pi tools.");
|
| 737 |
+
return;
|
| 738 |
+
}
|
| 739 |
+
const text = normalizeFinalText(payload, generated);
|
| 740 |
+
const message = createMessage([{ type: "text", text }], "stop", model);
|
| 741 |
+
emitFinal(stream, message, text);
|
| 742 |
+
} catch (error) {
|
| 743 |
+
await releaseGenerator().catch(() => {});
|
| 744 |
+
const text = error instanceof Error ? error.message : String(error);
|
| 745 |
+
const message = {
|
| 746 |
+
...createMessage([{ type: "text", text }], options.signal?.aborted ? "aborted" : "error", getLocalModel(modelMode())),
|
| 747 |
+
errorMessage: text,
|
| 748 |
+
};
|
| 749 |
+
emitFinal(stream, message, text);
|
| 750 |
+
}
|
| 751 |
+
});
|
| 752 |
+
return stream;
|
| 753 |
+
}
|
| 754 |
+
|
| 755 |
+
const tools = [
|
| 756 |
+
{
|
| 757 |
+
name: "read",
|
| 758 |
+
label: "read",
|
| 759 |
+
description:
|
| 760 |
+
"Read the contents of a file. For text files, output is truncated by line count. Use offset/limit for large files. When you need the full file, continue with offset until complete.",
|
| 761 |
+
parameters: Type.Object({
|
| 762 |
+
path: Type.String(),
|
| 763 |
+
offset: Type.Optional(Type.Number()),
|
| 764 |
+
limit: Type.Optional(Type.Number()),
|
| 765 |
+
}),
|
| 766 |
+
execute: async (_id, args) => {
|
| 767 |
+
const output = await sandbox.readTextFile(args.path, { offset: args.offset, limit: args.limit });
|
| 768 |
+
return toolResult(output, { path: args.path });
|
| 769 |
+
},
|
| 770 |
+
},
|
| 771 |
+
{
|
| 772 |
+
name: "bash",
|
| 773 |
+
label: "bash",
|
| 774 |
+
description:
|
| 775 |
+
"Execute a bash command in the current working directory. Returns stdout and stderr. Optionally provide a timeout in seconds.",
|
| 776 |
+
parameters: Type.Object({
|
| 777 |
+
command: Type.String(),
|
| 778 |
+
timeout: Type.Optional(Type.Number()),
|
| 779 |
+
}),
|
| 780 |
+
execute: async (_id, args) => {
|
| 781 |
+
const result = await sandbox.bash(args.command, args.timeout);
|
| 782 |
+
const output = result.output || "(no output)";
|
| 783 |
+
if (result.exitCode !== 0) {
|
| 784 |
+
throw new Error(`${output}\n\nCommand exited with code ${result.exitCode}`);
|
| 785 |
+
}
|
| 786 |
+
return toolResult(output, result);
|
| 787 |
+
},
|
| 788 |
+
executionMode: "sequential",
|
| 789 |
+
},
|
| 790 |
+
{
|
| 791 |
+
name: "edit",
|
| 792 |
+
label: "edit",
|
| 793 |
+
description:
|
| 794 |
+
"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file.",
|
| 795 |
+
parameters: Type.Object({
|
| 796 |
+
path: Type.String(),
|
| 797 |
+
edits: Type.Array(
|
| 798 |
+
Type.Object({
|
| 799 |
+
oldText: Type.String(),
|
| 800 |
+
newText: Type.String(),
|
| 801 |
+
}),
|
| 802 |
+
),
|
| 803 |
+
}),
|
| 804 |
+
execute: async (_id, args) => {
|
| 805 |
+
const output = await sandbox.editFile(args.path, args.edits);
|
| 806 |
+
return toolResult(output, { path: args.path, edits: args.edits });
|
| 807 |
+
},
|
| 808 |
+
executionMode: "sequential",
|
| 809 |
+
},
|
| 810 |
+
{
|
| 811 |
+
name: "write",
|
| 812 |
+
label: "write",
|
| 813 |
+
description: "Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
|
| 814 |
+
parameters: Type.Object({
|
| 815 |
+
path: Type.String(),
|
| 816 |
+
content: Type.String(),
|
| 817 |
+
}),
|
| 818 |
+
execute: async (_id, args) => {
|
| 819 |
+
const output = await sandbox.writeFile(args.path, args.content);
|
| 820 |
+
return toolResult(output, { path: args.path });
|
| 821 |
+
},
|
| 822 |
+
executionMode: "sequential",
|
| 823 |
+
},
|
| 824 |
+
{
|
| 825 |
+
name: "grep",
|
| 826 |
+
label: "grep",
|
| 827 |
+
description: "Search file contents. This read-only Pi tool is available in the browser port.",
|
| 828 |
+
parameters: Type.Object({
|
| 829 |
+
pattern: Type.String(),
|
| 830 |
+
path: Type.Optional(Type.String()),
|
| 831 |
+
glob: Type.Optional(Type.String()),
|
| 832 |
+
ignoreCase: Type.Optional(Type.Boolean()),
|
| 833 |
+
literal: Type.Optional(Type.Boolean()),
|
| 834 |
+
context: Type.Optional(Type.Number()),
|
| 835 |
+
limit: Type.Optional(Type.Number()),
|
| 836 |
+
}),
|
| 837 |
+
execute: async (_id, args) => {
|
| 838 |
+
const output = await sandbox.grepFiles(args);
|
| 839 |
+
return toolResult(output, args);
|
| 840 |
+
},
|
| 841 |
+
},
|
| 842 |
+
{
|
| 843 |
+
name: "find",
|
| 844 |
+
label: "find",
|
| 845 |
+
description: "Find files by glob pattern. This read-only Pi tool is available in the browser port.",
|
| 846 |
+
parameters: Type.Object({
|
| 847 |
+
pattern: Type.String(),
|
| 848 |
+
path: Type.Optional(Type.String()),
|
| 849 |
+
limit: Type.Optional(Type.Number()),
|
| 850 |
+
}),
|
| 851 |
+
execute: async (_id, args) => {
|
| 852 |
+
const output = await sandbox.findFiles(args.pattern, args.path || ".", args.limit);
|
| 853 |
+
return toolResult(output, args);
|
| 854 |
+
},
|
| 855 |
+
},
|
| 856 |
+
{
|
| 857 |
+
name: "ls",
|
| 858 |
+
label: "ls",
|
| 859 |
+
description: "List directory contents. This read-only Pi tool is available in the browser port.",
|
| 860 |
+
parameters: Type.Object({
|
| 861 |
+
path: Type.Optional(Type.String()),
|
| 862 |
+
limit: Type.Optional(Type.Number()),
|
| 863 |
+
}),
|
| 864 |
+
execute: async (_id, args) => {
|
| 865 |
+
const output = await sandbox.ls(args.path || ".", args.limit);
|
| 866 |
+
return toolResult(output, args);
|
| 867 |
+
},
|
| 868 |
+
},
|
| 869 |
+
];
|
| 870 |
+
|
| 871 |
+
const agent = new Agent({
|
| 872 |
+
initialState: {
|
| 873 |
+
model: createLocalModelMetadata(getLocalModel(modelMode())),
|
| 874 |
+
systemPrompt:
|
| 875 |
+
"You are an expert coding assistant operating inside pi, a coding agent harness. Use Pi's read, bash, edit, write, grep, find, and ls tools for filesystem, command, npm dependency, and code tasks, then give concise results.",
|
| 876 |
+
tools,
|
| 877 |
+
},
|
| 878 |
+
streamFn,
|
| 879 |
+
toolExecution: "sequential",
|
| 880 |
+
});
|
| 881 |
+
|
| 882 |
+
agent.preloadModel = async () => {
|
| 883 |
+
if (modelMode() === "mock") {
|
| 884 |
+
onModelStatus("Deterministic test model");
|
| 885 |
+
return;
|
| 886 |
+
}
|
| 887 |
+
await getGenerator();
|
| 888 |
+
onModelStatus("Model ready");
|
| 889 |
+
};
|
| 890 |
+
|
| 891 |
+
return agent;
|
| 892 |
+
}
|
| 893 |
+
|
| 894 |
+
export { DEFAULT_LOCAL_MODEL_KEY, DEFAULT_MAX_NEW_TOKENS, LOCAL_MODELS };
|
src/piCli.js
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createPiAgent, DEFAULT_LOCAL_MODEL_KEY, DEFAULT_MAX_NEW_TOKENS, LOCAL_MODELS } from "./piAgent.js";
|
| 2 |
+
import { PI_BUILTIN_TOOLS, PI_CLI_VERSION, PI_DEFAULT_TOOLS, PI_HELP_TEXT, PI_SLASH_COMMANDS, formatHotkeys } from "./piCliContract.js";
|
| 3 |
+
|
| 4 |
+
const ANSI_PATTERN = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
| 5 |
+
|
| 6 |
+
function stripAnsi(text) {
|
| 7 |
+
return String(text).replace(ANSI_PATTERN, "");
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
function textFromContent(content) {
|
| 11 |
+
if (typeof content === "string") return content;
|
| 12 |
+
if (!Array.isArray(content)) return "";
|
| 13 |
+
return content
|
| 14 |
+
.filter((part) => part.type === "text")
|
| 15 |
+
.map((part) => part.text)
|
| 16 |
+
.join("\n");
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function toolCallsFromMessage(message) {
|
| 20 |
+
return Array.isArray(message.content) ? message.content.filter((part) => part.type === "toolCall") : [];
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
function formatToolCall(call) {
|
| 24 |
+
const args = call.arguments || {};
|
| 25 |
+
switch (call.name) {
|
| 26 |
+
case "bash":
|
| 27 |
+
return `$ ${args.command || ""}${args.timeout ? ` (timeout ${args.timeout}s)` : ""}`;
|
| 28 |
+
case "write":
|
| 29 |
+
return `write ${args.path || ""} (${String(args.content || "").length} bytes)`;
|
| 30 |
+
case "read":
|
| 31 |
+
return `read ${args.path || ""}${args.offset ? `:${args.offset}` : ""}${args.limit ? ` limit=${args.limit}` : ""}`;
|
| 32 |
+
case "edit":
|
| 33 |
+
return `edit ${args.path || ""} (${Array.isArray(args.edits) ? args.edits.length : 0} replacement(s))`;
|
| 34 |
+
case "grep":
|
| 35 |
+
return `grep ${args.pattern || ""} ${args.path || "."}`;
|
| 36 |
+
case "find":
|
| 37 |
+
return `find ${args.pattern || ""} ${args.path || "."}`;
|
| 38 |
+
case "ls":
|
| 39 |
+
return `ls ${args.path || "."}`;
|
| 40 |
+
default:
|
| 41 |
+
return `${call.name} ${JSON.stringify(args)}`;
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
function modelLabel(mode) {
|
| 46 |
+
if (mode === "mock") return "Deterministic test model";
|
| 47 |
+
return LOCAL_MODELS[mode]?.id || LOCAL_MODELS[DEFAULT_LOCAL_MODEL_KEY].id;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
function shortModelName(mode) {
|
| 51 |
+
if (mode === "mock") return "mock";
|
| 52 |
+
return LOCAL_MODELS[mode]?.name || LOCAL_MODELS[DEFAULT_LOCAL_MODEL_KEY].name;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
export function createPiCli({ terminal, sandbox, nodes, params }) {
|
| 56 |
+
let mode = "mock";
|
| 57 |
+
let device = "wasm";
|
| 58 |
+
let maxNewTokens = DEFAULT_MAX_NEW_TOKENS;
|
| 59 |
+
let temperature = 0;
|
| 60 |
+
let modelReady = false;
|
| 61 |
+
let status = "Ready";
|
| 62 |
+
let busy = false;
|
| 63 |
+
let input = "";
|
| 64 |
+
let renderedMessages = 0;
|
| 65 |
+
let outputText = "";
|
| 66 |
+
let sessionName = "untitled";
|
| 67 |
+
let sessionId = crypto.randomUUID();
|
| 68 |
+
let queuedLines = [];
|
| 69 |
+
let agent = null;
|
| 70 |
+
|
| 71 |
+
if (params.get("device")) device = params.get("device");
|
| 72 |
+
if (params.get("tokens")) maxNewTokens = Number(params.get("tokens")) || DEFAULT_MAX_NEW_TOKENS;
|
| 73 |
+
if (params.get("max_new_tokens")) maxNewTokens = Number(params.get("max_new_tokens")) || DEFAULT_MAX_NEW_TOKENS;
|
| 74 |
+
if (LOCAL_MODELS[params.get("model")]) mode = params.get("model");
|
| 75 |
+
if (LOCAL_MODELS[params.get("mode")]) mode = params.get("mode");
|
| 76 |
+
if (params.get("mode") === "mock") mode = "mock";
|
| 77 |
+
if (mode !== "mock" && !LOCAL_MODELS[mode]) mode = DEFAULT_LOCAL_MODEL_KEY;
|
| 78 |
+
if (!navigator.gpu && device === "webgpu") device = "wasm";
|
| 79 |
+
modelReady = mode === "mock";
|
| 80 |
+
|
| 81 |
+
function write(text) {
|
| 82 |
+
const value = String(text);
|
| 83 |
+
outputText += stripAnsi(value).replace(/\r/g, "");
|
| 84 |
+
if (outputText.length > 250000) outputText = outputText.slice(-200000);
|
| 85 |
+
terminal.write(value);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
function writeln(text = "") {
|
| 89 |
+
write(`${text}\n`);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
function setStatus(next) {
|
| 93 |
+
status = next;
|
| 94 |
+
nodes.status.textContent = next;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
function setModelStatus(next) {
|
| 98 |
+
nodes.modelStatus.textContent = next;
|
| 99 |
+
nodes.modelLabel.textContent = modelLabel(mode);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
function setSandboxStatus(next) {
|
| 103 |
+
nodes.sandboxStatus.textContent = next;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
function createAgent() {
|
| 107 |
+
const next = createPiAgent({
|
| 108 |
+
sandbox,
|
| 109 |
+
modelMode: () => mode,
|
| 110 |
+
device: () => device,
|
| 111 |
+
maxTokens: () => maxNewTokens,
|
| 112 |
+
temperature: () => temperature,
|
| 113 |
+
onModelStatus: setModelStatus,
|
| 114 |
+
});
|
| 115 |
+
next.subscribe((event) => {
|
| 116 |
+
switch (event.type) {
|
| 117 |
+
case "agent_start":
|
| 118 |
+
setStatus("Agent running");
|
| 119 |
+
break;
|
| 120 |
+
case "tool_execution_start":
|
| 121 |
+
writeln(`\x1b[2m[running ${event.toolName}]\x1b[22m`);
|
| 122 |
+
break;
|
| 123 |
+
case "message_end":
|
| 124 |
+
renderNewMessages();
|
| 125 |
+
break;
|
| 126 |
+
case "agent_end":
|
| 127 |
+
setStatus("Ready");
|
| 128 |
+
break;
|
| 129 |
+
default:
|
| 130 |
+
break;
|
| 131 |
+
}
|
| 132 |
+
});
|
| 133 |
+
return next;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
function resetAgent() {
|
| 137 |
+
agent?.abort();
|
| 138 |
+
agent = createAgent();
|
| 139 |
+
renderedMessages = 0;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
async function bootSandbox({ quiet = false } = {}) {
|
| 143 |
+
if (!quiet) writeln("\x1b[2mBooting WebContainer sandbox...\x1b[22m");
|
| 144 |
+
await sandbox.boot();
|
| 145 |
+
if (!quiet) writeln("\x1b[2mSandbox ready.\x1b[22m");
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
function showGate(message = "Ready.") {
|
| 149 |
+
nodes.gateStatus.textContent = message;
|
| 150 |
+
nodes.modelGate.classList.remove("hidden");
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
function hideGate() {
|
| 154 |
+
nodes.modelGate.classList.add("hidden");
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
async function loadModel() {
|
| 158 |
+
if (mode === "mock") {
|
| 159 |
+
modelReady = true;
|
| 160 |
+
setModelStatus("Deterministic test model");
|
| 161 |
+
hideGate();
|
| 162 |
+
return;
|
| 163 |
+
}
|
| 164 |
+
busy = true;
|
| 165 |
+
setStatus("Loading model");
|
| 166 |
+
nodes.gateStatus.textContent = "Booting sandbox...";
|
| 167 |
+
writeln(`\x1b[2mPreparing ${modelLabel(mode)} on ${device}...\x1b[22m`);
|
| 168 |
+
try {
|
| 169 |
+
await bootSandbox({ quiet: true });
|
| 170 |
+
nodes.gateStatus.textContent = "Downloading model...";
|
| 171 |
+
await agent.preloadModel();
|
| 172 |
+
modelReady = true;
|
| 173 |
+
setModelStatus("Model ready");
|
| 174 |
+
nodes.gateStatus.textContent = "Model ready.";
|
| 175 |
+
hideGate();
|
| 176 |
+
writeln("\x1b[32mModel ready.\x1b[0m");
|
| 177 |
+
} catch (error) {
|
| 178 |
+
const message = error.stack || error.message || String(error);
|
| 179 |
+
setModelStatus("Model error");
|
| 180 |
+
nodes.gateStatus.textContent = message;
|
| 181 |
+
writeln(`\x1b[31m${message}\x1b[0m`);
|
| 182 |
+
} finally {
|
| 183 |
+
busy = false;
|
| 184 |
+
setStatus("Ready");
|
| 185 |
+
writePrompt();
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
function writeStartup() {
|
| 190 |
+
writeln(`pi - AI coding assistant with read, bash, edit, write tools`);
|
| 191 |
+
writeln(`version ${PI_CLI_VERSION} | web terminal ${terminal.engine} | cwd /workspace`);
|
| 192 |
+
writeln(`shortcuts: /hotkeys | /model | /settings | /session | /help`);
|
| 193 |
+
writeln(`loaded AGENTS.md: none | prompt templates: none | skills: none | extensions: none`);
|
| 194 |
+
writeln(`tools: ${PI_DEFAULT_TOOLS.join(", ")} (read-only extras available: grep, find, ls)`);
|
| 195 |
+
if (!modelReady) {
|
| 196 |
+
writeln(`model: ${modelLabel(mode)} is not downloaded. Use the setup dialog or /model mock.`);
|
| 197 |
+
} else {
|
| 198 |
+
writeln(`model: ${modelLabel(mode)}`);
|
| 199 |
+
}
|
| 200 |
+
writeln();
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
function footer() {
|
| 204 |
+
return `cwd /workspace | session ${sessionName} | model ${shortModelName(mode)} | ${maxNewTokens} max tokens | ${status}`;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
function writePrompt() {
|
| 208 |
+
if (busy) return;
|
| 209 |
+
writeln(`\x1b[2m${footer()}\x1b[22m`);
|
| 210 |
+
write("> ");
|
| 211 |
+
terminal.focus();
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
function renderNewMessages() {
|
| 215 |
+
const messages = agent.state.messages;
|
| 216 |
+
while (renderedMessages < messages.length) {
|
| 217 |
+
const message = messages[renderedMessages];
|
| 218 |
+
renderedMessages += 1;
|
| 219 |
+
if (message.role === "user") continue;
|
| 220 |
+
if (message.role === "toolResult") {
|
| 221 |
+
const text = textFromContent(message.content);
|
| 222 |
+
const label = message.isError ? `[tool error: ${message.toolName}]` : `[tool: ${message.toolName}]`;
|
| 223 |
+
writeln(`\x1b[36m${label}\x1b[0m`);
|
| 224 |
+
if (text) writeln(text);
|
| 225 |
+
writeln();
|
| 226 |
+
continue;
|
| 227 |
+
}
|
| 228 |
+
const toolCalls = toolCallsFromMessage(message);
|
| 229 |
+
if (toolCalls.length > 0) {
|
| 230 |
+
writeln("\x1b[35mUsing pi tools:\x1b[0m");
|
| 231 |
+
for (const call of toolCalls) writeln(` ${formatToolCall(call)}`);
|
| 232 |
+
writeln();
|
| 233 |
+
continue;
|
| 234 |
+
}
|
| 235 |
+
const text = textFromContent(message.content);
|
| 236 |
+
if (text) {
|
| 237 |
+
writeln("\x1b[32mpi\x1b[0m");
|
| 238 |
+
writeln(text);
|
| 239 |
+
writeln();
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
async function runBashLine(command, sendToModel) {
|
| 245 |
+
busy = true;
|
| 246 |
+
setStatus("Running command");
|
| 247 |
+
try {
|
| 248 |
+
await bootSandbox({ quiet: true });
|
| 249 |
+
writeln(`$ ${command}`);
|
| 250 |
+
const result = await sandbox.bash(command, 120);
|
| 251 |
+
if (result.output) writeln(result.output);
|
| 252 |
+
writeln(`exit ${result.exitCode}`);
|
| 253 |
+
if (sendToModel) {
|
| 254 |
+
await promptAgent(`Command output from \`${command}\`:\n${result.output || "(no output)"}\nexit ${result.exitCode}`);
|
| 255 |
+
}
|
| 256 |
+
} catch (error) {
|
| 257 |
+
writeln(`\x1b[31m${error.stack || error.message || String(error)}\x1b[0m`);
|
| 258 |
+
} finally {
|
| 259 |
+
busy = false;
|
| 260 |
+
setStatus("Ready");
|
| 261 |
+
if (!sendToModel) writePrompt();
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
async function promptAgent(prompt) {
|
| 266 |
+
if (mode !== "mock" && !modelReady) {
|
| 267 |
+
writeln("Download the selected local model before sending, or use /model mock.");
|
| 268 |
+
showGate("Download the selected model before sending, or use the test model.");
|
| 269 |
+
writePrompt();
|
| 270 |
+
return;
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
busy = true;
|
| 274 |
+
setStatus("Agent running");
|
| 275 |
+
try {
|
| 276 |
+
await bootSandbox({ quiet: true });
|
| 277 |
+
await agent.prompt(prompt);
|
| 278 |
+
} catch (error) {
|
| 279 |
+
writeln(`\x1b[31m${error.stack || error.message || String(error)}\x1b[0m`);
|
| 280 |
+
} finally {
|
| 281 |
+
busy = false;
|
| 282 |
+
setStatus("Ready");
|
| 283 |
+
const next = queuedLines.shift();
|
| 284 |
+
if (next) {
|
| 285 |
+
writeln(`\x1b[2m[dequeued] ${next}\x1b[22m`);
|
| 286 |
+
await handleLine(next, { fromQueue: true });
|
| 287 |
+
} else {
|
| 288 |
+
writePrompt();
|
| 289 |
+
}
|
| 290 |
+
}
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
function printModels() {
|
| 294 |
+
writeln("Available browser-local models:");
|
| 295 |
+
for (const [key, value] of Object.entries(LOCAL_MODELS)) {
|
| 296 |
+
writeln(` ${key.padEnd(12)} ${value.id}`);
|
| 297 |
+
}
|
| 298 |
+
writeln(" mock deterministic test model");
|
| 299 |
+
writeln("Use /model <key> to switch. Non-mock models run in this browser through Transformers.js.");
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
function printSession() {
|
| 303 |
+
writeln(`Session`);
|
| 304 |
+
writeln(` id: ${sessionId}`);
|
| 305 |
+
writeln(` name: ${sessionName}`);
|
| 306 |
+
writeln(` messages: ${agent.state.messages.length}`);
|
| 307 |
+
writeln(` cwd: /workspace`);
|
| 308 |
+
writeln(` storage: browser memory`);
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
async function handleSlash(line) {
|
| 312 |
+
const [command, ...rest] = line.trim().split(/\s+/);
|
| 313 |
+
const argText = rest.join(" ");
|
| 314 |
+
switch (command) {
|
| 315 |
+
case "/help":
|
| 316 |
+
writeln(PI_HELP_TEXT);
|
| 317 |
+
break;
|
| 318 |
+
case "/hotkeys":
|
| 319 |
+
writeln(formatHotkeys());
|
| 320 |
+
break;
|
| 321 |
+
case "/model":
|
| 322 |
+
if (!argText) {
|
| 323 |
+
printModels();
|
| 324 |
+
break;
|
| 325 |
+
}
|
| 326 |
+
if (argText === "mock" || LOCAL_MODELS[argText]) {
|
| 327 |
+
mode = argText;
|
| 328 |
+
modelReady = mode === "mock";
|
| 329 |
+
resetAgent();
|
| 330 |
+
setModelStatus(modelReady ? "Deterministic test model" : "Model idle");
|
| 331 |
+
writeln(`model: ${modelLabel(mode)}`);
|
| 332 |
+
if (!modelReady) showGate("Download the selected model before sending.");
|
| 333 |
+
else hideGate();
|
| 334 |
+
} else {
|
| 335 |
+
writeln(`Unknown model: ${argText}`);
|
| 336 |
+
printModels();
|
| 337 |
+
}
|
| 338 |
+
break;
|
| 339 |
+
case "/settings":
|
| 340 |
+
if (argText.startsWith("device=")) {
|
| 341 |
+
device = argText.slice("device=".length) || device;
|
| 342 |
+
modelReady = mode === "mock";
|
| 343 |
+
resetAgent();
|
| 344 |
+
} else if (argText.startsWith("tokens=")) {
|
| 345 |
+
maxNewTokens = Number(argText.slice("tokens=".length)) || maxNewTokens;
|
| 346 |
+
} else if (argText.startsWith("temperature=")) {
|
| 347 |
+
temperature = Number(argText.slice("temperature=".length)) || 0;
|
| 348 |
+
}
|
| 349 |
+
writeln(`Settings`);
|
| 350 |
+
writeln(` device: ${device}`);
|
| 351 |
+
writeln(` max tokens: ${maxNewTokens}`);
|
| 352 |
+
writeln(` temperature: ${temperature}`);
|
| 353 |
+
writeln(` transport: browser-local Transformers.js`);
|
| 354 |
+
break;
|
| 355 |
+
case "/session":
|
| 356 |
+
printSession();
|
| 357 |
+
break;
|
| 358 |
+
case "/new":
|
| 359 |
+
resetAgent();
|
| 360 |
+
await sandbox.reset();
|
| 361 |
+
sessionId = crypto.randomUUID();
|
| 362 |
+
sessionName = "untitled";
|
| 363 |
+
writeln("Started a new browser session and reset the WebContainer workspace.");
|
| 364 |
+
break;
|
| 365 |
+
case "/name":
|
| 366 |
+
sessionName = argText || "untitled";
|
| 367 |
+
writeln(`Session name: ${sessionName}`);
|
| 368 |
+
break;
|
| 369 |
+
case "/tools":
|
| 370 |
+
writeln(`Default tools: ${PI_DEFAULT_TOOLS.join(", ")}`);
|
| 371 |
+
writeln(`Built-in tools: ${PI_BUILTIN_TOOLS.join(", ")}`);
|
| 372 |
+
break;
|
| 373 |
+
case "/clear":
|
| 374 |
+
terminal.clear();
|
| 375 |
+
outputText = "";
|
| 376 |
+
writeStartup();
|
| 377 |
+
break;
|
| 378 |
+
case "/login":
|
| 379 |
+
case "/logout":
|
| 380 |
+
writeln("This web port runs a local browser model, so provider login is not required. Use /model to switch local planners.");
|
| 381 |
+
break;
|
| 382 |
+
case "/resume":
|
| 383 |
+
case "/tree":
|
| 384 |
+
case "/fork":
|
| 385 |
+
case "/clone":
|
| 386 |
+
case "/compact":
|
| 387 |
+
case "/copy":
|
| 388 |
+
case "/export":
|
| 389 |
+
case "/share":
|
| 390 |
+
case "/reload":
|
| 391 |
+
case "/changelog":
|
| 392 |
+
case "/scoped-models":
|
| 393 |
+
writeln(`${command} is recognized from Pi CLI. In this static browser build it is represented in-memory; full filesystem-backed session management is a follow-up target.`);
|
| 394 |
+
break;
|
| 395 |
+
case "/quit":
|
| 396 |
+
writeln("Close this browser tab to quit the web terminal.");
|
| 397 |
+
break;
|
| 398 |
+
case "/commands":
|
| 399 |
+
writeln(PI_SLASH_COMMANDS.join("\n"));
|
| 400 |
+
break;
|
| 401 |
+
default:
|
| 402 |
+
writeln(`Unknown command: ${command}`);
|
| 403 |
+
writeln("Type /help or /commands.");
|
| 404 |
+
break;
|
| 405 |
+
}
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
async function handleLine(line, { fromQueue = false } = {}) {
|
| 409 |
+
const trimmed = line.trim();
|
| 410 |
+
if (!trimmed) {
|
| 411 |
+
writePrompt();
|
| 412 |
+
return;
|
| 413 |
+
}
|
| 414 |
+
if (busy && !fromQueue) {
|
| 415 |
+
queuedLines.push(line);
|
| 416 |
+
writeln(`\x1b[2m[queued] ${line}\x1b[22m`);
|
| 417 |
+
return;
|
| 418 |
+
}
|
| 419 |
+
if (trimmed.startsWith("/")) {
|
| 420 |
+
await handleSlash(trimmed);
|
| 421 |
+
writePrompt();
|
| 422 |
+
return;
|
| 423 |
+
}
|
| 424 |
+
if (trimmed.startsWith("!!")) {
|
| 425 |
+
await runBashLine(trimmed.slice(2).trim(), false);
|
| 426 |
+
return;
|
| 427 |
+
}
|
| 428 |
+
if (trimmed.startsWith("!")) {
|
| 429 |
+
await runBashLine(trimmed.slice(1).trim(), true);
|
| 430 |
+
return;
|
| 431 |
+
}
|
| 432 |
+
await promptAgent(line);
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
function redrawInput(nextInput) {
|
| 436 |
+
write("\r\x1b[2K> ");
|
| 437 |
+
input = nextInput;
|
| 438 |
+
write(input);
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
function handleData(data) {
|
| 442 |
+
for (const char of data) {
|
| 443 |
+
if (char === "\r") {
|
| 444 |
+
const submitted = input;
|
| 445 |
+
input = "";
|
| 446 |
+
writeln();
|
| 447 |
+
void handleLine(submitted);
|
| 448 |
+
} else if (char === "\u007f") {
|
| 449 |
+
if (input.length > 0) {
|
| 450 |
+
input = input.slice(0, -1);
|
| 451 |
+
write("\b \b");
|
| 452 |
+
}
|
| 453 |
+
} else if (char === "\x03") {
|
| 454 |
+
if (busy) {
|
| 455 |
+
agent.abort();
|
| 456 |
+
writeln("^C");
|
| 457 |
+
setStatus("Aborted");
|
| 458 |
+
} else if (input) {
|
| 459 |
+
redrawInput("");
|
| 460 |
+
} else {
|
| 461 |
+
writeln("^C");
|
| 462 |
+
writeln("Use /quit or close this browser tab to exit.");
|
| 463 |
+
writePrompt();
|
| 464 |
+
}
|
| 465 |
+
} else if (char === "\x1b") {
|
| 466 |
+
if (busy) agent.abort();
|
| 467 |
+
} else if (char >= " " && char !== "\x7f") {
|
| 468 |
+
input += char;
|
| 469 |
+
write(char);
|
| 470 |
+
}
|
| 471 |
+
}
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
nodes.confirmLoadModel.addEventListener("click", async () => {
|
| 475 |
+
device = nodes.gateDevice.value;
|
| 476 |
+
await loadModel();
|
| 477 |
+
});
|
| 478 |
+
nodes.useTestModel.addEventListener("click", () => {
|
| 479 |
+
mode = "mock";
|
| 480 |
+
modelReady = true;
|
| 481 |
+
resetAgent();
|
| 482 |
+
setModelStatus("Deterministic test model");
|
| 483 |
+
hideGate();
|
| 484 |
+
writeln("model: deterministic test model");
|
| 485 |
+
writePrompt();
|
| 486 |
+
});
|
| 487 |
+
|
| 488 |
+
resetAgent();
|
| 489 |
+
setStatus("Ready");
|
| 490 |
+
setSandboxStatus("Not booted");
|
| 491 |
+
setModelStatus(modelReady ? "Deterministic test model" : "Model idle");
|
| 492 |
+
nodes.gateDevice.value = device;
|
| 493 |
+
if (modelReady || params.get("setup") === "skip") hideGate();
|
| 494 |
+
else showGate("Ready.");
|
| 495 |
+
|
| 496 |
+
terminal.onData(handleData);
|
| 497 |
+
writeStartup();
|
| 498 |
+
writePrompt();
|
| 499 |
+
|
| 500 |
+
return {
|
| 501 |
+
loadModel,
|
| 502 |
+
handleLine,
|
| 503 |
+
get outputText() {
|
| 504 |
+
return outputText;
|
| 505 |
+
},
|
| 506 |
+
get transcript() {
|
| 507 |
+
return agent.state.messages
|
| 508 |
+
.map((message) => {
|
| 509 |
+
if (message.role === "toolResult") return `TOOL ${message.toolName}\n${textFromContent(message.content)}`;
|
| 510 |
+
return `${message.role.toUpperCase()}\n${textFromContent(message.content)}`;
|
| 511 |
+
})
|
| 512 |
+
.join("\n\n");
|
| 513 |
+
},
|
| 514 |
+
get modelReady() {
|
| 515 |
+
return modelReady;
|
| 516 |
+
},
|
| 517 |
+
get status() {
|
| 518 |
+
return status;
|
| 519 |
+
},
|
| 520 |
+
get mode() {
|
| 521 |
+
return mode;
|
| 522 |
+
},
|
| 523 |
+
};
|
| 524 |
+
}
|
src/piCliContract.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const PI_CLI_VERSION = "0.77.0";
|
| 2 |
+
|
| 3 |
+
export const PI_DEFAULT_TOOLS = ["read", "bash", "edit", "write"];
|
| 4 |
+
export const PI_BUILTIN_TOOLS = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
| 5 |
+
|
| 6 |
+
export const PI_SLASH_COMMANDS = [
|
| 7 |
+
"/login",
|
| 8 |
+
"/logout",
|
| 9 |
+
"/model",
|
| 10 |
+
"/scoped-models",
|
| 11 |
+
"/settings",
|
| 12 |
+
"/resume",
|
| 13 |
+
"/new",
|
| 14 |
+
"/name <name>",
|
| 15 |
+
"/session",
|
| 16 |
+
"/tree",
|
| 17 |
+
"/fork",
|
| 18 |
+
"/clone",
|
| 19 |
+
"/compact [prompt]",
|
| 20 |
+
"/copy",
|
| 21 |
+
"/export [file]",
|
| 22 |
+
"/share",
|
| 23 |
+
"/reload",
|
| 24 |
+
"/hotkeys",
|
| 25 |
+
"/changelog",
|
| 26 |
+
"/quit",
|
| 27 |
+
];
|
| 28 |
+
|
| 29 |
+
export const PI_HELP_TEXT = `pi - AI coding assistant with read, bash, edit, write tools
|
| 30 |
+
|
| 31 |
+
Usage:
|
| 32 |
+
pi [options] [@files...] [messages...]
|
| 33 |
+
|
| 34 |
+
Commands:
|
| 35 |
+
pi install <source> [-l] Install extension source and add to settings
|
| 36 |
+
pi remove <source> [-l] Remove extension source from settings
|
| 37 |
+
pi uninstall <source> [-l] Alias for remove
|
| 38 |
+
pi update [source|self|pi] Update pi and installed extensions
|
| 39 |
+
pi list List installed extensions from settings
|
| 40 |
+
pi config Open TUI to enable/disable package resources
|
| 41 |
+
pi <command> --help Show help for install/remove/uninstall/update/list
|
| 42 |
+
|
| 43 |
+
Options:
|
| 44 |
+
--provider <name> Provider name (default: google)
|
| 45 |
+
--model <pattern> Model pattern or ID (supports "provider/id" and optional ":<thinking>")
|
| 46 |
+
--api-key <key> API key (defaults to env vars)
|
| 47 |
+
--system-prompt <text> System prompt (default: coding assistant prompt)
|
| 48 |
+
--append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)
|
| 49 |
+
--mode <mode> Output mode: text (default), json, or rpc
|
| 50 |
+
--print, -p Non-interactive mode: process prompt and exit
|
| 51 |
+
--continue, -c Continue previous session
|
| 52 |
+
--resume, -r Select a session to resume
|
| 53 |
+
--session <path|id> Use specific session file or partial UUID
|
| 54 |
+
--session-id <id> Use exact project session ID, creating it if missing
|
| 55 |
+
--fork <path|id> Fork specific session file or partial UUID into a new session
|
| 56 |
+
--session-dir <dir> Directory for session storage and lookup
|
| 57 |
+
--no-session Don't save session (ephemeral)
|
| 58 |
+
--models <patterns> Comma-separated model patterns for Ctrl+P cycling
|
| 59 |
+
--no-tools, -nt Disable all tools by default (built-in and extension)
|
| 60 |
+
--no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled
|
| 61 |
+
--tools, -t <tools> Comma-separated allowlist of tool names to enable
|
| 62 |
+
--exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable
|
| 63 |
+
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
| 64 |
+
--extension, -e <path> Load an extension file (can be used multiple times)
|
| 65 |
+
--no-extensions, -ne Disable extension discovery (explicit -e paths still work)
|
| 66 |
+
--skill <path> Load a skill file or directory (can be used multiple times)
|
| 67 |
+
--prompt-template <path> Load a prompt template file or directory (can be used multiple times)
|
| 68 |
+
--theme <path> Load a theme file or directory (can be used multiple times)
|
| 69 |
+
--no-themes Disable theme discovery and loading
|
| 70 |
+
--no-context-files, -nc Disable AGENTS.md and CLAUDE.md discovery and loading
|
| 71 |
+
--export <file> Export session file to HTML and exit
|
| 72 |
+
--list-models [search] List available models (with optional fuzzy search)
|
| 73 |
+
--verbose Force verbose startup (overrides quietStartup setting)
|
| 74 |
+
--offline Disable startup network operations (same as PI_OFFLINE=1)
|
| 75 |
+
--help, -h Show this help
|
| 76 |
+
--version, -v Show version number
|
| 77 |
+
|
| 78 |
+
Built-in Tool Names:
|
| 79 |
+
read - Read file contents
|
| 80 |
+
bash - Execute bash commands
|
| 81 |
+
edit - Edit files with find/replace
|
| 82 |
+
write - Write files (creates/overwrites)
|
| 83 |
+
grep - Search file contents (read-only, off by default)
|
| 84 |
+
find - Find files by glob pattern (read-only, off by default)
|
| 85 |
+
ls - List directory contents (read-only, off by default)`;
|
| 86 |
+
|
| 87 |
+
export function formatHotkeys() {
|
| 88 |
+
return `Common Pi keybindings
|
| 89 |
+
|
| 90 |
+
Ctrl+C Clear editor
|
| 91 |
+
Ctrl+C twice Quit
|
| 92 |
+
Escape Cancel or abort
|
| 93 |
+
Escape twice Open /tree
|
| 94 |
+
Ctrl+L Open /model
|
| 95 |
+
Ctrl+P Cycle scoped models forward
|
| 96 |
+
Shift+Ctrl+P Cycle scoped models backward
|
| 97 |
+
Shift+Tab Cycle thinking level
|
| 98 |
+
Ctrl+O Collapse or expand tool output
|
| 99 |
+
Ctrl+T Collapse or expand thinking blocks
|
| 100 |
+
Shift+Enter Multi-line input`;
|
| 101 |
+
}
|
src/sandbox.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const BOOT_FILES = {
|
| 2 |
+
"package.json": {
|
| 3 |
+
file: {
|
| 4 |
+
contents: JSON.stringify(
|
| 5 |
+
{
|
| 6 |
+
type: "module",
|
| 7 |
+
scripts: {
|
| 8 |
+
demo: "node hello.js",
|
| 9 |
+
},
|
| 10 |
+
dependencies: {},
|
| 11 |
+
devDependencies: {},
|
| 12 |
+
},
|
| 13 |
+
null,
|
| 14 |
+
2,
|
| 15 |
+
),
|
| 16 |
+
},
|
| 17 |
+
},
|
| 18 |
+
"README.md": {
|
| 19 |
+
file: {
|
| 20 |
+
contents:
|
| 21 |
+
"# Pi web sandbox\n\nThis filesystem and every spawned process run inside WebContainers in this browser tab.\n",
|
| 22 |
+
},
|
| 23 |
+
},
|
| 24 |
+
"hello.js": {
|
| 25 |
+
file: {
|
| 26 |
+
contents: 'console.log("hello from the browser sandbox");\nconsole.log(2 + 2);\n',
|
| 27 |
+
},
|
| 28 |
+
},
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
const MAX_OUTPUT_CHARS = 16000;
|
| 32 |
+
const DEFAULT_TIMEOUT_MS = 10000;
|
| 33 |
+
const DEFAULT_MAX_READ_LINES = 2000;
|
| 34 |
+
const DEFAULT_RESULT_LIMIT = 200;
|
| 35 |
+
|
| 36 |
+
function splitCommandLine(value) {
|
| 37 |
+
const parts = [];
|
| 38 |
+
let current = "";
|
| 39 |
+
let quote = "";
|
| 40 |
+
let escaping = false;
|
| 41 |
+
for (const char of String(value || "")) {
|
| 42 |
+
if (escaping) {
|
| 43 |
+
current += char;
|
| 44 |
+
escaping = false;
|
| 45 |
+
continue;
|
| 46 |
+
}
|
| 47 |
+
if (char === "\\") {
|
| 48 |
+
escaping = true;
|
| 49 |
+
continue;
|
| 50 |
+
}
|
| 51 |
+
if (quote) {
|
| 52 |
+
if (char === quote) {
|
| 53 |
+
quote = "";
|
| 54 |
+
} else {
|
| 55 |
+
current += char;
|
| 56 |
+
}
|
| 57 |
+
continue;
|
| 58 |
+
}
|
| 59 |
+
if (char === "\"" || char === "'") {
|
| 60 |
+
quote = char;
|
| 61 |
+
} else if (/\s/.test(char)) {
|
| 62 |
+
if (current) {
|
| 63 |
+
parts.push(current);
|
| 64 |
+
current = "";
|
| 65 |
+
}
|
| 66 |
+
} else {
|
| 67 |
+
current += char;
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
if (current) parts.push(current);
|
| 71 |
+
return parts;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function escapeRegExp(value) {
|
| 75 |
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function globToRegExp(glob) {
|
| 79 |
+
const source = String(glob || "*")
|
| 80 |
+
.split("*")
|
| 81 |
+
.map(escapeRegExp)
|
| 82 |
+
.join(".*");
|
| 83 |
+
return new RegExp(`^${source}$`);
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
export function createSandbox({ onLog = () => {}, onStatus = () => {} } = {}) {
|
| 87 |
+
let instance = null;
|
| 88 |
+
let booting = null;
|
| 89 |
+
let root = "";
|
| 90 |
+
|
| 91 |
+
async function boot() {
|
| 92 |
+
if (instance) return instance;
|
| 93 |
+
if (booting) return booting;
|
| 94 |
+
|
| 95 |
+
booting = (async () => {
|
| 96 |
+
if (!globalThis.crossOriginIsolated) {
|
| 97 |
+
throw new Error("WebContainers need cross-origin isolation. Start this app through Vite or another server that sends COOP/COEP headers.");
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
onStatus("Booting WebContainer");
|
| 101 |
+
const { WebContainer } = await import("@webcontainer/api");
|
| 102 |
+
instance = await WebContainer.boot({
|
| 103 |
+
coep: "credentialless",
|
| 104 |
+
workdirName: "workspace",
|
| 105 |
+
});
|
| 106 |
+
root = instance.workdir;
|
| 107 |
+
await instance.mount(BOOT_FILES);
|
| 108 |
+
onLog(`sandbox booted at ${root}`);
|
| 109 |
+
onStatus("Sandbox ready");
|
| 110 |
+
return instance;
|
| 111 |
+
})();
|
| 112 |
+
|
| 113 |
+
try {
|
| 114 |
+
return await booting;
|
| 115 |
+
} finally {
|
| 116 |
+
booting = null;
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
function assertRelativePath(path, { allowDot = false } = {}) {
|
| 121 |
+
let value = String(path ?? (allowDot ? "." : "")).trim();
|
| 122 |
+
if (value === "" && allowDot) value = ".";
|
| 123 |
+
value = value.replace(/^\/+/, "").replace(/^workspace\/?/, "");
|
| 124 |
+
if (value === "." && allowDot) return ".";
|
| 125 |
+
if (!value || value.includes("\0")) throw new Error("Path is required.");
|
| 126 |
+
const segments = value.split("/").filter(Boolean);
|
| 127 |
+
if (segments.some((segment) => segment === "..")) {
|
| 128 |
+
throw new Error("Paths must stay inside the sandbox workspace.");
|
| 129 |
+
}
|
| 130 |
+
const normalized = segments.filter((segment) => segment !== ".").join("/");
|
| 131 |
+
if (!normalized && allowDot) return ".";
|
| 132 |
+
if (!normalized) throw new Error("Path is required.");
|
| 133 |
+
return normalized;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
function toWorkspacePath(path, options) {
|
| 137 |
+
return assertRelativePath(path, options);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
async function pathExists(path) {
|
| 141 |
+
const wc = await boot();
|
| 142 |
+
try {
|
| 143 |
+
await wc.fs.stat(toWorkspacePath(path, { allowDot: true }));
|
| 144 |
+
return true;
|
| 145 |
+
} catch {
|
| 146 |
+
return false;
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
async function reset() {
|
| 151 |
+
const wc = await boot();
|
| 152 |
+
const entries = await wc.fs.readdir(".");
|
| 153 |
+
await Promise.all(
|
| 154 |
+
entries.map((entry) =>
|
| 155 |
+
wc.fs.rm(entry, {
|
| 156 |
+
force: true,
|
| 157 |
+
recursive: true,
|
| 158 |
+
}),
|
| 159 |
+
),
|
| 160 |
+
);
|
| 161 |
+
await wc.mount(BOOT_FILES);
|
| 162 |
+
onLog("sandbox reset");
|
| 163 |
+
return "Sandbox reset to the starter project.";
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
async function walkFiles(start = ".") {
|
| 167 |
+
const wc = await boot();
|
| 168 |
+
const rootPath = toWorkspacePath(start, { allowDot: true });
|
| 169 |
+
const files = [];
|
| 170 |
+
|
| 171 |
+
async function walk(current) {
|
| 172 |
+
const entries = await wc.fs.readdir(current, { withFileTypes: true });
|
| 173 |
+
for (const entry of entries) {
|
| 174 |
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
| 175 |
+
const child = current === "." ? entry.name : `${current}/${entry.name}`;
|
| 176 |
+
if (entry.isDirectory()) {
|
| 177 |
+
await walk(child);
|
| 178 |
+
} else {
|
| 179 |
+
files.push(child);
|
| 180 |
+
}
|
| 181 |
+
}
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
const stat = await wc.fs.stat(rootPath);
|
| 185 |
+
if (stat.isDirectory()) {
|
| 186 |
+
await walk(rootPath);
|
| 187 |
+
} else {
|
| 188 |
+
files.push(rootPath);
|
| 189 |
+
}
|
| 190 |
+
return files;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
async function ls(path = ".", limit = DEFAULT_RESULT_LIMIT) {
|
| 194 |
+
const wc = await boot();
|
| 195 |
+
const target = toWorkspacePath(path || ".", { allowDot: true });
|
| 196 |
+
const stat = await wc.fs.stat(target);
|
| 197 |
+
if (!stat.isDirectory()) return `file ${target}`;
|
| 198 |
+
const entries = await wc.fs.readdir(target, { withFileTypes: true });
|
| 199 |
+
const capped = entries.slice(0, Number(limit || DEFAULT_RESULT_LIMIT));
|
| 200 |
+
const lines = capped.map((entry) => `${entry.isDirectory() ? "dir " : "file"} ${entry.name}`);
|
| 201 |
+
if (entries.length > capped.length) {
|
| 202 |
+
lines.push(`[Showing ${capped.length} of ${entries.length} entries. Use a larger limit to continue.]`);
|
| 203 |
+
}
|
| 204 |
+
return lines.join("\n") || "(empty)";
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
async function readTextFile(path, { offset, limit } = {}) {
|
| 208 |
+
const wc = await boot();
|
| 209 |
+
const target = toWorkspacePath(path);
|
| 210 |
+
const text = await wc.fs.readFile(target, "utf-8");
|
| 211 |
+
const lines = text.split("\n");
|
| 212 |
+
const start = Math.max(0, Number(offset || 1) - 1);
|
| 213 |
+
if (start >= lines.length) throw new Error(`Offset ${offset} is beyond end of file (${lines.length} lines total)`);
|
| 214 |
+
const maxLines = Number(limit || DEFAULT_MAX_READ_LINES);
|
| 215 |
+
const selected = lines.slice(start, start + maxLines);
|
| 216 |
+
let output = selected.join("\n");
|
| 217 |
+
if (start + selected.length < lines.length) {
|
| 218 |
+
output += `\n\n[${lines.length - start - selected.length} more lines in file. Use offset=${start + selected.length + 1} to continue.]`;
|
| 219 |
+
}
|
| 220 |
+
return output;
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
async function writeFile(path, content) {
|
| 224 |
+
const wc = await boot();
|
| 225 |
+
const relative = assertRelativePath(path);
|
| 226 |
+
const parent = relative.split("/").slice(0, -1).join("/");
|
| 227 |
+
if (parent) {
|
| 228 |
+
await wc.fs.mkdir(parent, { recursive: true });
|
| 229 |
+
}
|
| 230 |
+
await wc.fs.writeFile(relative, String(content ?? ""));
|
| 231 |
+
onLog(`wrote ${relative}`);
|
| 232 |
+
return `Successfully wrote ${String(content ?? "").length} bytes to ${relative}`;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
async function editFile(path, edits = []) {
|
| 236 |
+
const wc = await boot();
|
| 237 |
+
const target = toWorkspacePath(path);
|
| 238 |
+
if (!Array.isArray(edits) || edits.length === 0) {
|
| 239 |
+
throw new Error("Edit tool input is invalid. edits must contain at least one replacement.");
|
| 240 |
+
}
|
| 241 |
+
const original = await wc.fs.readFile(target, "utf-8");
|
| 242 |
+
const replacements = [];
|
| 243 |
+
for (const edit of edits) {
|
| 244 |
+
const oldText = String(edit?.oldText ?? "");
|
| 245 |
+
const newText = String(edit?.newText ?? "");
|
| 246 |
+
if (!oldText) throw new Error("Every edit.oldText must be non-empty.");
|
| 247 |
+
const first = original.indexOf(oldText);
|
| 248 |
+
if (first === -1) throw new Error(`Could not edit file: ${path}. oldText was not found.`);
|
| 249 |
+
if (original.indexOf(oldText, first + oldText.length) !== -1) {
|
| 250 |
+
throw new Error(`Could not edit file: ${path}. oldText must match exactly one location.`);
|
| 251 |
+
}
|
| 252 |
+
replacements.push({ start: first, end: first + oldText.length, newText });
|
| 253 |
+
}
|
| 254 |
+
replacements.sort((a, b) => b.start - a.start);
|
| 255 |
+
for (let index = 1; index < replacements.length; index += 1) {
|
| 256 |
+
if (replacements[index].end > replacements[index - 1].start) {
|
| 257 |
+
throw new Error("Edit tool input is invalid. edits must not overlap.");
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
let next = original;
|
| 261 |
+
for (const replacement of replacements) {
|
| 262 |
+
next = `${next.slice(0, replacement.start)}${replacement.newText}${next.slice(replacement.end)}`;
|
| 263 |
+
}
|
| 264 |
+
await wc.fs.writeFile(target, next);
|
| 265 |
+
onLog(`edited ${target}`);
|
| 266 |
+
return `Successfully replaced ${edits.length} block(s) in ${path}.`;
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
async function findFiles(pattern = "*", path = ".", limit = DEFAULT_RESULT_LIMIT) {
|
| 270 |
+
const start = toWorkspacePath(path || ".", { allowDot: true });
|
| 271 |
+
if (!(await pathExists(start))) throw new Error(`Path does not exist: ${path || "."}`);
|
| 272 |
+
const matcher = globToRegExp(pattern);
|
| 273 |
+
const files = await walkFiles(start);
|
| 274 |
+
const matches = files.filter((file) => matcher.test(file) || matcher.test(file.split("/").pop() || ""));
|
| 275 |
+
const capped = matches.slice(0, Number(limit || DEFAULT_RESULT_LIMIT));
|
| 276 |
+
if (matches.length > capped.length) capped.push(`[Showing ${capped.length} of ${matches.length} matches. Use a larger limit to continue.]`);
|
| 277 |
+
return capped.join("\n") || "(no matches)";
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
async function grepFiles({ pattern, path = ".", glob = "*", ignoreCase = false, literal = false, context = 0, limit = DEFAULT_RESULT_LIMIT }) {
|
| 281 |
+
if (!pattern) throw new Error("pattern is required.");
|
| 282 |
+
const flags = ignoreCase ? "i" : "";
|
| 283 |
+
const matcher = literal ? new RegExp(escapeRegExp(pattern), flags) : new RegExp(pattern, flags);
|
| 284 |
+
const globMatcher = globToRegExp(glob || "*");
|
| 285 |
+
const files = (await walkFiles(path || ".")).filter((file) => globMatcher.test(file) || globMatcher.test(file.split("/").pop() || ""));
|
| 286 |
+
const matches = [];
|
| 287 |
+
for (const file of files) {
|
| 288 |
+
let text = "";
|
| 289 |
+
try {
|
| 290 |
+
text = await readTextFile(file, { limit: DEFAULT_MAX_READ_LINES });
|
| 291 |
+
} catch {
|
| 292 |
+
continue;
|
| 293 |
+
}
|
| 294 |
+
const lines = text.split("\n");
|
| 295 |
+
for (let index = 0; index < lines.length; index += 1) {
|
| 296 |
+
if (!matcher.test(lines[index])) continue;
|
| 297 |
+
matcher.lastIndex = 0;
|
| 298 |
+
const before = Math.max(0, index - Number(context || 0));
|
| 299 |
+
const after = Math.min(lines.length - 1, index + Number(context || 0));
|
| 300 |
+
for (let lineIndex = before; lineIndex <= after; lineIndex += 1) {
|
| 301 |
+
matches.push(`${file}:${lineIndex + 1}:${lines[lineIndex]}`);
|
| 302 |
+
if (matches.length >= Number(limit || DEFAULT_RESULT_LIMIT)) {
|
| 303 |
+
matches.push(`[Showing first ${matches.length - 1} matches. Use a larger limit to continue.]`);
|
| 304 |
+
return matches.join("\n");
|
| 305 |
+
}
|
| 306 |
+
}
|
| 307 |
+
}
|
| 308 |
+
}
|
| 309 |
+
return matches.join("\n") || "(no matches)";
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
async function spawnProcess(command, args = [], timeoutMs = DEFAULT_TIMEOUT_MS) {
|
| 313 |
+
const wc = await boot();
|
| 314 |
+
const cmd = String(command || "").trim();
|
| 315 |
+
if (!cmd) throw new Error("Command is required.");
|
| 316 |
+
const cleanArgs = Array.isArray(args) ? args.map((arg) => String(arg)) : [];
|
| 317 |
+
|
| 318 |
+
onLog(`$ ${[cmd, ...cleanArgs].join(" ")}`);
|
| 319 |
+
const process = await wc.spawn(cmd, cleanArgs, {
|
| 320 |
+
terminal: {
|
| 321 |
+
cols: 96,
|
| 322 |
+
rows: 28,
|
| 323 |
+
},
|
| 324 |
+
});
|
| 325 |
+
|
| 326 |
+
let output = "";
|
| 327 |
+
const reader = process.output.getReader();
|
| 328 |
+
const pump = (async () => {
|
| 329 |
+
while (true) {
|
| 330 |
+
const { done, value } = await reader.read();
|
| 331 |
+
if (done) break;
|
| 332 |
+
output += value;
|
| 333 |
+
if (output.length > MAX_OUTPUT_CHARS) {
|
| 334 |
+
output = `${output.slice(0, MAX_OUTPUT_CHARS)}\n[output truncated]`;
|
| 335 |
+
process.kill();
|
| 336 |
+
break;
|
| 337 |
+
}
|
| 338 |
+
}
|
| 339 |
+
})();
|
| 340 |
+
|
| 341 |
+
const timeout = new Promise((resolve) => {
|
| 342 |
+
setTimeout(() => {
|
| 343 |
+
process.kill();
|
| 344 |
+
resolve("timeout");
|
| 345 |
+
}, Number(timeoutMs) || DEFAULT_TIMEOUT_MS);
|
| 346 |
+
});
|
| 347 |
+
|
| 348 |
+
const exitCode = await Promise.race([process.exit, timeout]);
|
| 349 |
+
await pump.catch(() => {});
|
| 350 |
+
const normalizedExitCode = exitCode === "timeout" ? 124 : exitCode;
|
| 351 |
+
const result = {
|
| 352 |
+
command: [cmd, ...cleanArgs].join(" "),
|
| 353 |
+
exitCode: normalizedExitCode,
|
| 354 |
+
output: output.trimEnd(),
|
| 355 |
+
};
|
| 356 |
+
onLog(`exit ${normalizedExitCode}`);
|
| 357 |
+
return result;
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
async function bash(command, timeoutSeconds) {
|
| 361 |
+
const timeoutMs = timeoutSeconds ? Number(timeoutSeconds) * 1000 : DEFAULT_TIMEOUT_MS;
|
| 362 |
+
try {
|
| 363 |
+
return await spawnProcess("jsh", ["-c", command], timeoutMs);
|
| 364 |
+
} catch (error) {
|
| 365 |
+
const parts = splitCommandLine(command);
|
| 366 |
+
if (parts.length === 0) throw error;
|
| 367 |
+
return await spawnProcess(parts[0], parts.slice(1), timeoutMs);
|
| 368 |
+
}
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
return {
|
| 372 |
+
boot,
|
| 373 |
+
reset,
|
| 374 |
+
bash,
|
| 375 |
+
editFile,
|
| 376 |
+
findFiles,
|
| 377 |
+
grepFiles,
|
| 378 |
+
ls,
|
| 379 |
+
listFiles: ls,
|
| 380 |
+
readFile: (path) => readTextFile(path),
|
| 381 |
+
readTextFile,
|
| 382 |
+
writeFile,
|
| 383 |
+
runCommand: spawnProcess,
|
| 384 |
+
get isReady() {
|
| 385 |
+
return Boolean(instance);
|
| 386 |
+
},
|
| 387 |
+
};
|
| 388 |
+
}
|
src/styles.css
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
color: #d6deeb;
|
| 3 |
+
background: #0b0f16;
|
| 4 |
+
font-family:
|
| 5 |
+
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
* {
|
| 9 |
+
box-sizing: border-box;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
html,
|
| 13 |
+
body,
|
| 14 |
+
#app {
|
| 15 |
+
min-width: 320px;
|
| 16 |
+
min-height: 100vh;
|
| 17 |
+
margin: 0;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
button,
|
| 21 |
+
select {
|
| 22 |
+
font: inherit;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
button {
|
| 26 |
+
min-height: 40px;
|
| 27 |
+
padding: 0 15px;
|
| 28 |
+
border: 0;
|
| 29 |
+
border-radius: 6px;
|
| 30 |
+
background: #227c70;
|
| 31 |
+
color: #ffffff;
|
| 32 |
+
font-weight: 750;
|
| 33 |
+
cursor: pointer;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
button:disabled {
|
| 37 |
+
cursor: wait;
|
| 38 |
+
opacity: 0.6;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
#app {
|
| 42 |
+
display: grid;
|
| 43 |
+
grid-template-rows: auto minmax(0, 1fr);
|
| 44 |
+
gap: 10px;
|
| 45 |
+
padding: 10px;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.runtime-bar {
|
| 49 |
+
display: flex;
|
| 50 |
+
align-items: end;
|
| 51 |
+
justify-content: space-between;
|
| 52 |
+
gap: 16px;
|
| 53 |
+
min-height: 54px;
|
| 54 |
+
padding: 4px 2px 10px;
|
| 55 |
+
border-bottom: 1px solid #263244;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
h1,
|
| 59 |
+
h2,
|
| 60 |
+
p {
|
| 61 |
+
margin: 0;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
h1 {
|
| 65 |
+
color: #f8fafc;
|
| 66 |
+
font-size: 30px;
|
| 67 |
+
line-height: 1;
|
| 68 |
+
letter-spacing: 0;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
#model-label {
|
| 72 |
+
margin-top: 5px;
|
| 73 |
+
color: #9eb1ca;
|
| 74 |
+
font-size: 13px;
|
| 75 |
+
overflow-wrap: anywhere;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.status-stack {
|
| 79 |
+
display: flex;
|
| 80 |
+
flex-wrap: wrap;
|
| 81 |
+
justify-content: end;
|
| 82 |
+
gap: 8px;
|
| 83 |
+
max-width: 760px;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.status {
|
| 87 |
+
min-height: 32px;
|
| 88 |
+
max-width: 280px;
|
| 89 |
+
padding: 6px 10px;
|
| 90 |
+
border: 1px solid #344156;
|
| 91 |
+
border-radius: 6px;
|
| 92 |
+
background: #141b27;
|
| 93 |
+
color: #cad7ea;
|
| 94 |
+
font-size: 13px;
|
| 95 |
+
overflow-wrap: anywhere;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.terminal-shell {
|
| 99 |
+
min-height: 0;
|
| 100 |
+
border: 1px solid #263244;
|
| 101 |
+
border-radius: 8px;
|
| 102 |
+
background: #10141c;
|
| 103 |
+
overflow: hidden;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
#terminal {
|
| 107 |
+
width: 100%;
|
| 108 |
+
height: calc(100vh - 86px);
|
| 109 |
+
min-height: 420px;
|
| 110 |
+
padding: 10px;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.xterm {
|
| 114 |
+
height: 100%;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
.xterm-viewport,
|
| 118 |
+
.xterm-screen {
|
| 119 |
+
border-radius: 0;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.model-gate {
|
| 123 |
+
position: fixed;
|
| 124 |
+
inset: 0;
|
| 125 |
+
display: grid;
|
| 126 |
+
place-items: center;
|
| 127 |
+
padding: 18px;
|
| 128 |
+
background: rgba(4, 8, 14, 0.64);
|
| 129 |
+
z-index: 20;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
.model-gate.hidden {
|
| 133 |
+
display: none;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.model-dialog {
|
| 137 |
+
display: grid;
|
| 138 |
+
gap: 16px;
|
| 139 |
+
width: min(560px, 100%);
|
| 140 |
+
padding: 22px;
|
| 141 |
+
border: 1px solid #334155;
|
| 142 |
+
border-radius: 8px;
|
| 143 |
+
background: #f8fafc;
|
| 144 |
+
color: #172033;
|
| 145 |
+
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.eyebrow {
|
| 149 |
+
margin-bottom: 7px;
|
| 150 |
+
color: #227c70;
|
| 151 |
+
font-size: 12px;
|
| 152 |
+
font-weight: 850;
|
| 153 |
+
text-transform: uppercase;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.model-dialog h2 {
|
| 157 |
+
color: #172033;
|
| 158 |
+
font-size: 24px;
|
| 159 |
+
line-height: 1.15;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.dialog-copy {
|
| 163 |
+
margin-top: 8px;
|
| 164 |
+
color: #526173;
|
| 165 |
+
line-height: 1.5;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.dialog-actions {
|
| 169 |
+
display: flex;
|
| 170 |
+
flex-wrap: wrap;
|
| 171 |
+
gap: 10px;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
#use-test-model {
|
| 175 |
+
background: #4f5f73;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.dialog-options {
|
| 179 |
+
display: grid;
|
| 180 |
+
gap: 10px;
|
| 181 |
+
color: #526173;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
label {
|
| 185 |
+
display: grid;
|
| 186 |
+
gap: 6px;
|
| 187 |
+
font-size: 13px;
|
| 188 |
+
font-weight: 700;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
select {
|
| 192 |
+
width: 100%;
|
| 193 |
+
height: 40px;
|
| 194 |
+
padding: 0 10px;
|
| 195 |
+
border: 1px solid #b8c5d4;
|
| 196 |
+
border-radius: 6px;
|
| 197 |
+
background: #ffffff;
|
| 198 |
+
color: #152033;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
#gate-status {
|
| 202 |
+
min-height: 22px;
|
| 203 |
+
color: #526173;
|
| 204 |
+
overflow-wrap: anywhere;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
@media (max-width: 760px) {
|
| 208 |
+
#app {
|
| 209 |
+
padding: 8px;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
.runtime-bar {
|
| 213 |
+
align-items: stretch;
|
| 214 |
+
flex-direction: column;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
.status-stack {
|
| 218 |
+
justify-content: start;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
#terminal {
|
| 222 |
+
height: calc(100vh - 164px);
|
| 223 |
+
min-height: 360px;
|
| 224 |
+
padding: 8px;
|
| 225 |
+
}
|
| 226 |
+
}
|
src/webTerminal.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import "@xterm/xterm/css/xterm.css";
|
| 2 |
+
|
| 3 |
+
const THEME = {
|
| 4 |
+
background: "#10141c",
|
| 5 |
+
foreground: "#d6deeb",
|
| 6 |
+
cursor: "#f7c948",
|
| 7 |
+
cursorAccent: "#10141c",
|
| 8 |
+
selectionBackground: "#334155",
|
| 9 |
+
black: "#111827",
|
| 10 |
+
red: "#f87171",
|
| 11 |
+
green: "#34d399",
|
| 12 |
+
yellow: "#facc15",
|
| 13 |
+
blue: "#60a5fa",
|
| 14 |
+
magenta: "#c084fc",
|
| 15 |
+
cyan: "#22d3ee",
|
| 16 |
+
white: "#f8fafc",
|
| 17 |
+
brightBlack: "#475569",
|
| 18 |
+
brightRed: "#fca5a5",
|
| 19 |
+
brightGreen: "#86efac",
|
| 20 |
+
brightYellow: "#fde68a",
|
| 21 |
+
brightBlue: "#93c5fd",
|
| 22 |
+
brightMagenta: "#d8b4fe",
|
| 23 |
+
brightCyan: "#67e8f9",
|
| 24 |
+
brightWhite: "#ffffff",
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
function normalizeNewlines(text) {
|
| 28 |
+
return String(text).replace(/\r?\n/g, "\r\n");
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
export async function createWebTerminal(container) {
|
| 32 |
+
let terminal;
|
| 33 |
+
let fitAddon;
|
| 34 |
+
let engine = "ghostty-web";
|
| 35 |
+
|
| 36 |
+
try {
|
| 37 |
+
const ghostty = await import("ghostty-web");
|
| 38 |
+
await ghostty.init();
|
| 39 |
+
terminal = new ghostty.Terminal({
|
| 40 |
+
cursorBlink: true,
|
| 41 |
+
fontFamily: "JetBrains Mono, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace",
|
| 42 |
+
fontSize: 14,
|
| 43 |
+
rows: 30,
|
| 44 |
+
cols: 100,
|
| 45 |
+
scrollback: 5000,
|
| 46 |
+
theme: THEME,
|
| 47 |
+
});
|
| 48 |
+
fitAddon = new ghostty.FitAddon();
|
| 49 |
+
terminal.loadAddon(fitAddon);
|
| 50 |
+
} catch (error) {
|
| 51 |
+
const [{ Terminal }, { FitAddon }] = await Promise.all([import("@xterm/xterm"), import("@xterm/addon-fit")]);
|
| 52 |
+
engine = "xterm";
|
| 53 |
+
terminal = new Terminal({
|
| 54 |
+
cursorBlink: true,
|
| 55 |
+
fontFamily: "JetBrains Mono, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace",
|
| 56 |
+
fontSize: 14,
|
| 57 |
+
rows: 30,
|
| 58 |
+
cols: 100,
|
| 59 |
+
scrollback: 5000,
|
| 60 |
+
theme: THEME,
|
| 61 |
+
});
|
| 62 |
+
fitAddon = new FitAddon();
|
| 63 |
+
terminal.loadAddon(fitAddon);
|
| 64 |
+
console.warn("ghostty-web failed, using xterm fallback", error);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
terminal.open(container);
|
| 68 |
+
fitAddon.fit();
|
| 69 |
+
terminal.focus();
|
| 70 |
+
|
| 71 |
+
const resizeObserver = new ResizeObserver(() => fitAddon.fit());
|
| 72 |
+
resizeObserver.observe(container);
|
| 73 |
+
|
| 74 |
+
return {
|
| 75 |
+
engine,
|
| 76 |
+
raw: terminal,
|
| 77 |
+
onData: (handler) => terminal.onData(handler),
|
| 78 |
+
write: (text) => terminal.write(normalizeNewlines(text)),
|
| 79 |
+
writeln: (text = "") => terminal.write(`${normalizeNewlines(text)}\r\n`),
|
| 80 |
+
clear: () => terminal.clear(),
|
| 81 |
+
focus: () => terminal.focus(),
|
| 82 |
+
fit: () => fitAddon.fit(),
|
| 83 |
+
dispose: () => {
|
| 84 |
+
resizeObserver.disconnect();
|
| 85 |
+
terminal.dispose();
|
| 86 |
+
},
|
| 87 |
+
};
|
| 88 |
+
}
|
vite.config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "vite";
|
| 2 |
+
|
| 3 |
+
const headers = {
|
| 4 |
+
"Cross-Origin-Embedder-Policy": "credentialless",
|
| 5 |
+
"Cross-Origin-Opener-Policy": "same-origin",
|
| 6 |
+
};
|
| 7 |
+
|
| 8 |
+
export default defineConfig({
|
| 9 |
+
server: {
|
| 10 |
+
headers,
|
| 11 |
+
},
|
| 12 |
+
preview: {
|
| 13 |
+
headers,
|
| 14 |
+
},
|
| 15 |
+
});
|
| 16 |
+
|