pi-plus 0.1.8 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,361 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Pi Coding Agent Theme",
4
+ "description": "Theme schema for Pi coding agent",
5
+ "type": "object",
6
+ "required": ["name", "colors"],
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "description": "JSON schema reference"
11
+ },
12
+ "name": {
13
+ "type": "string",
14
+ "pattern": "^[^/]+$",
15
+ "description": "Theme name. Must not contain '/' because it is reserved for automatic light/dark theme settings."
16
+ },
17
+ "appearance": {
18
+ "type": "string",
19
+ "enum": ["dark", "light"],
20
+ "description": "Background the theme is designed for. Detected from the theme colors when omitted."
21
+ },
22
+ "vars": {
23
+ "type": "object",
24
+ "description": "Reusable color variables",
25
+ "additionalProperties": {
26
+ "oneOf": [
27
+ {
28
+ "type": "string",
29
+ "description": "Hex color (#RGB or #RRGGBB), OKLCH color, variable reference, or empty string for terminal default"
30
+ },
31
+ {
32
+ "type": "integer",
33
+ "minimum": 0,
34
+ "maximum": 255,
35
+ "description": "256-color palette index (0-255)"
36
+ }
37
+ ]
38
+ }
39
+ },
40
+ "colors": {
41
+ "type": "object",
42
+ "description": "Theme color definitions (scrollbar, thinkingMax, and search highlight colors are optional and use compatible fallbacks)",
43
+ "required": [
44
+ "accent",
45
+ "border",
46
+ "borderAccent",
47
+ "borderMuted",
48
+ "success",
49
+ "error",
50
+ "warning",
51
+ "muted",
52
+ "dim",
53
+ "text",
54
+ "thinkingText",
55
+ "selectedBg",
56
+ "userMessageBg",
57
+ "userMessageText",
58
+ "customMessageBg",
59
+ "customMessageText",
60
+ "customMessageLabel",
61
+ "toolPendingBg",
62
+ "toolSuccessBg",
63
+ "toolErrorBg",
64
+ "toolTitle",
65
+ "toolOutput",
66
+ "mdHeading",
67
+ "mdLink",
68
+ "mdLinkUrl",
69
+ "mdCode",
70
+ "mdCodeBlock",
71
+ "mdCodeBlockBorder",
72
+ "mdQuote",
73
+ "mdQuoteBorder",
74
+ "mdHr",
75
+ "mdListBullet",
76
+ "toolDiffAdded",
77
+ "toolDiffRemoved",
78
+ "toolDiffContext",
79
+ "syntaxComment",
80
+ "syntaxKeyword",
81
+ "syntaxFunction",
82
+ "syntaxVariable",
83
+ "syntaxString",
84
+ "syntaxNumber",
85
+ "syntaxType",
86
+ "syntaxOperator",
87
+ "syntaxPunctuation",
88
+ "thinkingOff",
89
+ "thinkingMinimal",
90
+ "thinkingLow",
91
+ "thinkingMedium",
92
+ "thinkingHigh",
93
+ "thinkingXhigh",
94
+ "bashMode"
95
+ ],
96
+ "properties": {
97
+ "accent": {
98
+ "$ref": "#/$defs/colorValue",
99
+ "description": "Primary accent color (logo, selected items, cursor)"
100
+ },
101
+ "border": {
102
+ "$ref": "#/$defs/colorValue",
103
+ "description": "Normal borders"
104
+ },
105
+ "borderAccent": {
106
+ "$ref": "#/$defs/colorValue",
107
+ "description": "Highlighted borders"
108
+ },
109
+ "borderMuted": {
110
+ "$ref": "#/$defs/colorValue",
111
+ "description": "Subtle borders"
112
+ },
113
+ "success": {
114
+ "$ref": "#/$defs/colorValue",
115
+ "description": "Success states"
116
+ },
117
+ "error": {
118
+ "$ref": "#/$defs/colorValue",
119
+ "description": "Error states"
120
+ },
121
+ "warning": {
122
+ "$ref": "#/$defs/colorValue",
123
+ "description": "Warning states"
124
+ },
125
+ "muted": {
126
+ "$ref": "#/$defs/colorValue",
127
+ "description": "Secondary/dimmed text"
128
+ },
129
+ "dim": {
130
+ "$ref": "#/$defs/colorValue",
131
+ "description": "Very dimmed text (more subtle than muted)"
132
+ },
133
+ "text": {
134
+ "$ref": "#/$defs/colorValue",
135
+ "description": "Default text color (usually empty string)"
136
+ },
137
+ "thinkingText": {
138
+ "$ref": "#/$defs/colorValue",
139
+ "description": "Thinking block text color"
140
+ },
141
+ "selectedBg": {
142
+ "$ref": "#/$defs/colorValue",
143
+ "description": "Selected item background"
144
+ },
145
+ "scrollbarTrack": {
146
+ "$ref": "#/$defs/colorValue",
147
+ "description": "Fullscreen scrollbar track foreground (falls back to muted when omitted)"
148
+ },
149
+ "scrollbarThumb": {
150
+ "$ref": "#/$defs/colorValue",
151
+ "description": "Fullscreen scrollbar thumb foreground (falls back to text when omitted)"
152
+ },
153
+ "searchMatchBg": {
154
+ "$ref": "#/$defs/colorValue",
155
+ "description": "Transcript search match background and current-match text (falls back to selectedBg when omitted)"
156
+ },
157
+ "searchMatchText": {
158
+ "$ref": "#/$defs/colorValue",
159
+ "description": "Transcript search match text and current-match background (falls back to text when omitted)"
160
+ },
161
+ "userMessageBg": {
162
+ "$ref": "#/$defs/colorValue",
163
+ "description": "User message background"
164
+ },
165
+ "userMessageText": {
166
+ "$ref": "#/$defs/colorValue",
167
+ "description": "User message text color"
168
+ },
169
+ "customMessageBg": {
170
+ "$ref": "#/$defs/colorValue",
171
+ "description": "Custom message background (hook-injected messages)"
172
+ },
173
+ "customMessageText": {
174
+ "$ref": "#/$defs/colorValue",
175
+ "description": "Custom message text color"
176
+ },
177
+ "customMessageLabel": {
178
+ "$ref": "#/$defs/colorValue",
179
+ "description": "Custom message type label color"
180
+ },
181
+ "toolPendingBg": {
182
+ "$ref": "#/$defs/colorValue",
183
+ "description": "Tool execution box (pending state)"
184
+ },
185
+ "toolSuccessBg": {
186
+ "$ref": "#/$defs/colorValue",
187
+ "description": "Tool execution box (success state)"
188
+ },
189
+ "toolErrorBg": {
190
+ "$ref": "#/$defs/colorValue",
191
+ "description": "Tool execution box (error state)"
192
+ },
193
+ "toolTitle": {
194
+ "$ref": "#/$defs/colorValue",
195
+ "description": "Tool execution box title color"
196
+ },
197
+ "toolOutput": {
198
+ "$ref": "#/$defs/colorValue",
199
+ "description": "Tool execution box output text color"
200
+ },
201
+ "mdHeading": {
202
+ "$ref": "#/$defs/colorValue",
203
+ "description": "Markdown heading text"
204
+ },
205
+ "mdLink": {
206
+ "$ref": "#/$defs/colorValue",
207
+ "description": "Markdown link text"
208
+ },
209
+ "mdLinkUrl": {
210
+ "$ref": "#/$defs/colorValue",
211
+ "description": "Markdown link URL"
212
+ },
213
+ "mdCode": {
214
+ "$ref": "#/$defs/colorValue",
215
+ "description": "Markdown inline code"
216
+ },
217
+ "mdCodeBlock": {
218
+ "$ref": "#/$defs/colorValue",
219
+ "description": "Markdown code block content"
220
+ },
221
+ "mdCodeBlockBorder": {
222
+ "$ref": "#/$defs/colorValue",
223
+ "description": "Markdown code block fences"
224
+ },
225
+ "mdQuote": {
226
+ "$ref": "#/$defs/colorValue",
227
+ "description": "Markdown blockquote text"
228
+ },
229
+ "mdQuoteBorder": {
230
+ "$ref": "#/$defs/colorValue",
231
+ "description": "Markdown blockquote border"
232
+ },
233
+ "mdHr": {
234
+ "$ref": "#/$defs/colorValue",
235
+ "description": "Markdown horizontal rule"
236
+ },
237
+ "mdListBullet": {
238
+ "$ref": "#/$defs/colorValue",
239
+ "description": "Markdown list bullets/numbers"
240
+ },
241
+ "toolDiffAdded": {
242
+ "$ref": "#/$defs/colorValue",
243
+ "description": "Added lines in tool diffs"
244
+ },
245
+ "toolDiffRemoved": {
246
+ "$ref": "#/$defs/colorValue",
247
+ "description": "Removed lines in tool diffs"
248
+ },
249
+ "toolDiffContext": {
250
+ "$ref": "#/$defs/colorValue",
251
+ "description": "Context lines in tool diffs"
252
+ },
253
+ "syntaxComment": {
254
+ "$ref": "#/$defs/colorValue",
255
+ "description": "Syntax highlighting: comments"
256
+ },
257
+ "syntaxKeyword": {
258
+ "$ref": "#/$defs/colorValue",
259
+ "description": "Syntax highlighting: keywords"
260
+ },
261
+ "syntaxFunction": {
262
+ "$ref": "#/$defs/colorValue",
263
+ "description": "Syntax highlighting: function names"
264
+ },
265
+ "syntaxVariable": {
266
+ "$ref": "#/$defs/colorValue",
267
+ "description": "Syntax highlighting: variable names"
268
+ },
269
+ "syntaxString": {
270
+ "$ref": "#/$defs/colorValue",
271
+ "description": "Syntax highlighting: string literals"
272
+ },
273
+ "syntaxNumber": {
274
+ "$ref": "#/$defs/colorValue",
275
+ "description": "Syntax highlighting: number literals"
276
+ },
277
+ "syntaxType": {
278
+ "$ref": "#/$defs/colorValue",
279
+ "description": "Syntax highlighting: type names"
280
+ },
281
+ "syntaxOperator": {
282
+ "$ref": "#/$defs/colorValue",
283
+ "description": "Syntax highlighting: operators"
284
+ },
285
+ "syntaxPunctuation": {
286
+ "$ref": "#/$defs/colorValue",
287
+ "description": "Syntax highlighting: punctuation"
288
+ },
289
+ "thinkingOff": {
290
+ "$ref": "#/$defs/colorValue",
291
+ "description": "Thinking level border: off"
292
+ },
293
+ "thinkingMinimal": {
294
+ "$ref": "#/$defs/colorValue",
295
+ "description": "Thinking level border: minimal"
296
+ },
297
+ "thinkingLow": {
298
+ "$ref": "#/$defs/colorValue",
299
+ "description": "Thinking level border: low"
300
+ },
301
+ "thinkingMedium": {
302
+ "$ref": "#/$defs/colorValue",
303
+ "description": "Thinking level border: medium"
304
+ },
305
+ "thinkingHigh": {
306
+ "$ref": "#/$defs/colorValue",
307
+ "description": "Thinking level border: high"
308
+ },
309
+ "thinkingXhigh": {
310
+ "$ref": "#/$defs/colorValue",
311
+ "description": "Thinking level border: xhigh"
312
+ },
313
+ "thinkingMax": {
314
+ "$ref": "#/$defs/colorValue",
315
+ "description": "Thinking level border: max (falls back to thinkingXhigh when omitted)"
316
+ },
317
+ "bashMode": {
318
+ "$ref": "#/$defs/colorValue",
319
+ "description": "Editor border color in bash mode"
320
+ }
321
+ },
322
+ "additionalProperties": false
323
+ },
324
+ "export": {
325
+ "type": "object",
326
+ "description": "Optional colors for HTML export (defaults derived from userMessageBg if not specified)",
327
+ "properties": {
328
+ "pageBg": {
329
+ "$ref": "#/$defs/colorValue",
330
+ "description": "Page background color"
331
+ },
332
+ "cardBg": {
333
+ "$ref": "#/$defs/colorValue",
334
+ "description": "Card/container background color"
335
+ },
336
+ "infoBg": {
337
+ "$ref": "#/$defs/colorValue",
338
+ "description": "Info sections background (system prompt, notices)"
339
+ }
340
+ },
341
+ "additionalProperties": false
342
+ }
343
+ },
344
+ "additionalProperties": false,
345
+ "$defs": {
346
+ "colorValue": {
347
+ "oneOf": [
348
+ {
349
+ "type": "string",
350
+ "description": "Hex color (#RGB or #RRGGBB), OKLCH color, variable reference, or empty string for terminal default"
351
+ },
352
+ {
353
+ "type": "integer",
354
+ "minimum": 0,
355
+ "maximum": 255,
356
+ "description": "256-color palette index (0-255)"
357
+ }
358
+ ]
359
+ }
360
+ }
361
+ }
@@ -0,0 +1,82 @@
1
+ import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
+ {
3
+ const __noEnvIndex = process.argv.indexOf("--no-env");
4
+ if (__noEnvIndex !== -1) {
5
+ process.argv.splice(__noEnvIndex, 1);
6
+ for (const __key of ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "GROQ_API_KEY", "CEREBRAS_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY", "ZAI_API_KEY", "MISTRAL_API_KEY", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "AI_GATEWAY_API_KEY", "OPENCODE_API_KEY", "COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", "HF_TOKEN", "GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT", "GOOGLE_CLOUD_LOCATION", "AWS_PROFILE", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "AWS_REGION", "AWS_DEFAULT_REGION", "AWS_BEARER_TOKEN_BEDROCK", "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "AWS_CONTAINER_CREDENTIALS_FULL_URI", "AWS_WEB_IDENTITY_TOKEN_FILE", "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_RESOURCE_NAME"]) delete process.env[__key];
7
+ }
8
+ }
9
+ var __require=(x=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(x,{get:(a,b)=>(typeof require<"u"?require:a)[b]}):x)(function(x){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+x+'" is not supported')});var procEnvCache=null;function getBunSandboxEnvValue(name){if(!(typeof process>"u"||!process.versions?.bun||Object.keys(process.env).length>0)){if(procEnvCache===null){procEnvCache=new Map;try{let{readFileSync}=__require("node:fs"),data=readFileSync("/proc/self/environ","utf-8");for(let entry of data.split("\0")){let idx=entry.indexOf("=");idx>0&&procEnvCache.set(entry.slice(0,idx),entry.slice(idx+1))}}catch{}}return procEnvCache.get(name)}}function getProviderEnvValue(name,env){return env?.[name]||(typeof process<"u"?process.env[name]:void 0)||getBunSandboxEnvValue(name)||void 0}var CANCEL_MESSAGE="Login cancelled",TIMEOUT_MESSAGE="Device flow timed out",SLOW_DOWN_TIMEOUT_MESSAGE="Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";function abortableSleep(ms,signal,cancelMessage){return new Promise((resolve,reject)=>{if(signal.aborted){reject(new Error(cancelMessage));return}let onAbort=()=>{clearTimeout(timeout),reject(new Error(cancelMessage))},timeout=setTimeout(()=>{signal.removeEventListener("abort",onAbort),resolve()},ms);signal.addEventListener("abort",onAbort,{once:!0})})}async function pollOAuthDeviceCodeFlow(options){let deadline=typeof options.expiresInSeconds=="number"?Date.now()+options.expiresInSeconds*1e3:Number.POSITIVE_INFINITY,intervalMs=Math.max(1e3,Math.floor((options.intervalSeconds??5)*1e3)),slowDownResponses=0;if(options.waitBeforeFirstPoll){let remainingMs=deadline-Date.now();remainingMs>0&&await abortableSleep(Math.min(intervalMs,remainingMs),options.signal,CANCEL_MESSAGE)}for(;Date.now()<deadline;){if(options.signal.aborted)throw new Error(CANCEL_MESSAGE);let result=await options.poll();if(result.status==="complete")return result.value;if(result.status==="failed")throw new Error(result.message);result.status==="slow_down"&&(slowDownResponses+=1,intervalMs=typeof result.intervalSeconds=="number"&&Number.isFinite(result.intervalSeconds)&&result.intervalSeconds>0?Math.max(1e3,Math.floor(result.intervalSeconds*1e3)):Math.max(1e3,intervalMs+5e3));let remainingMs=deadline-Date.now();if(remainingMs<=0)break;await abortableSleep(Math.min(intervalMs,remainingMs),options.signal,CANCEL_MESSAGE)}throw new Error(slowDownResponses>0?SLOW_DOWN_TIMEOUT_MESSAGE:TIMEOUT_MESSAGE)}var LOGO_SVG='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>';function escapeHtml(value){return value.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function renderPage(options){let title=escapeHtml(options.title),heading=escapeHtml(options.heading),message=escapeHtml(options.message),details=options.details?escapeHtml(options.details):void 0;return`<!doctype html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8" />
13
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
14
+ <title>${title}</title>
15
+ <style>
16
+ :root {
17
+ --text: #fafafa;
18
+ --text-dim: #a1a1aa;
19
+ --page-bg: #09090b;
20
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
21
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
22
+ }
23
+ * { box-sizing: border-box; }
24
+ html { color-scheme: dark; }
25
+ body {
26
+ margin: 0;
27
+ min-height: 100vh;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ padding: 24px;
32
+ background: var(--page-bg);
33
+ color: var(--text);
34
+ font-family: var(--font-sans);
35
+ text-align: center;
36
+ }
37
+ main {
38
+ width: 100%;
39
+ max-width: 560px;
40
+ display: flex;
41
+ flex-direction: column;
42
+ align-items: center;
43
+ justify-content: center;
44
+ }
45
+ .logo {
46
+ width: 72px;
47
+ height: 72px;
48
+ display: block;
49
+ margin-bottom: 24px;
50
+ }
51
+ h1 {
52
+ margin: 0 0 10px;
53
+ font-size: 28px;
54
+ line-height: 1.15;
55
+ font-weight: 650;
56
+ color: var(--text);
57
+ }
58
+ p {
59
+ margin: 0;
60
+ line-height: 1.7;
61
+ color: var(--text-dim);
62
+ font-size: 15px;
63
+ }
64
+ .details {
65
+ margin-top: 16px;
66
+ font-family: var(--font-mono);
67
+ font-size: 13px;
68
+ color: var(--text-dim);
69
+ white-space: pre-wrap;
70
+ word-break: break-word;
71
+ }
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <main>
76
+ <div class="logo">${LOGO_SVG}</div>
77
+ <h1>${heading}</h1>
78
+ <p>${message}</p>
79
+ ${details?`<div class="details">${details}</div>`:""}
80
+ </main>
81
+ </body>
82
+ </html>`}function oauthSuccessHtml(message){return renderPage({title:"Authentication successful",heading:"Authentication successful",message})}function oauthErrorHtml(message,details){return renderPage({title:"Authentication failed",heading:"Authentication failed",message,details})}function base64urlEncode(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function generatePKCE(){let verifierBytes=new Uint8Array(32);crypto.getRandomValues(verifierBytes);let verifier=base64urlEncode(verifierBytes),data=new TextEncoder().encode(verifier),hashBuffer=await crypto.subtle.digest("SHA-256",data),challenge=base64urlEncode(new Uint8Array(hashBuffer));return{verifier,challenge}}var _randomBytes=null,_http=null;typeof process<"u"&&(process.versions?.node||process.versions?.bun)&&(import("node:crypto").then(m=>{_randomBytes=m.randomBytes}),import("node:http").then(m=>{_http=m}));var CLIENT_ID="app_EMoamEEZ73f0CkXaXp7hrann",AUTH_BASE_URL="https://auth.openai.com",AUTHORIZE_URL=`${AUTH_BASE_URL}/oauth/authorize`,TOKEN_URL=`${AUTH_BASE_URL}/oauth/token`,REDIRECT_URI="http://localhost:1455/auth/callback",DEVICE_USER_CODE_URL=`${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`,DEVICE_TOKEN_URL=`${AUTH_BASE_URL}/api/accounts/deviceauth/token`,DEVICE_VERIFICATION_URI=`${AUTH_BASE_URL}/codex/device`,DEVICE_REDIRECT_URI=`${AUTH_BASE_URL}/deviceauth/callback`,DEVICE_CODE_TIMEOUT_SECONDS=900,OPENAI_CODEX_BROWSER_LOGIN_METHOD="browser",OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD="device_code",SCOPE="openid profile email offline_access",JWT_CLAIM_PATH="https://api.openai.com/auth";function getCallbackHost(){return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST")||"127.0.0.1"}function createState(){if(!_randomBytes)throw new Error("OpenAI Codex OAuth is only available in Node.js environments");return _randomBytes(16).toString("hex")}function parseAuthorizationInput(input){let value=input.trim();if(!value)return{};try{let url=new URL(value);return{code:url.searchParams.get("code")??void 0,state:url.searchParams.get("state")??void 0}}catch{}if(value.includes("#")){let[code,state]=value.split("#",2);return{code,state}}if(value.includes("code=")){let params=new URLSearchParams(value);return{code:params.get("code")??void 0,state:params.get("state")??void 0}}return{code:value}}function decodeJwt(token){try{let parts=token.split(".");if(parts.length!==3)return null;let payload=parts[1]??"",decoded=atob(payload);return JSON.parse(decoded)}catch{return null}}async function fetchWithLoginCancellation(input,init){try{return await fetch(input,init)}catch(error){throw init.signal?.aborted?new Error("Login cancelled"):error}}async function readTokenResponse(response,operation){if(!response.ok){let text=await response.text().catch(()=>"");throw new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text||response.statusText}`)}let json=await response.json();if(!json?.access_token||!json.refresh_token||typeof json.expires_in!="number")throw new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);return{access:json.access_token,refresh:json.refresh_token,expires:Date.now()+json.expires_in*1e3}}async function exchangeAuthorizationCode(code,verifier,redirectUri,signal){let response=await fetchWithLoginCancellation(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:CLIENT_ID,code,code_verifier:verifier,redirect_uri:redirectUri}),signal});return readTokenResponse(response,"exchange")}async function refreshAccessToken(refreshToken,signal){let response;try{response=await fetch(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:refreshToken,client_id:CLIENT_ID}),signal})}catch(error){throw new Error(`OpenAI Codex token refresh error: ${error instanceof Error?error.message:String(error)}`)}return readTokenResponse(response,"refresh")}async function startOpenAICodexDeviceAuth(signal){let response=await fetchWithLoginCancellation(DEVICE_USER_CODE_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:CLIENT_ID}),signal});if(!response.ok){if(response.status===404)throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");let responseBody=await response.text().catch(()=>"");throw new Error(`OpenAI Codex device code request failed with status ${response.status}${responseBody?`: ${responseBody}`:""}`)}let json=await response.json(),intervalSeconds=typeof json?.interval=="string"?Number(json.interval.trim()):json?.interval;if(!json?.device_auth_id||!json.user_code||typeof intervalSeconds!="number"||!Number.isFinite(intervalSeconds)||intervalSeconds<0)throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);return{deviceAuthId:json.device_auth_id,userCode:json.user_code,intervalSeconds}}async function pollOpenAICodexDeviceAuth(device,signal){return pollOAuthDeviceCodeFlow({intervalSeconds:device.intervalSeconds,expiresInSeconds:DEVICE_CODE_TIMEOUT_SECONDS,signal,poll:async()=>{let response=await fetchWithLoginCancellation(DEVICE_TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:device.deviceAuthId,user_code:device.userCode}),signal});if(response.ok){let json=await response.json();return!json?.authorization_code||!json.code_verifier?{status:"failed",message:`Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`}:{status:"complete",value:{authorizationCode:json.authorization_code,codeVerifier:json.code_verifier}}}if(response.status===403||response.status===404)return{status:"pending"};let responseBody=await response.text().catch(()=>""),errorCode;try{let error=JSON.parse(responseBody)?.error;errorCode=typeof error=="object"?error?.code:error}catch{}return errorCode==="deviceauth_authorization_pending"?{status:"pending"}:errorCode==="slow_down"?{status:"slow_down"}:{status:"failed",message:`OpenAI Codex device auth failed with status ${response.status}${responseBody?`: ${responseBody}`:""}`}}})}async function createAuthorizationFlow(originator="pi"){let{verifier,challenge}=await generatePKCE(),state=createState(),url=new URL(AUTHORIZE_URL);return url.searchParams.set("response_type","code"),url.searchParams.set("client_id",CLIENT_ID),url.searchParams.set("redirect_uri",REDIRECT_URI),url.searchParams.set("scope",SCOPE),url.searchParams.set("code_challenge",challenge),url.searchParams.set("code_challenge_method","S256"),url.searchParams.set("state",state),url.searchParams.set("id_token_add_organizations","true"),url.searchParams.set("codex_cli_simplified_flow","true"),url.searchParams.set("originator",originator),{verifier,state,url:url.toString()}}function startLocalOAuthServer(state){if(!_http)throw new Error("OpenAI Codex OAuth is only available in Node.js environments");let settleWait,waitForCodePromise=new Promise(resolve=>{let settled=!1;settleWait=value=>{settled||(settled=!0,resolve(value))}}),server=_http.createServer((req,res)=>{try{let url=new URL(req.url||"","http://localhost");if(url.pathname!=="/auth/callback"){res.statusCode=404,res.setHeader("Content-Type","text/html; charset=utf-8"),res.end(oauthErrorHtml("Callback route not found."));return}if(url.searchParams.get("state")!==state){res.statusCode=400,res.setHeader("Content-Type","text/html; charset=utf-8"),res.end(oauthErrorHtml("State mismatch."));return}let code=url.searchParams.get("code");if(!code){res.statusCode=400,res.setHeader("Content-Type","text/html; charset=utf-8"),res.end(oauthErrorHtml("Missing authorization code."));return}res.statusCode=200,res.setHeader("Content-Type","text/html; charset=utf-8"),res.end(oauthSuccessHtml("OpenAI authentication completed. You can close this window.")),settleWait?.({code})}catch{res.statusCode=500,res.setHeader("Content-Type","text/html; charset=utf-8"),res.end(oauthErrorHtml("Internal error while processing OAuth callback."))}});return new Promise(resolve=>{server.listen(1455,getCallbackHost(),()=>{resolve({close:()=>server.close(),cancelWait:()=>{settleWait?.(null)},waitForCode:()=>waitForCodePromise})}).on("error",_err=>{settleWait?.(null),resolve({close:()=>{try{server.close()}catch{}},cancelWait:()=>{},waitForCode:async()=>null})})})}function getAccountId(accessToken){let accountId=decodeJwt(accessToken)?.[JWT_CLAIM_PATH]?.chatgpt_account_id;return typeof accountId=="string"&&accountId.length>0?accountId:null}function credentialsFromToken(token){let accountId=getAccountId(token.access);if(!accountId)throw new Error("Failed to extract accountId from token");return{type:"oauth",access:token.access,refresh:token.refresh,expires:token.expires,accountId}}async function exchangeAuthorizationCodeForCredentials(code,verifier,redirectUri,signal){return credentialsFromToken(await exchangeAuthorizationCode(code,verifier,redirectUri,signal))}async function loginOpenAICodexDeviceCode(interaction){let device=await startOpenAICodexDeviceAuth(interaction.signal);interaction.notify({type:"device_code",userCode:device.userCode,verificationUri:DEVICE_VERIFICATION_URI,intervalSeconds:device.intervalSeconds,expiresInSeconds:DEVICE_CODE_TIMEOUT_SECONDS});let code=await pollOpenAICodexDeviceAuth(device,interaction.signal);return exchangeAuthorizationCodeForCredentials(code.authorizationCode,code.codeVerifier,DEVICE_REDIRECT_URI,interaction.signal)}async function loginOpenAICodex(interaction){let{verifier,state,url}=await createAuthorizationFlow(),server=await startLocalOAuthServer(state),manualAbort=new AbortController,onAbort=()=>server.cancelWait();interaction.signal.addEventListener("abort",onAbort,{once:!0}),interaction.signal.aborted&&onAbort();let code,manualCode,manualError;interaction.notify({type:"auth_url",url,instructions:"A browser window should open. Complete login to finish."});try{let manualPromise=interaction.prompt({type:"manual_code",message:"Complete login in your browser, or paste the authorization code / redirect URL here:",placeholder:REDIRECT_URI,signal:manualAbort.signal}).then(input=>{manualCode=input,server.cancelWait()}).catch(error=>{manualError=error instanceof Error?error:new Error(String(error)),server.cancelWait()}),result=await server.waitForCode();if(manualError)throw manualError;if(result?.code)code=result.code;else if(manualCode){let parsed=parseAuthorizationInput(manualCode);if(parsed.state&&parsed.state!==state)throw new Error("State mismatch");code=parsed.code}if(!code){if(await manualPromise,manualError)throw manualError;if(manualCode){let parsed=parseAuthorizationInput(manualCode);if(parsed.state&&parsed.state!==state)throw new Error("State mismatch");code=parsed.code}}if(!code)throw new Error("Missing authorization code");return exchangeAuthorizationCodeForCredentials(code,verifier,REDIRECT_URI,interaction.signal)}finally{interaction.signal.removeEventListener("abort",onAbort),manualAbort.abort(),server.close()}}async function refreshOpenAICodexToken(refreshToken,signal){return credentialsFromToken(await refreshAccessToken(refreshToken,signal))}var openaiCodexOAuth={name:"OpenAI (ChatGPT Plus/Pro)",isSubscription:!0,async login(interaction){let method=await interaction.prompt({type:"select",message:"Select OpenAI Codex login method:",options:[{id:OPENAI_CODEX_BROWSER_LOGIN_METHOD,label:"Browser login (default)"},{id:OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,label:"Device code login (headless)"}]});if(method===OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD)return loginOpenAICodexDeviceCode(interaction);if(method!==OPENAI_CODEX_BROWSER_LOGIN_METHOD)throw new Error(`Unknown OpenAI Codex login method: ${method}`);return loginOpenAICodex(interaction)},refresh:(credential,signal)=>refreshOpenAICodexToken(credential.refresh,signal),async toAuth(credential){return{apiKey:credential.access}}};export{openaiCodexOAuth};
package/openrouter.js ADDED
@@ -0,0 +1,82 @@
1
+ import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
+ {
3
+ const __noEnvIndex = process.argv.indexOf("--no-env");
4
+ if (__noEnvIndex !== -1) {
5
+ process.argv.splice(__noEnvIndex, 1);
6
+ for (const __key of ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "GROQ_API_KEY", "CEREBRAS_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY", "ZAI_API_KEY", "MISTRAL_API_KEY", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "AI_GATEWAY_API_KEY", "OPENCODE_API_KEY", "COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", "HF_TOKEN", "GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT", "GOOGLE_CLOUD_LOCATION", "AWS_PROFILE", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "AWS_REGION", "AWS_DEFAULT_REGION", "AWS_BEARER_TOKEN_BEDROCK", "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "AWS_CONTAINER_CREDENTIALS_FULL_URI", "AWS_WEB_IDENTITY_TOKEN_FILE", "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_RESOURCE_NAME"]) delete process.env[__key];
7
+ }
8
+ }
9
+ var __require=(x=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(x,{get:(a,b)=>(typeof require<"u"?require:a)[b]}):x)(function(x){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+x+'" is not supported')});import{createServer}from"node:http";var procEnvCache=null;function getBunSandboxEnvValue(name){if(!(typeof process>"u"||!process.versions?.bun||Object.keys(process.env).length>0)){if(procEnvCache===null){procEnvCache=new Map;try{let{readFileSync}=__require("node:fs"),data=readFileSync("/proc/self/environ","utf-8");for(let entry of data.split("\0")){let idx=entry.indexOf("=");idx>0&&procEnvCache.set(entry.slice(0,idx),entry.slice(idx+1))}}catch{}}return procEnvCache.get(name)}}function getProviderEnvValue(name,env){return env?.[name]||(typeof process<"u"?process.env[name]:void 0)||getBunSandboxEnvValue(name)||void 0}var LOGO_SVG='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>';function escapeHtml(value){return value.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function renderPage(options){let title=escapeHtml(options.title),heading=escapeHtml(options.heading),message=escapeHtml(options.message),details=options.details?escapeHtml(options.details):void 0;return`<!doctype html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8" />
13
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
14
+ <title>${title}</title>
15
+ <style>
16
+ :root {
17
+ --text: #fafafa;
18
+ --text-dim: #a1a1aa;
19
+ --page-bg: #09090b;
20
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
21
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
22
+ }
23
+ * { box-sizing: border-box; }
24
+ html { color-scheme: dark; }
25
+ body {
26
+ margin: 0;
27
+ min-height: 100vh;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ padding: 24px;
32
+ background: var(--page-bg);
33
+ color: var(--text);
34
+ font-family: var(--font-sans);
35
+ text-align: center;
36
+ }
37
+ main {
38
+ width: 100%;
39
+ max-width: 560px;
40
+ display: flex;
41
+ flex-direction: column;
42
+ align-items: center;
43
+ justify-content: center;
44
+ }
45
+ .logo {
46
+ width: 72px;
47
+ height: 72px;
48
+ display: block;
49
+ margin-bottom: 24px;
50
+ }
51
+ h1 {
52
+ margin: 0 0 10px;
53
+ font-size: 28px;
54
+ line-height: 1.15;
55
+ font-weight: 650;
56
+ color: var(--text);
57
+ }
58
+ p {
59
+ margin: 0;
60
+ line-height: 1.7;
61
+ color: var(--text-dim);
62
+ font-size: 15px;
63
+ }
64
+ .details {
65
+ margin-top: 16px;
66
+ font-family: var(--font-mono);
67
+ font-size: 13px;
68
+ color: var(--text-dim);
69
+ white-space: pre-wrap;
70
+ word-break: break-word;
71
+ }
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <main>
76
+ <div class="logo">${LOGO_SVG}</div>
77
+ <h1>${heading}</h1>
78
+ <p>${message}</p>
79
+ ${details?`<div class="details">${details}</div>`:""}
80
+ </main>
81
+ </body>
82
+ </html>`}function oauthSuccessHtml(message){return renderPage({title:"Authentication successful",heading:"Authentication successful",message})}function oauthErrorHtml(message,details){return renderPage({title:"Authentication failed",heading:"Authentication failed",message,details})}function base64urlEncode(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function generatePKCE(){let verifierBytes=new Uint8Array(32);crypto.getRandomValues(verifierBytes);let verifier=base64urlEncode(verifierBytes),data=new TextEncoder().encode(verifier),hashBuffer=await crypto.subtle.digest("SHA-256",data),challenge=base64urlEncode(new Uint8Array(hashBuffer));return{verifier,challenge}}var AUTHORIZE_URL="https://openrouter.ai/auth",TOKEN_URL="https://openrouter.ai/api/v1/auth/keys",LOGIN_TIMEOUT_MS=300*1e3,TOKEN_EXCHANGE_TIMEOUT_MS=3e4;function getCallbackHost(){return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST")||"127.0.0.1"}function sendHtml(response,status,html){response.statusCode=status,response.setHeader("content-type","text/html; charset=utf-8"),response.setHeader("cache-control","no-store"),response.end(html)}function parseAuthorizationInput(input){let value=input.trim();if(value){try{return new URL(value).searchParams.get("code")??void 0}catch{}return value.includes("code=")?new URLSearchParams(value).get("code")??void 0:value}}function errorDetail(body){if(typeof body.error_description=="string")return body.error_description;if(typeof body.message=="string")return body.message;if(typeof body.error=="string")return body.error;if(body.error&&typeof body.error=="object"&&!Array.isArray(body.error)){let message=body.error.message;if(typeof message=="string")return message}}async function exchangeAuthorizationCode(code,verifier,signal){if(signal.aborted)throw new Error("Login cancelled");let controller=new AbortController,onAbort=()=>controller.abort(signal.reason);signal.addEventListener("abort",onAbort,{once:!0});let timeout=setTimeout(()=>controller.abort(new Error("OpenRouter OAuth token exchange timed out")),TOKEN_EXCHANGE_TIMEOUT_MS),response,body={};try{response=await fetch(TOKEN_URL,{method:"POST",headers:{accept:"application/json","content-type":"application/json"},body:JSON.stringify({code,code_verifier:verifier,code_challenge_method:"S256"}),signal:controller.signal});try{let parsed=await response.json();parsed&&typeof parsed=="object"&&!Array.isArray(parsed)&&(body=parsed)}catch{if(response.ok)throw new Error("OpenRouter OAuth returned invalid JSON")}}catch(error){throw signal.aborted?new Error("Login cancelled"):controller.signal.aborted?new Error("OpenRouter OAuth token exchange timed out"):error}finally{clearTimeout(timeout),signal.removeEventListener("abort",onAbort)}if(!response.ok){let detail=errorDetail(body);throw new Error(`OpenRouter OAuth key exchange failed (HTTP ${response.status})${detail?`: ${detail}`:""}`)}if(typeof body.key!="string"||body.key.length===0)throw new Error('OpenRouter OAuth response carries no "key"');return{type:"oauth",access:body.key,refresh:"",expires:Number.MAX_SAFE_INTEGER}}async function startCallbackServer(callbackPath,verifier,signal){if(signal.aborted)throw new Error("Login cancelled");let callbackHost=getCallbackHost(),resolveCredential=()=>{},rejectCredential=()=>{},credential=new Promise((resolve,reject)=>{resolveCredential=resolve,rejectCredential=reject}),server,claimed=!1,settled=!1,timeout,onAbort,close=()=>{timeout&&clearTimeout(timeout),onAbort&&signal.removeEventListener("abort",onAbort),server.close()},finish=result=>{settled||(settled=!0,close(),"credential"in result?resolveCredential(result.credential):rejectCredential(result.error))};if(server=createServer((request,response)=>{(async()=>{let requestUrl=new URL(request.url??"/",`http://${callbackHost}`);if(request.method!=="GET"||requestUrl.pathname!==callbackPath){sendHtml(response,404,oauthErrorHtml("OAuth callback route not found."));return}if(claimed||settled){sendHtml(response,409,oauthErrorHtml("This OAuth callback has already been used."));return}let oauthError=requestUrl.searchParams.get("error");if(oauthError){let description=requestUrl.searchParams.get("error_description")??oauthError;sendHtml(response,400,oauthErrorHtml("OpenRouter authorization was denied.",description)),finish({error:new Error(`OpenRouter authorization failed: ${description}`)});return}let code=requestUrl.searchParams.get("code");if(!code){sendHtml(response,400,oauthErrorHtml("OpenRouter returned no authorization code."));return}claimed=!0;try{let result=await exchangeAuthorizationCode(code,verifier,signal);sendHtml(response,200,oauthSuccessHtml("Signed in to OpenRouter. You may now close this page.")),finish({credential:result})}catch(error){let message=error instanceof Error?error.message:"Unknown token exchange error";sendHtml(response,502,oauthErrorHtml("OpenRouter key exchange failed.",message)),finish({error:error instanceof Error?error:new Error(message)})}})()}),await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(0,callbackHost,()=>{server.removeListener("error",reject),resolve()})}),server.on("error",error=>finish({error})),onAbort=()=>finish({error:new Error("Login cancelled")}),signal.addEventListener("abort",onAbort,{once:!0}),signal.aborted)throw close(),new Error("Login cancelled");timeout=setTimeout(()=>finish({error:new Error("OpenRouter OAuth login timed out")}),LOGIN_TIMEOUT_MS);let address=server.address();if(!address||typeof address=="string")throw close(),new Error("Could not determine the OpenRouter OAuth callback port");return{callbackUrl:`http://${callbackHost}:${address.port}${callbackPath}`,close,cancelWait:()=>{claimed||finish({credential:null})},waitForCredential:()=>credential}}async function loginOpenRouter(interaction){let{verifier,challenge}=await generatePKCE(),callbackPath=`/oauth/callback/${crypto.randomUUID()}`,callback=await startCallbackServer(callbackPath,verifier,interaction.signal),manualAbort=new AbortController,manualInput,manualError;try{let authorizeUrl=new URL(AUTHORIZE_URL);authorizeUrl.search=new URLSearchParams({callback_url:callback.callbackUrl,code_challenge:challenge,code_challenge_method:"S256"}).toString(),interaction.notify({type:"progress",message:`Listening for OpenRouter OAuth callback on ${callback.callbackUrl}`}),interaction.notify({type:"auth_url",url:authorizeUrl.toString(),instructions:"Complete sign-in in your browser. If the browser is on another machine, paste the final redirect URL here."});let manualPromise=interaction.prompt({type:"manual_code",message:"Complete sign-in in your browser, or paste the authorization code / redirect URL here:",placeholder:callback.callbackUrl,signal:manualAbort.signal}).then(input=>{manualInput=input,callback.cancelWait()}).catch(error=>{manualError=error instanceof Error?error:new Error(String(error)),callback.cancelWait()}),credential=await callback.waitForCredential();if(manualError)throw manualError;if(credential)return credential;if(await manualPromise,manualError)throw manualError;let code=manualInput?parseAuthorizationInput(manualInput):void 0;if(!code)throw new Error("Missing authorization code");return interaction.notify({type:"progress",message:"Exchanging authorization code for an API key..."}),await exchangeAuthorizationCode(code,verifier,interaction.signal)}finally{manualAbort.abort(),callback.close()}}var openRouterOAuth={name:"OpenRouter OAuth",loginLabel:"Sign in with OpenRouter",login:loginOpenRouter,async refresh(credential,_signal){return credential},async toAuth(credential){return{apiKey:credential.access}}};export{openRouterOAuth};
package/package.json CHANGED
@@ -1,53 +1,33 @@
1
1
  {
2
2
  "name": "pi-plus",
3
- "version": "0.1.8",
4
- "description": "Claude Code-exact context usage detection for pi (extension)",
3
+ "version": "0.1.11",
4
+ "description": "pi coding agent with the pi-plus override layer (pipi CLI)",
5
5
  "type": "module",
6
- "pi": {
7
- "extensions": [
8
- "./src/index.ts"
9
- ]
6
+ "piConfig": {
7
+ "configDir": ".pi"
10
8
  },
11
- "scripts": {
12
- "typecheck": "tsc --noEmit",
13
- "test": "vitest run",
14
- "test:watch": "vitest"
9
+ "bin": {
10
+ "pipi": "pipi.js"
15
11
  },
16
- "devDependencies": {
17
- "@earendil-works/pi-ai": "0.85.1",
18
- "@earendil-works/pi-coding-agent": "0.85.1",
19
- "@earendil-works/pi-tui": "0.85.1",
20
- "typescript": "^5.6.0",
21
- "vitest": "^2.1.9"
22
- },
23
- "license": "MIT",
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/sherocktong/pi-plus.git"
27
- },
28
- "homepage": "https://github.com/sherocktong/pi-plus#readme",
29
- "bugs": {
30
- "url": "https://github.com/sherocktong/pi-plus/issues"
31
- },
32
- "keywords": [
33
- "pi-package",
34
- "pi",
35
- "pi-extension",
36
- "claude-code",
37
- "context-usage",
38
- "context-window"
39
- ],
40
12
  "files": [
41
- "src",
13
+ "pipi.js",
14
+ "*.js",
15
+ "modes/",
16
+ "core/",
42
17
  "README.md",
43
- "LICENSE"
18
+ "CHANGELOG.md"
44
19
  ],
45
- "peerDependencies": {
46
- "@earendil-works/pi-ai": "*",
47
- "@earendil-works/pi-coding-agent": "*",
48
- "@earendil-works/pi-tui": "*"
49
- },
50
20
  "dependencies": {
51
- "@juanibiapina/pi-extension-settings": "^0.10.0"
52
- }
21
+ "@earendil-works/chord": "^0.87.1",
22
+ "@earendil-works/pi-tui": "^0.87.1",
23
+ "@silvia-odwyer/photon-node": "0.3.4",
24
+ "jiti": "2.7.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=22.19.0"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "license": "MIT"
53
33
  }