@north-light/cli 0.3.539 → 0.3.541
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-memory/crouter-sdk/release-notes.md +2 -0
- package/dist/builtin-memory/northlight-apps/concepts/apps-and-sign-in.md +1 -1
- package/dist/builtin-memory/northlight-apps/concepts/connections.md +1 -1
- package/dist/builtin-memory/northlight-apps/errors.md +1 -1
- package/dist/builtin-memory/northlight-apps/guides/many-users.md +2 -2
- package/dist/core/events/outbound.d.ts +21 -0
- package/dist/core/events/outbound.js +1 -0
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/model-auth/flows.d.ts +20 -0
- package/dist/core/model-auth/flows.js +1 -1
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/errors.d.ts +2 -0
- package/dist/core/providers/errors.js +1 -1
- package/dist/core/providers/mint.js +1 -1
- package/dist/daemon/api/vendor-signin.js +25 -25
- package/dist/daemon/run-notices.js +1 -1
- package/docs/apps/concepts/apps-and-sign-in.md +1 -1
- package/docs/apps/concepts/connections.md +1 -1
- package/docs/apps/errors.md +1 -1
- package/docs/apps/guides/many-users.md +2 -2
- package/docs/sdk/release-notes.md +2 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -11,6 +11,8 @@ Changes to `@north-light/sdk` that can change what an existing app sees. Newest
|
|
|
11
11
|
|
|
12
12
|
## 0.3.539
|
|
13
13
|
|
|
14
|
+
`exchangeIdentity` now returns the verified identity when the directory's token answer also carries a runtime connection. It used to throw `oauth_exchange_mismatch`, so no member could sign in to an app held by a team with a runtime grant (the directory gives that app a connection on every sign-in). The SDK revokes the unneeded refresh token best-effort; revocation removes only that token, never the grant. `exchangeCode` still refuses a sign-in-only answer.
|
|
15
|
+
|
|
14
16
|
A run started with no `kind` and no `model`, in a profile with no `default_kind` (every app's own default profile), now starts as the `general` kind on the `medium` rung. It used to start as `developer` on that kind's model (`openai/medium`). `profile.list` reports `general` as such a profile's `default_kind`. On an app-paid grant the run lands on the app's `medium` inference rung, where it used to land on `strong`. Pass `model` on `run.start` to keep another rung.
|
|
15
17
|
|
|
16
18
|
Profiles name the machine environment they run on. Additive: no request or response an existing app sends or reads changes.
|
|
@@ -76,7 +76,7 @@ Then set your own session for the person. The SDK does not manage your sessions.
|
|
|
76
76
|
|
|
77
77
|
### Scopes decide what comes back
|
|
78
78
|
|
|
79
|
-
A sign-in that asks only for `openid`, optionally with `email` and `profile`, creates no connection. Finish it with `oauth.exchangeIdentity(...)`, which returns the verified `Identity` (`sub`, and `email`, `name` and `picture` when approved and present). A sign-in that asks for runtime scopes such as `crtr:llm` finishes with `exchangeCode`.
|
|
79
|
+
A sign-in that asks only for `openid`, optionally with `email` and `profile`, creates no connection. Finish it with `oauth.exchangeIdentity(...)`, which returns the verified `Identity` (`sub`, and `email`, `name` and `picture` when approved and present). A sign-in that asks for runtime scopes such as `crtr:llm` finishes with `exchangeCode`. `exchangeCode` refuses a sign-in-only answer with `oauth_exchange_mismatch`; `exchangeIdentity` accepts an answer that also carries a connection, returns the identity, and revokes the unneeded refresh token.
|
|
80
80
|
|
|
81
81
|
### When the directory refuses to sign someone in
|
|
82
82
|
|
|
@@ -119,7 +119,7 @@ It needs the app registered with the `app_token_for_user` operation, and an acti
|
|
|
119
119
|
|
|
120
120
|
## Signing in a visitor
|
|
121
121
|
|
|
122
|
-
A person who only needs to be recognized, not connected, signs in with identity only: request `openid`, optionally with `email` and `profile`, and no runtime scope. Finish with `exchangeIdentity`, not `exchangeCode`. An `Identity` is not a connection: no runtime URL, grant or refresh token, nothing goes in the store, and you keep your own session. To act for that person later, send them through `authorizeUrl` again with the runtime scopes you need.
|
|
122
|
+
A person who only needs to be recognized, not connected, signs in with identity only: request `openid`, optionally with `email` and `profile`, and no runtime scope. Finish with `exchangeIdentity`, not `exchangeCode`. An `Identity` is not a connection: no runtime URL, grant or refresh token (if the directory answers with one anyway, `exchangeIdentity` revokes the refresh token), nothing goes in the store, and you keep your own session. To act for that person later, send them through `authorizeUrl` again with the runtime scopes you need.
|
|
123
123
|
|
|
124
124
|
## Checked and not checked
|
|
125
125
|
|
|
@@ -18,7 +18,7 @@ Codes the SDK raises itself are listed in `sdkErrorCodes`, and `CrouterError.cod
|
|
|
18
18
|
| Code | Means | What to do |
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| `id_token_invalid` | An ID token failed verification (signature, issuer, audience, expiry, missing or mismatched nonce), or a sign-in-only answer had no ID token. A JWKS that can't be fetched or is malformed is not this code: that error is thrown as is. | Restart the sign-in. |
|
|
21
|
-
| `oauth_exchange_mismatch` |
|
|
21
|
+
| `oauth_exchange_mismatch` | `exchangeCode` got a sign-in-only answer (no connection). | Finish a sign-in-only request with `exchangeIdentity` ([[northlight-apps/guides/many-users]]). |
|
|
22
22
|
| `oauth_nonce_missing` | `exchangeIdentity` was called without the `nonce` from `authorizeUrl`. | Save the nonce in the visitor's session and pass it back. |
|
|
23
23
|
| `oauth_state_mismatch` | The callback's `state` was missing or differed from the saved one. | Refuse the callback. |
|
|
24
24
|
| `not_connected` | `oauth.client(userId)` or a refresh found no stored connection for that person. | Show "sign in again". |
|
|
@@ -18,7 +18,7 @@ Keep the connection and refresh token on the server, never in a visitor's browse
|
|
|
18
18
|
|
|
19
19
|
## Signing in a visitor
|
|
20
20
|
|
|
21
|
-
A visitor who only needs to be recognized, not connected, signs in with identity only. Request `openid`, adding `email` for their email and `profile` for their name and photo, and no runtime scope. The directory shows one line ("Know who you are on Northlight"), writes no grant, and answers with an ID token and an access token usable only at `/userinfo`: no refresh token, grant or runtime URL. Finish that sign-in with `exchangeIdentity`, not `exchangeCode`:
|
|
21
|
+
A visitor who only needs to be recognized, not connected, signs in with identity only. Request `openid`, adding `email` for their email and `profile` for their name and photo, and no runtime scope. The directory shows one line ("Know who you are on Northlight"), writes no grant, and answers with an ID token and an access token usable only at `/userinfo`: no refresh token, grant or runtime URL. (An app held by a team with a runtime grant gets a connection on every sign-in; `exchangeIdentity` still returns only the identity and revokes the unneeded refresh token.) Finish that sign-in with `exchangeIdentity`, not `exchangeCode`:
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
const {url, codeVerifier, state, nonce} = await oauth.authorizeUrl({scopes: ['openid', 'email', 'profile'], state: sessionState});
|
|
@@ -36,7 +36,7 @@ visitor.picture; // an https URL, present only when 'profile' was approved
|
|
|
36
36
|
|
|
37
37
|
The SDK checks the ID token's ES256 signature against the directory's JWKS, its issuer, that `aud` is your client id, that it has not expired, and that its `nonce` equals the one `authorizeUrl` returned. `nonce` is required. Any failure throws `CrouterError` code `id_token_invalid`. If the directory's JWKS can't be fetched or is malformed, that error is thrown as is.
|
|
38
38
|
|
|
39
|
-
An `Identity` is not a connection. Nothing is stored in a `ConnectionStore` and nothing refreshes; your app keeps its own session for the visitor, and the visitor's runtime can't be called on their behalf. To act for the visitor later, send them through `authorizeUrl` again with the runtime scopes you need and finish with `exchangeCode`, which returns a `Connection`.
|
|
39
|
+
An `Identity` is not a connection. Nothing is stored in a `ConnectionStore` and nothing refreshes; your app keeps its own session for the visitor, and the visitor's runtime can't be called on their behalf. To act for the visitor later, send them through `authorizeUrl` again with the runtime scopes you need and finish with `exchangeCode`, which returns a `Connection`. `exchangeCode` refuses a sign-in-only answer with code `oauth_exchange_mismatch`.
|
|
40
40
|
|
|
41
41
|
## Whose action a call records
|
|
42
42
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EventField } from './types.js';
|
|
2
|
+
/** A failed outbound request: where it was going and which step of the caller's work it was. The thrown fetch error is the `cause`. */
|
|
3
|
+
export declare class OutboundError extends Error {
|
|
4
|
+
readonly step: string;
|
|
5
|
+
readonly url: string;
|
|
6
|
+
constructor(step: string, url: string, cause: unknown);
|
|
7
|
+
}
|
|
8
|
+
/** `fetch` whose thrown error (DNS, socket, timeout) is rethrown as an `OutboundError` naming the step and destination, with the original as its cause. */
|
|
9
|
+
export declare function outboundFetch(step: string, url: string, init?: RequestInit): Promise<Response>;
|
|
10
|
+
/** A request that completed with a failure status: names the step and destination, with the status as the cause. */
|
|
11
|
+
export declare function outboundRefusal(step: string, url: string, message: string): OutboundError;
|
|
12
|
+
/** Read a response body as JSON; a failed read (connection dropped after the headers) is an `OutboundError`, and a body that is not JSON is a `SyntaxError` as before. */
|
|
13
|
+
export declare function outboundJson(step: string, url: string, response: Response): Promise<unknown>;
|
|
14
|
+
/** Log fields for a caught outbound-request error: the step, the destination host and path (never the query string, headers or bodies), and the
|
|
15
|
+
* `Error.cause` chain, including the members of an `AggregateError` (what Node's `fetch` reports for a refused connection), each with its name, message,
|
|
16
|
+
* code, errno, syscall and hostname. The step and destination come from the first `OutboundError` met; `target` supplies them when the caller never wrapped
|
|
17
|
+
* the request. */
|
|
18
|
+
export declare function describeOutboundFailure(error: unknown, target?: {
|
|
19
|
+
step: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
}): Record<string, EventField>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var d=Object.defineProperty;var u=(t,e)=>d(t,"name",{value:e,configurable:!0});class a extends Error{static{u(this,"OutboundError")}step;url;constructor(e,n,r){super(r instanceof Error?r.message:String(r),{cause:r}),this.step=e,this.url=n,this.name="OutboundError"}}async function w(t,e,n){try{return await fetch(e,n)}catch(r){throw new a(t,e,r)}}u(w,"outboundFetch");function b(t,e,n){return new a(t,e,new Error(n))}u(b,"outboundRefusal");async function E(t,e,n){let r;try{r=await n.text()}catch(o){throw new a(t,e,o)}return JSON.parse(r)}u(E,"outboundJson");const p=8,g=["code","errno","syscall","hostname"];function y(t){if(typeof t!="object"||t===null)return{name:"Error",message:String(t)};const e=t,n={name:typeof e.name=="string"?e.name:"Error",message:typeof e.message=="string"?e.message:String(t)};for(const r of g){const o=e[r];(typeof o=="string"||typeof o=="number")&&(n[r]=o)}return n}u(y,"link");function x(t,e){let n=e,r=!1;const o=[],f=new Set,i=[t];for(;i.length>0&&o.length<p;){const s=i.shift();if(s===void 0||f.has(s)||(f.add(s),s instanceof a?(r||(n={step:s.step,url:s.url}),r=!0):o.push(y(s)),typeof s!="object"||s===null))continue;const{cause:h,errors:l}=s;i.push(h),s instanceof AggregateError&&Array.isArray(l)&&i.push(...l.slice(0,p))}const c={cause_chain:o};if(n&&(c.step=n.step,n.url!==void 0)){const s=new URL(n.url);c.host=s.host,c.path=s.pathname}return c}u(x,"describeOutboundFailure");export{a as OutboundError,x as describeOutboundFailure,w as outboundFetch,E as outboundJson,b as outboundRefusal};
|
package/dist/core/grants/sync.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var S=Object.defineProperty;var r=(e,n)=>S(e,"name",{value:n,configurable:!0});import{createPrivateKey as E,randomUUID as j,sign as x}from"node:crypto";import{readFileSync as $}from"node:fs";import{join as T}from"node:path";import{crtrHome as A}from"../canvas/paths.js";import{isUnsandboxedMachine as g}from"../runtime/unsandboxed.js";import{emitEvent as p}from"../events/emit.js";import{describeOutboundFailure as N,outboundFetch as v,outboundJson as h,outboundRefusal as k}from"../events/outbound.js";import{allGrants as O,dependenciesOf as P,inferenceEndpointOf as R,replaceGrants as G}from"./mirror.js";import{onGrantSync as J}from"./on-sync.js";import{PersonUnresolvedError as B,personId as D,resolvePerson as F,retireLocalPerson as M,setPersonId as U}from"../canvas/person.js";const ie=300;function C(e,n){const t=Math.floor(Date.now()/1e3),o=Buffer.from(JSON.stringify({alg:"ES256",typ:"JWT",kid:e.kid})).toString("base64url"),u=Buffer.from(JSON.stringify({iss:e.client_id,sub:e.client_id,aud:n,iat:t,exp:t+300,jti:j()})).toString("base64url"),i=`${o}.${u}`,s=x("sha256",Buffer.from(i),{key:E({key:e.jwk,format:"jwk"}),dsaEncoding:"ieee-p1363"});return`${i}.${s.toString("base64url")}`}r(C,"assertion");function y(){const e=JSON.parse($(T(A(),"secrets","runtime-client.json"),"utf8"));if(!e.jwk||e.alg!=="ES256"||!e.kid||!e.client_id||!e.runtime||e.jwk.kid!==e.kid||e.jwk.kty!=="EC"||e.jwk.crv!=="P-256"||!e.jwk.d)throw new TypeError("invalid runtime client key");return e}r(y,"runtimeClient");function se(e=y()){return e.runtime.startsWith("runtime:")?e.runtime.slice(8):e.runtime}r(se,"runtimeId");function I(e){return`This machine was removed from your Northlight account. To use it again, create a join token at ${e.replace(/\/$/,"")}/account/machines, and run \`crtr sys join\` from a plain terminal.`}r(I,"machineRevokedMessage");class w extends Error{static{r(this,"MachineRevokedError")}constructor(n){super(I(n)),this.name="MachineRevokedError"}}async function L(e,n){const t=y(),o=`${e.replace(/\/$/,"")}/token`,u=new URLSearchParams({grant_type:"client_credentials",client_id:t.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:C(t,o),...n===void 0?{}:{audience:n}}),i=await v("directory_token",o,{method:"POST",body:u,signal:AbortSignal.timeout(1e4)});if(!i.ok)throw i.status===401&&g()&&await W(i)?new w(e):k("directory_token",o,`directory token request failed: ${i.status}`);const s=await h("directory_token",o,i);if(typeof s.access_token!="string")throw new TypeError("directory token response has no access_token");return{access_token:s.access_token,expires_at:q(s.access_token,s.expires_in)}}r(L,"mintDirectoryToken");async function W(e){try{return(await e.json()).error==="invalid_client"}catch{return!1}}r(W,"isInvalidClient");function q(e,n){if(typeof n=="number"&&Number.isFinite(n)&&n>0)return Date.now()+n*1e3;try{const t=JSON.parse(Buffer.from(e.split(".")[1]??"","base64url").toString("utf8"));if(typeof t.exp=="number")return t.exp*1e3}catch{}return Date.now()+3e5}r(q,"tokenExpiry");function z(e,n){if(!e||typeof e!="object")throw new TypeError("invalid grant sync response");const t=e,o=n.runtime.startsWith("runtime:")?n.runtime:`runtime:${n.runtime}`;if(!(t.runtime===o||g()&&typeof t.runtime=="string"&&t.runtime.startsWith("runtime:")&&t.grantee!=="app:terminal")||typeof t.id!="string"||typeof t.sub!="string"||typeof t.grantee!="string"||!Number.isInteger(t.rev)||!Array.isArray(t.scopes)||!Array.isArray(t.resolved_scopes)||!["active","revoked","removed"].includes(t.status??"")||typeof t.synced_at!="string")throw new TypeError("invalid grant sync response");const s=e.revisions?.find(a=>a.rev===t.rev)?.approved_at;if(typeof s!="string")throw new TypeError("grant revision has no approval time");const l=t.payer==="app"?"app":"user";return{...t,spend_cap:t.spend_cap??null,expires_at:t.expires_at??null,revoked_at:t.revoked_at??null,approved_at:s,payer:l,dependencies:P(t.dependencies),inference_endpoint:l==="app"?R(t.inference_endpoint):null,run_notice_url:typeof t.run_notice_url=="string"&&t.run_notice_url!==""?t.run_notice_url:null}}r(z,"responseGrant");async function H(){try{D();return}catch(t){if(!(t instanceof B))throw t}const e=await F();if(e===null)return;U(e.id);const n=await M();p({level:"info",event:"daemon.person.resolved",fields:{person_id:e.id,source:e.source}}),p({level:"info",event:"daemon.person.retired",fields:{person_id:n.person,retired:n.retired,columns:n.columns}})}r(H,"resolveLatePerson");let _;function ae(e){return _?_(e):Promise.resolve()}r(ae,"requestGrantSync");function ce(e,n=t=>{}){let t;const o=e.replace(/\/$/,""),u=r(async()=>{const a=y(),c=await L(o),f=await v("grant_sync",`${o}/v1/runtime/grants`,{headers:{Authorization:`Bearer ${c.access_token}`},signal:AbortSignal.timeout(1e4)});if(!f.ok)throw k("grant_sync",`${o}/v1/runtime/grants`,`directory grant sync failed: ${f.status}`);const d=await h("grant_sync",`${o}/v1/runtime/grants`,f);if(!Array.isArray(d.grants)||typeof d.synced_at!="string")throw new TypeError("invalid grant sync response");const b=await O();await G(d.grants.map(m=>z(m,a)),d.synced_at,o),p({level:"info",event:"daemon.grants.synced",fields:{grants:d.grants.length,synced_at:d.synced_at}}),await H();try{await J(b,n)}catch(m){p({level:"error",event:"daemon.grants.on_sync_failed",error:m})}},"run");let i;const s=r(()=>{const a=u().catch(c=>{throw c instanceof w?p({level:"error",event:"daemon.grants.machine_revoked",error:c}):p({level:"error",event:"daemon.grants.sync_failed",error:c,fields:N(c)}),c}).finally(()=>{t===a&&(t=void 0)});return t=a,a},"start"),l=r((a={})=>t?a.fresh?(i??=t.catch(()=>{}).then(()=>(i=void 0,t??s())),i):t:s(),"sync");return _=l,l}r(ce,"createGrantSync");export{ie as GRANT_SYNC_INTERVAL_S,w as MachineRevokedError,C as assertion,ce as createGrantSync,I as machineRevokedMessage,L as mintDirectoryToken,ae as requestGrantSync,y as runtimeClient,se as runtimeId};
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
import { RunError } from '../runs/operations.js';
|
|
2
|
+
import type { EventField } from '../events/types.js';
|
|
1
3
|
import { type ManagedProviderId } from '../subscription-state.js';
|
|
2
4
|
export declare const MODEL_AUTH_FLOW_TTL_S = 900;
|
|
5
|
+
/** Why a sign-in failed, as the directory's signup page is told: our runtime could not reach the provider (`network`), the code expired (`expired`),
|
|
6
|
+
* the provider answered with a rejection (`refused`), or a lost flow or any other runtime fault (`runtime`). */
|
|
7
|
+
export type SignInFailureCategory = 'network' | 'expired' | 'refused' | 'runtime';
|
|
8
|
+
/** A failed request to a vendor during sign-in, with the step and destination for the log. */
|
|
9
|
+
export declare class VendorSignInError extends RunError {
|
|
10
|
+
readonly category: 'network' | 'refused';
|
|
11
|
+
readonly step: string;
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly httpStatus?: number | undefined;
|
|
14
|
+
constructor(category: 'network' | 'refused', step: string, url: string, message: string, cause?: unknown, httpStatus?: number | undefined);
|
|
15
|
+
logFields(): Record<string, EventField>;
|
|
16
|
+
}
|
|
3
17
|
export type ModelAuthFlowStatus = 'pending' | 'connected' | 'failed' | 'expired';
|
|
4
18
|
export interface StartedFlow {
|
|
5
19
|
flow_id: string;
|
|
@@ -44,6 +58,12 @@ export interface FlowView {
|
|
|
44
58
|
}
|
|
45
59
|
/** A flow's current state, or undefined when it is unknown or long finished. */
|
|
46
60
|
export declare function readModelAuthFlow(flowId: string): FlowView | undefined;
|
|
61
|
+
/** How a finished flow failed, for the log and the redirect to the directory. An unknown or lost flow is a `runtime` failure. */
|
|
62
|
+
export declare function modelAuthFlowFailure(flowId: string): {
|
|
63
|
+
category: SignInFailureCategory;
|
|
64
|
+
step?: string;
|
|
65
|
+
fields: Record<string, EventField>;
|
|
66
|
+
};
|
|
47
67
|
/** Wait up to `waitMs` for a flow to leave `pending`; answers its state either way. */
|
|
48
68
|
export declare function waitModelAuthFlow(flowId: string, waitMs: number, signal?: AbortSignal): Promise<FlowView | undefined>;
|
|
49
69
|
/** Test seam: forget every flow (a daemon restart does this by construction). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var J=Object.defineProperty;var a=(e,t)=>J(e,"name",{value:t,configurable:!0});import{createHash as V,randomBytes as T,randomUUID as K}from"node:crypto";import{createServer as X}from"node:http";import{RunError as u}from"../runs/operations.js";import{ANTHROPIC_PROVIDER_ID as w,OPENAI_CHATGPT_PROVIDER_ID as m,OPENAI_CODEX_PROVIDER_ID as k,commitManagedLogin as Y,openAIDeviceId as Z,readSubscriptionPool as W}from"../subscription-state.js";const R=900,L="9d1c250a-e61b-44d9-88ed-5944d1962f5e",Q="https://claude.ai/oauth/authorize",ee="https://console.anthropic.com/oauth/code/callback",N={port:53692,path:"/callback",redirect:"http://localhost:53692/callback"},te="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",I="app_EMoamEEZ73f0CkXaXp7hrann",ne="https://auth.openai.com/codex/device",U="https://auth.openai.com/deviceauth/callback",D={port:1455,path:"/auth/callback",redirect:"http://localhost:1455/auth/callback"},re="openid profile email offline_access",ie=900,G="https://api.openai.com/auth",oe="dynamic_agent_client",ae="crtr",se="https://auth.openai.com/api/accounts/authorize",H="https://api.openai.com/v1",b={port:1455,path:"/auth/callback",redirect:"http://127.0.0.1:1455/auth/callback"},E="chatgpt.tokens.use.direct",ce=`openid profile email offline_access resource.invoke ${E}`,de=180*1e3,ue=a(()=>process.env.CRTR_MODEL_AUTH_ANTHROPIC_TOKEN_URL||"https://platform.claude.com/v1/oauth/token","anthropicTokenUrl"),P=a(()=>(process.env.CRTR_MODEL_AUTH_OPENAI_BASE||"https://auth.openai.com").replace(/\/$/,""),"openaiBase"),_=new Map,y=new Map;function C(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}a(C,"base64url");function he(){const e=C(T(32));return{verifier:e,challenge:C(V("sha256").update(e).digest())}}a(he,"pkce");const M="openai-chatgpt";function $(e){if(e==="openai")return k;if(e===M)return m;if(e===w||e===k)return e;throw new u(400,"invalid_request","provider must be anthropic, openai-codex, or openai-chatgpt","provider")}a($,"managedProvider");function Ie(e){return e===m?M:e}a(Ie,"modelAuthApiProvider");function Ee(e){return $(e)}a(Ee,"modelAuthProvider");function f(e,t,n){if(e.status==="pending"){e.status=t,n!==void 0&&(e.message=n),clearTimeout(e.timer),e.abort.abort(),e.server?.close(),e.server?.closeIdleConnections(),y.get(e.provider)===e.id&&y.delete(e.provider);for(const i of e.waiters)i();e.waiters.clear(),setTimeout(()=>{_.get(e.id)===e&&_.delete(e.id)},6e4).unref()}}a(f,"settle");async function S(e,t){if(e.status!=="pending")throw new Error("the sign-in ended before the credential was written");await Y(e.provider,e.label,{access:t.access,refresh:t.refresh,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{},...t.email?{email:t.email}:{},lastRateLimitedAt:e.lastRateLimitedAt,autoLabel:e.label===void 0})}a(S,"writeCredential");async function j(e,t,n){const i=await fetch(ue(),{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({grant_type:"authorization_code",client_id:L,code:t,state:n,redirect_uri:e.redirectUri,code_verifier:e.verifier}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!i.ok)throw new Error(`Anthropic token exchange failed (${i.status})`);const r=await i.json();if(typeof r.access_token!="string"||typeof r.refresh_token!="string"||typeof r.expires_in!="number"||!Number.isFinite(r.expires_in))throw new Error("Anthropic token exchange returned no credential");await S(e,{access:r.access_token,refresh:r.refresh_token,expires:Date.now()+r.expires_in*1e3-300*1e3})}a(j,"exchangeAnthropic");function pe(e){const t=e.split(".")[1];let n;try{n=JSON.parse(Buffer.from(t??"","base64url").toString("utf8"))[G]?.chatgpt_account_id}catch{n=void 0}if(typeof n!="string"||n==="")throw new Error("OpenAI token carries no ChatGPT account id");return n}a(pe,"openaiAccountId");async function q(e,t,n,i){const r=await fetch(`${P()}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:I,code:t,code_verifier:n,redirect_uri:i}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!r.ok)throw new Error(`OpenAI token exchange failed (${r.status})`);const o=await r.json();if(typeof o.access_token!="string"||typeof o.refresh_token!="string"||typeof o.expires_in!="number")throw new Error("OpenAI token exchange returned no credential");await S(e,{access:o.access_token,refresh:o.refresh_token,expires:Date.now()+o.expires_in*1e3,accountId:pe(o.access_token)})}a(q,"exchangeOpenAI");function F(e){try{const t=JSON.parse(Buffer.from(e?.split(".")[1]??"","base64url").toString("utf8"));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}}a(F,"jwtPayload");function le(...e){for(const t of e){const n=F(t)?.[G];if(typeof n?.chatgpt_account_id=="string"&&n.chatgpt_account_id)return n.chatgpt_account_id}}a(le,"chatgptAccountId");function z(e,t){const n=e.searchParams.get("error");if(n)throw new u(400,"invalid_request",`ChatGPT authorization failed: ${n}`,"input");const i=e.searchParams.get("code");if(!i)throw new u(400,"invalid_request","the redirect URL has no authorization code","input");if(e.searchParams.get("state")!==t)throw new u(400,"invalid_request","the pasted redirect URL belongs to a different sign-in","state");const r=e.searchParams.get("client_id")?.trim();if(!r)throw new u(400,"invalid_request","the redirect URL carries no issued client id","input");return{code:i,clientId:r}}a(z,"chatgptCallback");async function B(e,t,n){const i=await fetch(`${P()}/api/accounts/oauth/token`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:n,code:t,code_verifier:e.verifier,redirect_uri:e.redirectUri,resource:H}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!i.ok)throw new Error(`ChatGPT token exchange failed (${i.status})`);const r=await i.json();if(typeof r.access_token!="string"||!r.access_token||typeof r.refresh_token!="string"||!r.refresh_token||typeof r.expires_in!="number"||!Number.isFinite(r.expires_in)||r.expires_in<=0||typeof r.id_token!="string"||!r.id_token)throw new Error("ChatGPT token exchange returned no credential");if(!(typeof r.scope=="string"?r.scope.trim().split(/\s+/):[]).includes(E))throw new Error(`ChatGPT sign-in did not grant ${E}`);const s=le(r.access_token,r.id_token),h=F(r.id_token)?.email;await S(e,{access:r.access_token,refresh:r.refresh_token,expires:Date.now()+r.expires_in*1e3-de,clientId:n,...s?{accountId:s}:{},...typeof h=="string"&&h?{email:h}:{}})}a(B,"exchangeChatGPT");function fe(e,t){return new Promise((n,i)=>{if(t.aborted){i(new Error("flow ended"));return}const r=a(()=>{clearTimeout(o),i(new Error("flow ended"))},"onAbort"),o=setTimeout(()=>{t.removeEventListener("abort",r),n()},e);t.addEventListener("abort",r,{once:!0})})}a(fe,"sleep");async function _e(e,t){const n=Math.min(e.expiresAt,Date.now()+ie*1e3);let i=Math.max(1e3,Math.floor(t.intervalS*1e3));try{for(;Date.now()<n&&(await fe(Math.min(i,Math.max(0,n-Date.now())),e.abort.signal),!(Date.now()>=n));){const r=await fetch(`${P()}/api/accounts/deviceauth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:t.deviceAuthId,user_code:t.userCode}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(r.ok){const h=await r.json();if(typeof h.authorization_code!="string"||typeof h.code_verifier!="string")throw new Error("OpenAI device approval returned no code");await q(e,h.authorization_code,h.code_verifier,U),f(e,"connected");return}if(r.status===403||r.status===404)continue;const o=await r.text().catch(()=>"");let s;try{const h=JSON.parse(o).error;s=typeof h=="object"&&h!==null?h.code:h}catch{s=void 0}if(s!=="deviceauth_authorization_pending"){if(s==="slow_down"){i+=5e3;continue}throw new Error(`OpenAI device sign-in failed (${r.status})`)}}f(e,"expired","the OpenAI code expired")}catch(r){e.status==="pending"&&f(e,"failed",r instanceof Error?r.message:String(r))}}a(_e,"pollDevice");function O(e,t){const n=X((i,r)=>{const o=new URL(i.url??"/","http://localhost"),s=a((l,p)=>{r.writeHead(l,{"Content-Type":"text/plain; charset=utf-8",Connection:"close"}),r.end(p)},"reply");if(o.pathname!==t.path){s(404,"Not found.");return}const h=o.searchParams.get("code");if(o.searchParams.get("state")!==e.state||!h){s(400,"Sign-in could not be completed: state mismatch or missing code.");return}let d="";if(e.provider===m)try{d=z(o,e.state).clientId}catch(l){s(400,`Sign-in could not be completed: ${l instanceof Error?l.message:String(l)}.`);return}if(e.status!=="pending"||e.exchanging){s(409,"This sign-in has already finished.");return}e.exchanging=!0,(e.provider===w?j(e,h,e.state):e.provider===m?B(e,h,d):q(e,h,e.verifier,e.redirectUri)).then(()=>{s(200,"Signed in. You can close this window."),f(e,"connected")},l=>{s(502,"The vendor refused the sign-in."),f(e,"failed",l instanceof Error?l.message:String(l))})});return e.server=n,new Promise((i,r)=>{n.once("error",o=>r(new u(409,"conflict",`cannot listen on localhost:${t.port} for the vendor redirect (${o.code??o.message})`,"redirect"))),n.listen(t.port,"127.0.0.1",()=>i())})}a(O,"listenLoopback");async function Ce(e){const t=$(e.provider);if(e.redirect!==void 0&&e.redirect!=="loopback")throw new u(400,"invalid_request",'redirect must be "loopback" when present',"redirect");if(e.label!==void 0&&typeof e.label!="string")throw new u(400,"invalid_request","label must be a string when present","label");const n=typeof e.label=="string"&&e.label.trim()?e.label.trim():void 0,i=y.get(t);if(i!==void 0){const p=_.get(i);p&&f(p,"failed","replaced by a newer sign-in")}const{verifier:r,challenge:o}=he(),s=e.redirect==="loopback",h=Date.now()+R*1e3,d={id:K(),provider:t,kind:s?"loopback":t===w?"code_page":t===m?"redirect_paste":"device",state:t===w?r:T(16).toString("hex"),verifier:r,redirectUri:t===w?s?N.redirect:ee:t===m?b.redirect:s?D.redirect:U,expiresAt:h,status:"pending",...e.jti?{jti:e.jti}:{},...n?{label:n}:{},lastRateLimitedAt:Math.max(0,...W(t).map(p=>p.lastRateLimitedAt)),abort:new AbortController,timer:setTimeout(()=>f(d,"expired","the sign-in expired"),R*1e3),waiters:new Set,exchanging:!1};d.timer.unref(),ge(d);const A=a(()=>new u(409,"conflict","a newer sign-in for this provider replaced this one","provider"),"replacedDuringStart"),l={flow_id:d.id,provider:t,expires_at:new Date(h).toISOString()};try{if(t===w){const c=new URLSearchParams;if(c.set("code","true"),c.set("client_id",L),c.set("response_type","code"),c.set("redirect_uri",d.redirectUri),c.set("scope",te),c.set("code_challenge",o),c.set("code_challenge_method","S256"),c.set("state",d.state),s&&await O(d,N),d.status!=="pending")throw A();return{...l,url:`${Q}?${c.toString()}`,instructions:s?"Sign in to Claude in your browser; this finishes by itself.":"Sign in to Claude, then copy the code Claude shows and paste it here."}}if(t===m){const c=new URL(se);if(c.search=new URLSearchParams({client_id:oe,agent_name_hint:ae,ext_agent_host_id:`urn:uuid:${Z()}`,response_type:"code",redirect_uri:d.redirectUri,resource:H,scope:ce,state:d.state,code_challenge:o,code_challenge_method:"S256",nonce:C(T(32))}).toString(),s&&await O(d,b),d.status!=="pending")throw A();return{...l,url:c.toString(),instructions:s?"Sign in with ChatGPT in your browser; this finishes by itself. If it does not, paste the final redirect URL here.":`Sign in with ChatGPT, then copy the full URL your browser lands on (it starts with ${b.redirect} and may show a connection error) and paste it here.`}}if(s){const c=new URL("https://auth.openai.com/oauth/authorize");if(c.searchParams.set("response_type","code"),c.searchParams.set("client_id",I),c.searchParams.set("redirect_uri",d.redirectUri),c.searchParams.set("scope",re),c.searchParams.set("code_challenge",o),c.searchParams.set("code_challenge_method","S256"),c.searchParams.set("state",d.state),c.searchParams.set("id_token_add_organizations","true"),c.searchParams.set("codex_cli_simplified_flow","true"),c.searchParams.set("originator","pi"),await O(d,D),d.status!=="pending")throw A();return{...l,url:c.toString(),instructions:"Sign in to ChatGPT in your browser; this finishes by itself."}}const p=await fetch(`${P()}/api/accounts/deviceauth/usercode`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:I}),signal:AbortSignal.timeout(3e4)});if(!p.ok)throw new u(502,"upstream_refused",`OpenAI refused to issue a device code (${p.status})`);const g=await p.json(),v=typeof g.interval=="string"?Number(g.interval.trim()):g.interval;if(typeof g.device_auth_id!="string"||typeof g.user_code!="string"||typeof v!="number"||!Number.isFinite(v)||v<0)throw new u(502,"upstream_refused","OpenAI returned an invalid device code");if(d.status!=="pending")throw A();return _e(d,{deviceAuthId:g.device_auth_id,userCode:g.user_code,intervalS:v}),{...l,url:ne,user_code:g.user_code,instructions:"Open the link, sign in to ChatGPT, and enter the code. The code expires in 15 minutes."}}catch(p){throw f(d,"failed",p instanceof Error?p.message:String(p)),_.delete(d.id),p instanceof u?p:new u(502,"upstream_refused",`vendor sign-in could not start: ${p instanceof Error?p.message:String(p)}`)}}a(Ce,"startModelAuthFlow");function ge(e){_.set(e.id,e),y.set(e.provider,e.id)}a(ge,"register");function Se(e){const t=_.get(e);if(t)return f(t,"failed","cancelled"),x(t)}a(Se,"cancelModelAuthFlow");function me(e){const t=e.trim();if(!t)return{};try{const n=new URL(t);return{code:n.searchParams.get("code")??void 0,state:n.searchParams.get("state")??void 0}}catch{}if(t.includes("#")){const[n,i]=t.split("#",2);return{code:n,state:i}}if(t.includes("code=")){const n=new URLSearchParams(t);return{code:n.get("code")??void 0,state:n.get("state")??void 0}}return{code:t}}a(me,"parseCodeInput");function we(e){const t=typeof e=="string"?_.get(e):void 0;if(!t||t.status!=="pending")throw new u(400,"invalid_request","no in-flight sign-in has this flow_id","flow_id");return t}a(we,"pendingFlow");async function Oe(e,t){const n=we(e);if(n.provider===m)return Ae(n,t);if(n.kind!=="code_page")throw new u(400,"invalid_request","this sign-in completes without a pasted code","flow_id");if(n.exchanging)throw new u(400,"invalid_request","this sign-in is already being completed","flow_id");if(typeof t!="string")throw new u(400,"invalid_request","input must be the code Claude shows","input");const{code:i,state:r}=me(t);if(!i)throw new u(400,"invalid_request","input must be the code Claude shows","input");if(r!==void 0&&r!==n.state)throw new u(400,"invalid_request","the pasted code belongs to a different sign-in","state");n.exchanging=!0;try{await j(n,i,r??n.state)}catch(o){throw f(n,"failed",o instanceof Error?o.message:String(o)),new u(502,"upstream_refused","Anthropic refused the code")}return f(n,"connected"),{flow_id:n.id,provider:n.provider,status:"connected"}}a(Oe,"completeModelAuthFlow");async function Ae(e,t){if(e.exchanging)throw new u(400,"invalid_request","this sign-in is already being completed","flow_id");const n=new URL(b.redirect);let i;try{i=typeof t=="string"?new URL(t.trim()):void 0}catch{i=void 0}if(!i||i.origin!==n.origin||i.pathname!==n.pathname)throw new u(400,"invalid_request",`input must be the full redirect URL starting with ${b.redirect}`,"input");const{code:r,clientId:o}=z(i,e.state);e.exchanging=!0;try{await B(e,r,o)}catch(s){throw f(e,"failed",s instanceof Error?s.message:String(s)),new u(502,"upstream_refused","OpenAI refused the ChatGPT sign-in")}return f(e,"connected"),{flow_id:e.id,provider:e.provider,status:"connected"}}a(Ae,"completeChatGPTFlow");function x(e){return{flow_id:e.id,provider:e.provider,status:e.status,...e.message?{message:e.message}:{},...e.jti?{jti:e.jti}:{}}}a(x,"view");function xe(e){const t=_.get(e);return t?x(t):void 0}a(xe,"readModelAuthFlow");async function ke(e,t,n){const i=_.get(e);if(i)return i.status==="pending"&&t>0&&!n?.aborted&&await new Promise(r=>{const o=a(()=>{clearTimeout(s),n?.removeEventListener("abort",o),i.waiters.delete(o),r()},"done"),s=setTimeout(o,t);n?.addEventListener("abort",o,{once:!0}),i.waiters.add(o)}),x(i)}a(ke,"waitModelAuthFlow");function Re(){for(const e of _.values())f(e,"failed","reset");_.clear(),y.clear()}a(Re,"resetModelAuthFlowsForTest");export{R as MODEL_AUTH_FLOW_TTL_S,M as OPENAI_CHATGPT_API_PROVIDER,Se as cancelModelAuthFlow,Oe as completeModelAuthFlow,Ie as modelAuthApiProvider,Ee as modelAuthProvider,xe as readModelAuthFlow,Re as resetModelAuthFlowsForTest,Ce as startModelAuthFlow,ke as waitModelAuthFlow};
|
|
1
|
+
var Z=Object.defineProperty;var s=(e,t)=>Z(e,"name",{value:t,configurable:!0});import{createHash as W,randomBytes as C,randomUUID as Q}from"node:crypto";import{createServer as ee}from"node:http";import{RunError as u}from"../runs/operations.js";import{describeOutboundFailure as te}from"../events/outbound.js";import{ANTHROPIC_PROVIDER_ID as y,OPENAI_CHATGPT_PROVIDER_ID as w,OPENAI_CODEX_PROVIDER_ID as D,commitManagedLogin as ne,openAIDeviceId as re,readSubscriptionPool as ie}from"../subscription-state.js";const $=900,G="9d1c250a-e61b-44d9-88ed-5944d1962f5e",oe="https://claude.ai/oauth/authorize",ae="https://console.anthropic.com/oauth/code/callback",H={port:53692,path:"/callback",redirect:"http://localhost:53692/callback"},se="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",E="app_EMoamEEZ73f0CkXaXp7hrann",ce="https://auth.openai.com/codex/device",M="https://auth.openai.com/deviceauth/callback",F={port:1455,path:"/auth/callback",redirect:"http://localhost:1455/auth/callback"},de="openid profile email offline_access",ue=900,q="https://api.openai.com/auth",he="dynamic_agent_client",pe="crtr",le="https://auth.openai.com/api/accounts/authorize",j="https://api.openai.com/v1",b={port:1455,path:"/auth/callback",redirect:"http://127.0.0.1:1455/auth/callback"},O="chatgpt.tokens.use.direct",fe=`openid profile email offline_access resource.invoke ${O}`,_e=180*1e3,ge=s(()=>process.env.CRTR_MODEL_AUTH_ANTHROPIC_TOKEN_URL||"https://platform.claude.com/v1/oauth/token","anthropicTokenUrl"),S=s(()=>(process.env.CRTR_MODEL_AUTH_OPENAI_BASE||"https://auth.openai.com").replace(/\/$/,""),"openaiBase");class A extends u{static{s(this,"VendorSignInError")}category;step;url;httpStatus;constructor(t,n,i,o,r,c){super(502,"upstream_refused",o),this.category=t,this.step=n,this.url=i,this.httpStatus=c,r!==void 0&&(this.cause=r)}logFields(){return{category:this.category,...this.httpStatus===void 0?{}:{http_status:this.httpStatus},...te(this.cause??this,{step:this.step,url:this.url})}}}async function P(e,t,n){try{return await fetch(t,n)}catch(i){throw new A("network",e,t,`could not reach ${new URL(t).host}${new URL(t).pathname}: ${i instanceof Error?i.message:String(i)}`,i)}}s(P,"vendorFetch");async function x(e,t,n){let i;try{i=await n.text()}catch(o){throw new A("network",e,t,`connection to ${new URL(t).host}${new URL(t).pathname} dropped while reading the answer: ${o instanceof Error?o.message:String(o)}`,o)}try{return JSON.parse(i)}catch{throw _(e,t,`${new URL(t).host}${new URL(t).pathname} answered with a body that is not JSON`,n.status)}}s(x,"vendorJson");const _=s((e,t,n,i)=>new A("refused",e,t,n,void 0,i),"refusal"),g=new Map,T=new Map;function R(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}s(R,"base64url");function me(){const e=R(C(32));return{verifier:e,challenge:R(W("sha256").update(e).digest())}}s(me,"pkce");const z="openai-chatgpt";function J(e){if(e==="openai")return D;if(e===z)return w;if(e===y||e===D)return e;throw new u(400,"invalid_request","provider must be anthropic, openai-codex, or openai-chatgpt","provider")}s(J,"managedProvider");function Re(e){return e===w?z:e}s(Re,"modelAuthApiProvider");function Le(e){return J(e)}s(Le,"modelAuthProvider");function f(e,t,n,i){if(e.status==="pending"){e.status=t,n!==void 0&&(e.message=n),i instanceof A&&(e.failure=i),clearTimeout(e.timer),e.abort.abort(),e.server?.close(),e.server?.closeIdleConnections(),T.get(e.provider)===e.id&&T.delete(e.provider);for(const o of e.waiters)o();e.waiters.clear(),setTimeout(()=>{g.get(e.id)===e&&g.delete(e.id)},6e4).unref()}}s(f,"settle");async function L(e,t){if(e.status!=="pending")throw new Error("the sign-in ended before the credential was written");await ne(e.provider,e.label,{access:t.access,refresh:t.refresh,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{},...t.email?{email:t.email}:{},lastRateLimitedAt:e.lastRateLimitedAt,autoLabel:e.label===void 0})}s(L,"writeCredential");async function B(e,t,n){const i=ge(),o=await P("token_exchange",i,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({grant_type:"authorization_code",client_id:G,code:t,state:n,redirect_uri:e.redirectUri,code_verifier:e.verifier}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!o.ok)throw _("token_exchange",i,`Anthropic token exchange failed (${o.status})`,o.status);const r=await x("token_exchange",i,o);if(typeof r.access_token!="string"||typeof r.refresh_token!="string"||typeof r.expires_in!="number"||!Number.isFinite(r.expires_in))throw _("token_exchange",i,"Anthropic token exchange returned no credential");await L(e,{access:r.access_token,refresh:r.refresh_token,expires:Date.now()+r.expires_in*1e3-300*1e3})}s(B,"exchangeAnthropic");function we(e){const t=e.split(".")[1];let n;try{n=JSON.parse(Buffer.from(t??"","base64url").toString("utf8"))[q]?.chatgpt_account_id}catch{n=void 0}if(typeof n!="string"||n==="")throw new Error("OpenAI token carries no ChatGPT account id");return n}s(we,"openaiAccountId");async function V(e,t,n,i){const o=`${S()}/oauth/token`,r=await P("token_exchange",o,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:E,code:t,code_verifier:n,redirect_uri:i}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!r.ok)throw _("token_exchange",o,`OpenAI token exchange failed (${r.status})`,r.status);const c=await x("token_exchange",o,r);if(typeof c.access_token!="string"||typeof c.refresh_token!="string"||typeof c.expires_in!="number")throw _("token_exchange",o,"OpenAI token exchange returned no credential");await L(e,{access:c.access_token,refresh:c.refresh_token,expires:Date.now()+c.expires_in*1e3,accountId:we(c.access_token)})}s(V,"exchangeOpenAI");function K(e){try{const t=JSON.parse(Buffer.from(e?.split(".")[1]??"","base64url").toString("utf8"));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}}s(K,"jwtPayload");function ye(...e){for(const t of e){const n=K(t)?.[q];if(typeof n?.chatgpt_account_id=="string"&&n.chatgpt_account_id)return n.chatgpt_account_id}}s(ye,"chatgptAccountId");function X(e,t){const n=e.searchParams.get("error");if(n)throw new u(400,"invalid_request",`ChatGPT authorization failed: ${n}`,"input");const i=e.searchParams.get("code");if(!i)throw new u(400,"invalid_request","the redirect URL has no authorization code","input");if(e.searchParams.get("state")!==t)throw new u(400,"invalid_request","the pasted redirect URL belongs to a different sign-in","state");const o=e.searchParams.get("client_id")?.trim();if(!o)throw new u(400,"invalid_request","the redirect URL carries no issued client id","input");return{code:i,clientId:o}}s(X,"chatgptCallback");async function Y(e,t,n){const i=`${S()}/api/accounts/oauth/token`,o=await P("token_exchange",i,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:n,code:t,code_verifier:e.verifier,redirect_uri:e.redirectUri,resource:j}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(!o.ok)throw _("token_exchange",i,`ChatGPT token exchange failed (${o.status})`,o.status);const r=await x("token_exchange",i,o);if(typeof r.access_token!="string"||!r.access_token||typeof r.refresh_token!="string"||!r.refresh_token||typeof r.expires_in!="number"||!Number.isFinite(r.expires_in)||r.expires_in<=0||typeof r.id_token!="string"||!r.id_token)throw _("token_exchange",i,"ChatGPT token exchange returned no credential");if(!(typeof r.scope=="string"?r.scope.trim().split(/\s+/):[]).includes(O))throw _("token_exchange",i,`ChatGPT sign-in did not grant ${O}`);const p=ye(r.access_token,r.id_token),a=K(r.id_token)?.email;await L(e,{access:r.access_token,refresh:r.refresh_token,expires:Date.now()+r.expires_in*1e3-_e,clientId:n,...p?{accountId:p}:{},...typeof a=="string"&&a?{email:a}:{}})}s(Y,"exchangeChatGPT");function ve(e,t){return new Promise((n,i)=>{if(t.aborted){i(new Error("flow ended"));return}const o=s(()=>{clearTimeout(r),i(new Error("flow ended"))},"onAbort"),r=setTimeout(()=>{t.removeEventListener("abort",o),n()},e);t.addEventListener("abort",o,{once:!0})})}s(ve,"sleep");async function be(e,t){const n=Math.min(e.expiresAt,Date.now()+ue*1e3);let i=Math.max(1e3,Math.floor(t.intervalS*1e3));try{for(;Date.now()<n&&(await ve(Math.min(i,Math.max(0,n-Date.now())),e.abort.signal),!(Date.now()>=n));){const o=`${S()}/api/accounts/deviceauth/token`,r=await P("poll",o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:t.deviceAuthId,user_code:t.userCode}),signal:AbortSignal.any([e.abort.signal,AbortSignal.timeout(3e4)])});if(r.ok){const a=await x("poll",o,r);if(typeof a.authorization_code!="string"||typeof a.code_verifier!="string")throw _("poll",o,"OpenAI device approval returned no code");await V(e,a.authorization_code,a.code_verifier,M),f(e,"connected");return}if(r.status===403||r.status===404)continue;let c;try{c=await r.text()}catch(a){throw new A("network","poll",o,`connection to ${new URL(o).host}${new URL(o).pathname} dropped while reading the answer: ${a instanceof Error?a.message:String(a)}`,a)}let p;try{const a=JSON.parse(c).error;p=typeof a=="object"&&a!==null?a.code:a}catch{p=void 0}if(p!=="deviceauth_authorization_pending"){if(p==="slow_down"){i+=5e3;continue}throw _("poll",o,`OpenAI device sign-in failed (${r.status})`,r.status)}}f(e,"expired","the OpenAI code expired")}catch(o){e.status==="pending"&&f(e,"failed",o instanceof Error?o.message:String(o),o)}}s(be,"pollDevice");function U(e,t){const n=ee((i,o)=>{const r=new URL(i.url??"/","http://localhost"),c=s((l,h)=>{o.writeHead(l,{"Content-Type":"text/plain; charset=utf-8",Connection:"close"}),o.end(h)},"reply");if(r.pathname!==t.path){c(404,"Not found.");return}const p=r.searchParams.get("code");if(r.searchParams.get("state")!==e.state||!p){c(400,"Sign-in could not be completed: state mismatch or missing code.");return}let a="";if(e.provider===w)try{a=X(r,e.state).clientId}catch(l){c(400,`Sign-in could not be completed: ${l instanceof Error?l.message:String(l)}.`);return}if(e.status!=="pending"||e.exchanging){c(409,"This sign-in has already finished.");return}e.exchanging=!0,(e.provider===y?B(e,p,e.state):e.provider===w?Y(e,p,a):V(e,p,e.verifier,e.redirectUri)).then(()=>{c(200,"Signed in. You can close this window."),f(e,"connected")},l=>{c(502,"The vendor refused the sign-in."),f(e,"failed",l instanceof Error?l.message:String(l),l)})});return e.server=n,new Promise((i,o)=>{n.once("error",r=>o(new u(409,"conflict",`cannot listen on localhost:${t.port} for the vendor redirect (${r.code??r.message})`,"redirect"))),n.listen(t.port,"127.0.0.1",()=>i())})}s(U,"listenLoopback");async function Ue(e){const t=J(e.provider);if(e.redirect!==void 0&&e.redirect!=="loopback")throw new u(400,"invalid_request",'redirect must be "loopback" when present',"redirect");if(e.label!==void 0&&typeof e.label!="string")throw new u(400,"invalid_request","label must be a string when present","label");const n=typeof e.label=="string"&&e.label.trim()?e.label.trim():void 0,i=T.get(t);if(i!==void 0){const h=g.get(i);h&&f(h,"failed","replaced by a newer sign-in")}const{verifier:o,challenge:r}=me(),c=e.redirect==="loopback",p=Date.now()+$*1e3,a={id:Q(),provider:t,kind:c?"loopback":t===y?"code_page":t===w?"redirect_paste":"device",state:t===y?o:C(16).toString("hex"),verifier:o,redirectUri:t===y?c?H.redirect:ae:t===w?b.redirect:c?F.redirect:M,expiresAt:p,status:"pending",...e.jti?{jti:e.jti}:{},...n?{label:n}:{},lastRateLimitedAt:Math.max(0,...ie(t).map(h=>h.lastRateLimitedAt)),abort:new AbortController,timer:setTimeout(()=>f(a,"expired","the sign-in expired"),$*1e3),waiters:new Set,exchanging:!1};a.timer.unref(),Ae(a);const v=s(()=>new u(409,"conflict","a newer sign-in for this provider replaced this one","provider"),"replacedDuringStart"),l={flow_id:a.id,provider:t,expires_at:new Date(p).toISOString()};try{if(t===y){const d=new URLSearchParams;if(d.set("code","true"),d.set("client_id",G),d.set("response_type","code"),d.set("redirect_uri",a.redirectUri),d.set("scope",se),d.set("code_challenge",r),d.set("code_challenge_method","S256"),d.set("state",a.state),c&&await U(a,H),a.status!=="pending")throw v();return{...l,url:`${oe}?${d.toString()}`,instructions:c?"Sign in to Claude in your browser; this finishes by itself.":"Sign in to Claude, then copy the code Claude shows and paste it here."}}if(t===w){const d=new URL(le);if(d.search=new URLSearchParams({client_id:he,agent_name_hint:pe,ext_agent_host_id:`urn:uuid:${re()}`,response_type:"code",redirect_uri:a.redirectUri,resource:j,scope:fe,state:a.state,code_challenge:r,code_challenge_method:"S256",nonce:R(C(32))}).toString(),c&&await U(a,b),a.status!=="pending")throw v();return{...l,url:d.toString(),instructions:c?"Sign in with ChatGPT in your browser; this finishes by itself. If it does not, paste the final redirect URL here.":`Sign in with ChatGPT, then copy the full URL your browser lands on (it starts with ${b.redirect} and may show a connection error) and paste it here.`}}if(c){const d=new URL("https://auth.openai.com/oauth/authorize");if(d.searchParams.set("response_type","code"),d.searchParams.set("client_id",E),d.searchParams.set("redirect_uri",a.redirectUri),d.searchParams.set("scope",de),d.searchParams.set("code_challenge",r),d.searchParams.set("code_challenge_method","S256"),d.searchParams.set("state",a.state),d.searchParams.set("id_token_add_organizations","true"),d.searchParams.set("codex_cli_simplified_flow","true"),d.searchParams.set("originator","pi"),await U(a,F),a.status!=="pending")throw v();return{...l,url:d.toString(),instructions:"Sign in to ChatGPT in your browser; this finishes by itself."}}const h=`${S()}/api/accounts/deviceauth/usercode`,I=await P("device_code_start",h,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:E}),signal:AbortSignal.timeout(3e4)});if(!I.ok)throw _("device_code_start",h,`OpenAI refused to issue a device code (${I.status})`,I.status);const m=await x("device_code_start",h,I),k=typeof m.interval=="string"?Number(m.interval.trim()):m.interval;if(typeof m.device_auth_id!="string"||typeof m.user_code!="string"||typeof k!="number"||!Number.isFinite(k)||k<0)throw _("device_code_start",h,"OpenAI returned an invalid device code");if(a.status!=="pending")throw v();return be(a,{deviceAuthId:m.device_auth_id,userCode:m.user_code,intervalS:k}),{...l,url:ce,user_code:m.user_code,instructions:"Open the link, sign in to ChatGPT, and enter the code. The code expires in 15 minutes."}}catch(h){throw f(a,"failed",h instanceof Error?h.message:String(h),h),g.delete(a.id),h instanceof u?h:new u(502,"upstream_refused",`vendor sign-in could not start: ${h instanceof Error?h.message:String(h)}`)}}s(Ue,"startModelAuthFlow");function Ae(e){g.set(e.id,e),T.set(e.provider,e.id)}s(Ae,"register");function Ne(e){const t=g.get(e);if(t)return f(t,"failed","cancelled"),N(t)}s(Ne,"cancelModelAuthFlow");function Pe(e){const t=e.trim();if(!t)return{};try{const n=new URL(t);return{code:n.searchParams.get("code")??void 0,state:n.searchParams.get("state")??void 0}}catch{}if(t.includes("#")){const[n,i]=t.split("#",2);return{code:n,state:i}}if(t.includes("code=")){const n=new URLSearchParams(t);return{code:n.get("code")??void 0,state:n.get("state")??void 0}}return{code:t}}s(Pe,"parseCodeInput");function xe(e){const t=typeof e=="string"?g.get(e):void 0;if(!t||t.status!=="pending")throw new u(400,"invalid_request","no in-flight sign-in has this flow_id","flow_id");return t}s(xe,"pendingFlow");async function De(e,t){const n=xe(e);if(n.provider===w)return Te(n,t);if(n.kind!=="code_page")throw new u(400,"invalid_request","this sign-in completes without a pasted code","flow_id");if(n.exchanging)throw new u(400,"invalid_request","this sign-in is already being completed","flow_id");if(typeof t!="string")throw new u(400,"invalid_request","input must be the code Claude shows","input");const{code:i,state:o}=Pe(t);if(!i)throw new u(400,"invalid_request","input must be the code Claude shows","input");if(o!==void 0&&o!==n.state)throw new u(400,"invalid_request","the pasted code belongs to a different sign-in","state");n.exchanging=!0;try{await B(n,i,o??n.state)}catch(r){throw f(n,"failed",r instanceof Error?r.message:String(r),r),new u(502,"upstream_refused","Anthropic refused the code")}return f(n,"connected"),{flow_id:n.id,provider:n.provider,status:"connected"}}s(De,"completeModelAuthFlow");async function Te(e,t){if(e.exchanging)throw new u(400,"invalid_request","this sign-in is already being completed","flow_id");const n=new URL(b.redirect);let i;try{i=typeof t=="string"?new URL(t.trim()):void 0}catch{i=void 0}if(!i||i.origin!==n.origin||i.pathname!==n.pathname)throw new u(400,"invalid_request",`input must be the full redirect URL starting with ${b.redirect}`,"input");const{code:o,clientId:r}=X(i,e.state);e.exchanging=!0;try{await Y(e,o,r)}catch(c){throw f(e,"failed",c instanceof Error?c.message:String(c),c),new u(502,"upstream_refused","OpenAI refused the ChatGPT sign-in")}return f(e,"connected"),{flow_id:e.id,provider:e.provider,status:"connected"}}s(Te,"completeChatGPTFlow");function N(e){return{flow_id:e.id,provider:e.provider,status:e.status,...e.message?{message:e.message}:{},...e.jti?{jti:e.jti}:{}}}s(N,"view");function $e(e){const t=g.get(e);return t?N(t):void 0}s($e,"readModelAuthFlow");function Ge(e){const t=g.get(e);return t?.status==="expired"?{category:"expired",fields:{category:"expired"}}:t?.failure?{category:t.failure.category,step:t.failure.step,fields:t.failure.logFields()}:{category:"runtime",fields:{category:"runtime"}}}s(Ge,"modelAuthFlowFailure");async function He(e,t,n){const i=g.get(e);if(i)return i.status==="pending"&&t>0&&!n?.aborted&&await new Promise(o=>{const r=s(()=>{clearTimeout(c),n?.removeEventListener("abort",r),i.waiters.delete(r),o()},"done"),c=setTimeout(r,t);n?.addEventListener("abort",r,{once:!0}),i.waiters.add(r)}),N(i)}s(He,"waitModelAuthFlow");function Me(){for(const e of g.values())f(e,"failed","reset");g.clear(),T.clear()}s(Me,"resetModelAuthFlowsForTest");export{$ as MODEL_AUTH_FLOW_TTL_S,z as OPENAI_CHATGPT_API_PROVIDER,A as VendorSignInError,Ne as cancelModelAuthFlow,De as completeModelAuthFlow,Re as modelAuthApiProvider,Ge as modelAuthFlowFailure,Le as modelAuthProvider,$e as readModelAuthFlow,Me as resetModelAuthFlowsForTest,Ue as startModelAuthFlow,He as waitModelAuthFlow};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),$(
|
|
1
|
+
var ue=Object.defineProperty;var a=(e,n)=>ue(e,"name",{value:n,configurable:!0});import{existsSync as g,readdirSync as W,readFileSync as S,rmSync as $,statSync as k}from"node:fs";import{join as d}from"node:path";import{validateCommandManifest as de}from"../../api/command-manifest/manifest.js";import{crtrHome as z}from"../canvas/paths.js";import{withCanvasWrite as G}from"../canvas/db.js";import{bundleVersion as ce,isInstalledGeneration as fe,recordCheckedGeneration as le}from"../command-plugins/bundle.js";import{readConfig as q,readState as pe,updateConfig as me,updateState as ve}from"../config.js";import{emitEvent as m}from"../events/emit.js";import{describeOutboundFailure as he,outboundFetch as we,outboundRefusal as V,OutboundError as ge}from"../events/outbound.js";import{withExclusiveDirectoryLockAsync as Z}from"../exclusive-lock.js";import{atomicWriteText as x,ensureDir as T,pathExists as $e}from"../fs-utils.js";import{reconcilePackages as L}from"../graph/package-reconcile.js";import{readPluginManifest as C}from"../manifest.js";import{bundleSwapLockPath as J}from"../plugin-swap-lock.js";import{pluginsDir as ye,userScopeRoot as K}from"../scope.js";import{listInstalledPlugins as be}from"../resolver.js";import{localGrants as j}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const A="[A-Za-z0-9_-]{1,64}",_e=new RegExp(`^(${A}):(${A})$`),Q=new RegExp(`^${A}$`),Pe=3e4,X=3e4;function Y(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=_e.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}a(Y,"providerIdsFromGrants");function v(e){return d(ye("user"),e)}a(v,"pluginRoot");function R(){return d(z(),"providers")}a(R,"providersRoot");function ee(e,n){return d(R(),e,`v${n}`)}a(ee,"majorDir");function B(e){if(!Q.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}a(B,"assertId");function E(){const e=q("user").runtime?.issuer;if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e.replace(/\/+$/,"")}a(E,"directoryBase");async function ne(e,n){const r=`${E()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await we("bundle_fetch",r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(Pe)})}catch(i){return{status:"failed",message:`could not reach ${r}: ${i instanceof Error?i.message:String(i)}`,cause:i}}if(t.status===304){if(n.ifNoneMatch===void 0){const i=`HTTP 304 from ${r} without a conditional request`;return{status:"failed",message:i,cause:V("bundle_fetch",r,i)}}return{status:304}}if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300){await t.body?.cancel().catch(()=>{});{const i=`HTTP ${t.status} from ${r}`;return{status:"failed",message:i,cause:V("bundle_fetch",r,i)}}}let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(i){return{status:"failed",message:`reading ${r} failed: ${i instanceof Error?i.message:String(i)}`,cause:new ge("bundle_fetch",r,i)}}const s=t.headers.get("etag");return{status:200,raw:o,...s!==null?{etag:s}:{}}}a(ne,"fetchProviderBundle");const I=new WeakSet;class M extends Error{static{a(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function re(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}a(re,"wrongBundle");function D(e,n,r){m({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof M?{issues:r.issues}:{}},error:r})}a(D,"logInvalid");function te(e,n,r){m({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r instanceof Error?r.message:String(r),...he(r instanceof Error?r.cause:void 0)}})}a(te,"logFailure");async function oe(){return import("../../commands/pkg/plugin-manage.js")}a(oe,"pluginManage");const y=new Map,b=a(e=>`${z()}\0${e}`,"installKey");function Se(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}a(Se,"publicInstallCause");function Ee(e){if(h(e)===void 0)return y.get(b(e))??{status:"installing"}}a(Ee,"providerInstallState");function Ie(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${Ee(e)?.error??"bundle installation pending"}`)}a(Ie,"unavailableProviderError");function h(e){const n=C(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&$e(d(v(e),"commands.json"))){try{if(k(d(v(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:n.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:n.transport.endpoint}}}}a(h,"installedManifest");async function N(e,n,r=!1){const t=K();h(e)===void 0&&y.set(b(e),{status:"installing"});try{return T(d(t,"tmp")),await Z(J(t,e),async()=>{const o=v(e),s=C(o);if(s!==null&&s.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const i=h(e)!==void 0;if(i&&r)return"unchanged";const f=i&&n?pe("user").plugins[e]?.etag:void 0,u=await ne(e,f!==void 0?{ifNoneMatch:f}:{});if(u.status===304)return"unchanged";if(u.status===404||u.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${u.message})`,{param:"provider"});if(u.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${u.message}`,{cause:u.cause});const l=ce(u.raw);if(i&&fe(e,"user",o,l))return le("user",e,u.etag,{swapped:!1}),"unchanged";const{installPluginArchive:w}=await oe();try{await w(e,u.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:u.etag,enable:!i,bundle:{endpoint:`${E()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:E()},accept:a(p=>re(e,void 0,p),"accept")})}catch(p){D(e,void 0,p);const U=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw I.add(U),U}try{await G(async p=>await L(p))}catch(p){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return m({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:i}}),y.delete(b(e)),i?"updated":"installed"},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw h(e)===void 0&&y.set(b(e),{status:"install_failed",error:Se(o)}),o instanceof c?o:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),Ie(e))}}a(N,"refreshInstalled");async function en(e){B(e),h(e)===void 0&&await N(e,!1,!0)}a(en,"ensureProviderBundle");async function nn(e,n){return B(e),n.version!==void 0&&h(e)?.provider.major!==n.version?O(e,n.version,n.conditional):N(e,n.conditional)}a(nn,"refreshProviderBundle");async function ae(e,n,r){const{validatePluginArchive:t}=await oe(),o=await t(r);if(o.bundle===void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(i=>i.message).join("; ")}`,o.issues);const s=re(e,n,o.bundle);if(s!==void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${s}`,[]);if(o.bundle.transport===void 0)throw new M(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}a(ae,"validateArchive");function Me(e){T(e),x(d(e,"retired"),`${new Date().toISOString()}
|
|
2
|
+
`),$(d(e,"bundle.tar"),{force:!0}),$(d(e,"etag"),{force:!0})}a(Me,"retire");function ie(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}a(ie,"retiredError");async function O(e,n,r){const t=ee(e,n),o=d(t,"bundle.tar"),s=g(o),i=s&&r&&g(d(t,"etag"))?S(d(t,"etag"),"utf8"):void 0,f=await ne(e,{version:n,...i!==void 0?{ifNoneMatch:i}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Me(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ie(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`,{cause:f.cause});try{await ae(e,n,f.raw)}catch(l){D(e,n,l);const w=new c(s?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw I.add(w),w}T(t),$(d(t,"retired"),{force:!0});const u=s&&Buffer.from(f.raw).equals(S(o));return u||x(o,f.raw),f.etag!==void 0?x(d(t,"etag"),f.etag):$(d(t,"etag"),{force:!0}),u?"unchanged":s?"updated":"installed"}a(O,"refreshPinned");function ke(){const e=[],n=R();if(!g(n))return e;for(const r of W(n)){if(!Q.test(r))continue;let t;try{t=W(d(n,r))}catch{continue}for(const o of t){const s=/^v([1-9][0-9]*)$/.exec(o);s&&g(d(n,r,o,"bundle.tar"))&&e.push([r,Number(s[1])])}}return e}a(ke,"cachedMajors");function xe(e){const n=de(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}a(xe,"readCommands");const _=new Map,H=new Map;function Te(e,n){const r=k(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=H.get(e);if(o?.stamp===t)return o.commands;const s=xe(JSON.parse(S(n,"utf8")));return H.set(e,{stamp:t,commands:s}),s}a(Te,"installedCommands");async function rn(e,n){B(e);const r=h(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const l=v(e),w=Te(e,d(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:w,transport:r.transport,source:"installed",root:l}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=ee(e,n);if(g(d(t,"retired")))throw ie(e,n);const o=d(t,"bundle.tar");g(o)||await O(e,n,!1);const s=`${e}/${n}`,i=k(o),f=`${i.mtimeMs}:${i.size}`;let u=_.get(s)?.stamp===f?_.get(s).bundle:void 0;if(u===void 0){try{u=await ae(e,n,new Uint8Array(S(o)))}catch(l){throw D(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}_.set(s,{stamp:f,bundle:u})}return{id:e,major:n,provider:u.provider,commands:u.commands,transport:u.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:u.transport.endpoint},source:"pinned"}}a(rn,"resolveProviderBundle");let P=null,F=!1;function Ce(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${E()}/providers/${encodeURIComponent(n)}/plugin`}a(Ce,"isDirectoryBundle");async function se(e){return Y(await j()).includes(e)}a(se,"grantNames");async function je(e){await Z(J(K(),e),async()=>{if(!(await se(e)||!Ce(C(v(e)),e))){$(v(e),{recursive:!0,force:!0}),me("user",n=>{delete n.plugins[e]}),ve("user",n=>{delete n.plugins[e]}),$(d(R(),e),{recursive:!0,force:!0}),H.delete(e),y.delete(b(e));for(const n of[..._.keys()])n.startsWith(`${e}/`)&&_.delete(n);try{await G(async n=>await L(n))}catch(n){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:n})}m({level:"info",event:"daemon.providers.bundle_uninstalled",fields:{provider:e}})}},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}a(je,"uninstallProvider");async function Ae(){if(!(await j()).every(e=>e.status!=="active")){for(const e of be("user"))if(!(e.manifest.provider===void 0||await se(e.name)))try{await je(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}a(Ae,"uninstallDisconnected");async function Re(){if(!q("user").runtime?.issuer)return;const e=Y(await j());for(const n of e)try{await N(n,!0)}catch(r){r instanceof Object&&I.has(r)||te(n,void 0,r)}await Ae();for(const[n,r]of ke())try{await O(n,r,!0)}catch(t){if(t instanceof c&&t.code==="provider_version_retired"||t instanceof Object&&I.has(t))continue;te(n,r,t)}}a(Re,"syncOnce");function tn(){return P!==null?(F=!0,P):(P=(async()=>{try{do F=!1,await Re();while(F)}finally{P=null}})(),P)}a(tn,"syncProviderBundles");export{en as ensureProviderBundle,Y as providerIdsFromGrants,Ee as providerInstallState,nn as refreshProviderBundle,rn as resolveProviderBundle,tn as syncProviderBundles,Ie as unavailableProviderError};
|
|
@@ -72,6 +72,8 @@ export interface ProviderErrorOptions {
|
|
|
72
72
|
retry_after_s?: number;
|
|
73
73
|
question_id?: string;
|
|
74
74
|
run_id?: string;
|
|
75
|
+
/** The error behind this one, kept for the log. */
|
|
76
|
+
cause?: unknown;
|
|
75
77
|
}
|
|
76
78
|
export declare class ProviderError extends RunError {
|
|
77
79
|
readonly origin: ErrorOrigin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var m=Object.defineProperty;var _=(t,i)=>m(t,"name",{value:i,configurable:!0});import{RunError as d}from"../runs/operations.js";import{errorCodes as r}from"../../api/error-codes.js";const g={provider_error:{type:"invalid_request_error",status:400,retryable:!1},provider_rate_limited:r.provider_rate_limited,provider_unavailable:r.provider_unavailable,provider_refused:r.provider_refused,provider_not_connected:r.provider_not_connected,provider_version_retired:r.provider_version_retired,storage_unavailable:r.storage_unavailable,approval_pending:{...r.approval_pending,user_action:"answer_question"},user_denied:r.user_denied},b={unauthorized:r.unauthorized,grant_revoked:r.grant_revoked,machine_revoked:r.machine_revoked,scope_missing:r.scope_missing,permission_error:r.permission_error,invalid_request:r.invalid_request,not_found:r.not_found,not_implemented:r.not_implemented,runtime_starting:r.runtime_starting,server_error:r.server_error,provider_bad_response:r.provider_bad_response,invalid_argument:r.invalid_argument,machine_required:r.machine_required,device_offline:r.device_offline,device_timeout:r.device_timeout,device_disconnected:r.device_disconnected,peer_version_too_old:r.peer_version_too_old,outcome_unknown:{type:"invalid_request_error",status:409,retryable:!1},wrong_site:{type:"invalid_request_error",status:409,retryable:!1},site_signed_out:{type:"invalid_request_error",status:409,retryable:!1},js_exception:{type:"invalid_request_error",status:409,retryable:!1},lib_broken:{type:"server_error",status:502,retryable:!1}};function a(t,i){return g[t]??b[t]??(Object.prototype.hasOwnProperty.call(r,t)?r[t]:void 0)??{type:i===401?"authentication_error":i===403?"permission_error":i===429?"rate_limit_error":i>=500?"server_error":"invalid_request_error",status:i,retryable:i>=500}}_(a,"codeRow");class u extends d{static{_(this,"ProviderError")}origin;type;retry_after_s;question_id;user_action;run_id;constructor(i,o,n={}){const e=a(i,n.status??500);super(n.status??e.status,i,o,n.param,n.details,n.scope,e.retryable),this.type=e.type,this.origin=n.origin??"runtime",n.retry_after_s!==void 0&&(this.retry_after_s=n.retry_after_s),n.question_id!==void 0&&(this.question_id=n.question_id),n.run_id!==void 0&&(this.run_id=n.run_id),e.user_action!==void 0&&(this.user_action=e.user_action)}}function R(t){return new u("scope_missing",`missing scope ${t}`,{scope:t})}_(R,"scopeMissing");function q(t){if(!(t instanceof Error))return null;const{status:i,code:o,details:n,retryable:e}=t;return typeof i!="number"||typeof o!="string"||i<400||i>599?null:new d(i,o,t.message,void 0,n,void 0,typeof e=="boolean"?e:a(o,i).retryable)}_(q,"shaped");function h(t){if(t.code==="unauthorized"||t.code==="grant_revoked")return"reconnect";if(t.code==="scope_missing"&&t.scope)return`grant_scope:${t.scope}`}_(h,"directoryAction");function w(t,i,o,n){const e=t instanceof d?t:q(t)??new u("server_error","provider request failed"),s=e instanceof u?e:null,v=a(e.code,e.status),c=s?.run_id??n,p=s?.user_action??(e.code==="cap_exhausted"?"raise_cap":o?h(e):void 0),l=e.code==="cap_exhausted"?e.details?.reset_at:void 0,y={code:e.code,message:e.message,...e.details===void 0?{}:{details:e.details},type:s?.type??v.type,...e.param?{param:e.param}:{},origin:s?.origin??"runtime",retryable:e.retryable,...s?.retry_after_s===void 0?{}:{retry_after_s:s.retry_after_s},...typeof l=="string"?{reset_at:l}:{},request_id:i,...c?{run_id:c}:{},...s?.question_id?{question_id:s.question_id}:{},...p?{user_action:p}:{},...e.scope?{scope:e.scope}:{}},f={"Request-Id":i};return s?.retry_after_s!==void 0&&(f["Retry-After"]=String(s.retry_after_s)),{status:e.status,body:{error:y},headers:f}}_(w,"providerErrorBody");export{g as PROVIDER_CODES,u as ProviderError,a as codeRow,w as providerErrorBody,R as scopeMissing};
|
|
1
|
+
var m=Object.defineProperty;var _=(t,i)=>m(t,"name",{value:i,configurable:!0});import{RunError as d}from"../runs/operations.js";import{errorCodes as r}from"../../api/error-codes.js";const g={provider_error:{type:"invalid_request_error",status:400,retryable:!1},provider_rate_limited:r.provider_rate_limited,provider_unavailable:r.provider_unavailable,provider_refused:r.provider_refused,provider_not_connected:r.provider_not_connected,provider_version_retired:r.provider_version_retired,storage_unavailable:r.storage_unavailable,approval_pending:{...r.approval_pending,user_action:"answer_question"},user_denied:r.user_denied},b={unauthorized:r.unauthorized,grant_revoked:r.grant_revoked,machine_revoked:r.machine_revoked,scope_missing:r.scope_missing,permission_error:r.permission_error,invalid_request:r.invalid_request,not_found:r.not_found,not_implemented:r.not_implemented,runtime_starting:r.runtime_starting,server_error:r.server_error,provider_bad_response:r.provider_bad_response,invalid_argument:r.invalid_argument,machine_required:r.machine_required,device_offline:r.device_offline,device_timeout:r.device_timeout,device_disconnected:r.device_disconnected,peer_version_too_old:r.peer_version_too_old,outcome_unknown:{type:"invalid_request_error",status:409,retryable:!1},wrong_site:{type:"invalid_request_error",status:409,retryable:!1},site_signed_out:{type:"invalid_request_error",status:409,retryable:!1},js_exception:{type:"invalid_request_error",status:409,retryable:!1},lib_broken:{type:"server_error",status:502,retryable:!1}};function a(t,i){return g[t]??b[t]??(Object.prototype.hasOwnProperty.call(r,t)?r[t]:void 0)??{type:i===401?"authentication_error":i===403?"permission_error":i===429?"rate_limit_error":i>=500?"server_error":"invalid_request_error",status:i,retryable:i>=500}}_(a,"codeRow");class u extends d{static{_(this,"ProviderError")}origin;type;retry_after_s;question_id;user_action;run_id;constructor(i,o,n={}){const e=a(i,n.status??500);super(n.status??e.status,i,o,n.param,n.details,n.scope,e.retryable),this.type=e.type,this.origin=n.origin??"runtime",n.cause!==void 0&&(this.cause=n.cause),n.retry_after_s!==void 0&&(this.retry_after_s=n.retry_after_s),n.question_id!==void 0&&(this.question_id=n.question_id),n.run_id!==void 0&&(this.run_id=n.run_id),e.user_action!==void 0&&(this.user_action=e.user_action)}}function R(t){return new u("scope_missing",`missing scope ${t}`,{scope:t})}_(R,"scopeMissing");function q(t){if(!(t instanceof Error))return null;const{status:i,code:o,details:n,retryable:e}=t;return typeof i!="number"||typeof o!="string"||i<400||i>599?null:new d(i,o,t.message,void 0,n,void 0,typeof e=="boolean"?e:a(o,i).retryable)}_(q,"shaped");function h(t){if(t.code==="unauthorized"||t.code==="grant_revoked")return"reconnect";if(t.code==="scope_missing"&&t.scope)return`grant_scope:${t.scope}`}_(h,"directoryAction");function w(t,i,o,n){const e=t instanceof d?t:q(t)??new u("server_error","provider request failed"),s=e instanceof u?e:null,v=a(e.code,e.status),c=s?.run_id??n,p=s?.user_action??(e.code==="cap_exhausted"?"raise_cap":o?h(e):void 0),l=e.code==="cap_exhausted"?e.details?.reset_at:void 0,y={code:e.code,message:e.message,...e.details===void 0?{}:{details:e.details},type:s?.type??v.type,...e.param?{param:e.param}:{},origin:s?.origin??"runtime",retryable:e.retryable,...s?.retry_after_s===void 0?{}:{retry_after_s:s.retry_after_s},...typeof l=="string"?{reset_at:l}:{},request_id:i,...c?{run_id:c}:{},...s?.question_id?{question_id:s.question_id}:{},...p?{user_action:p}:{},...e.scope?{scope:e.scope}:{}},f={"Request-Id":i};return s?.retry_after_s!==void 0&&(f["Retry-After"]=String(s.retry_after_s)),{status:e.status,body:{error:y},headers:f}}_(w,"providerErrorBody");export{g as PROVIDER_CODES,u as ProviderError,a as codeRow,w as providerErrorBody,R as scopeMissing};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var E=Object.defineProperty;var c=(r,e)=>E(r,"name",{value:e,configurable:!0});import{createHash as M}from"node:crypto";import{readConfig as j}from"../config.js";import{emitEvent as S}from"../events/emit.js";import{OutboundError as A}from"../events/outbound.js";import{grantSyncStateFor as N}from"../grants/mirror.js";import{assertion as O,machineRevokedMessage as P,requestGrantSync as I,runtimeClient as L}from"../grants/sync.js";import{isUnsandboxedMachine as D}from"../runtime/unsandboxed.js";import{ProviderError as h}from"./errors.js";import{cacheKey as F,cachedToken as G,discardEpoch as U,storeToken as C}from"./token-cache.js";import{discardFile as le}from"./token-cache.js";const J=1e4,Y=[300,900];function B(r){const e=r.issuer??j("user").runtime?.issuer;if(!e)throw new h("not_found","provider calls need a directory-connected runtime");return e.replace(/\/$/,"")}c(B,"issuerOf");function g(r,e,t){return new h(r,e,{origin:"directory",details:t})}c(g,"directoryError");function H(r,e,t,i){const n=typeof e.error=="string"?e.error:null,s=typeof e.error_description=="string"?e.error_description:void 0,o={directory_status:r,...n?{directory_error:n}:{},...s?{directory_description:s}:{}},d=s?`: ${s}`:"";switch(n){case"invalid_client":return D()?{error:g("machine_revoked",P(i),o),grantRefusal:!1}:{error:g("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_request":return{error:g("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_target":return{error:g("not_found",`the directory knows no provider at ${t.audience}${d}`,o),grantRefusal:!1};case"invalid_grant":return{error:g("grant_revoked",`the directory has no active grant for ${t.grantee}${d}`,o),grantRefusal:!0};case"provider_not_connected":return{error:g("provider_not_connected",s??"The user has not connected this provider.",o),grantRefusal:!1};case"invalid_scope":{const a=typeof e.scope=="string"&&e.scope?e.scope:t.scope.length===1?t.scope[0]:void 0,l=a?`missing scope ${a}`:`the grant does not cover ${t.scope.join(" ")}`;return{error:new h("scope_missing",l,{origin:"directory",details:o,...a?{scope:a}:{}}),grantRefusal:!0}}default:return{error:g("server_error",`the directory token endpoint answered ${r}${n?` ${n}`:""}${d}`,o),grantRefusal:!1}}}c(H,"refusal");function K(r){try{return JSON.parse(Buffer.from(r.split(".")[1]??"","base64url").toString("utf8"))}catch{return{}}}c(K,"claims");function z(r){const e=r.grant_rev,t=r.grant_status;return typeof e!="number"||!Number.isInteger(e)||e<0||typeof t!="string"?null:{rev:e,status:t}}c(z,"refusedGrantState");function Q(r,e){const t=c(i=>i.status==="active"?0:1,"phase");return r.rev>e.rev||r.rev===e.rev&&t(r)>t(e)}c(Q,"newer");async function V(r,e,t){if(!e)return;const i=await N(r.grantee,r.sub)??{rev:0,status:"removed"};if(!Q(e,i))return;const n=t.syncGrants??I;Promise.resolve().then(()=>n({fresh:!0})).catch(()=>{})}c(V,"syncIfNewer");async function W(r,e={}){const t=B(e),i=`${t}/token`,n=L(),s=c(async()=>{const u=new URLSearchParams({grant_type:"client_credentials",client_id:n.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:O(n,i),audience:r.audience,sub:r.sub,grantee:r.grantee,scope:[...new Set(r.scope)].sort().join(" "),initiating_app:r.initiating_app}),p=AbortSignal.timeout(J),w=e.signal?AbortSignal.any([e.signal,p]):p,$=await(e.fetch??fetch)(i,{method:"POST",body:u,signal:w,headers:{Accept:"application/json"}});return{response:$,text:await $.text()}},"attempt");let o,d;try{for(let u=0;;u++){({response:o,text:d}=await s());const p=Y[u];if(p===void 0||![502,503,504].includes(o.status))break;await new Promise(w=>setTimeout(w,p))}}catch(u){throw e.signal?.aborted?e.signal.reason:new h("server_error",`the directory token endpoint could not be reached: ${u instanceof Error?u.message:String(u)}`,{origin:"directory",cause:new A("directory_token",i,u)})}let a=null;try{a=d?JSON.parse(d):null}catch{a=null}const l=a&&typeof a=="object"&&!Array.isArray(a)?a:{};if(!o.ok){const{error:u,grantRefusal:p}=H(o.status,l,r,t);throw u.code==="machine_revoked"&&S({level:"error",event:"daemon.grants.machine_revoked",fields:{audience:r.audience}}),S({level:"info",event:"daemon.providers.mint_refused",fields:{audience:r.audience,grantee:r.grantee,code:u.code,directory_error:typeof l.error=="string"?l.error:null,status:o.status}}),p&&await V(r,z(l),e),u}const m=l.access_token;if(typeof m!="string"||!m)throw new h("server_error","the directory token response has no access_token",{origin:"directory"});const f=K(m),x=Math.floor((e.now?.()??Date.now())/1e3),y=l.expires_in,_=typeof y=="number"&&Number.isFinite(y)&&y>0?x+Math.floor(y):null,v=typeof f.exp=="number"?f.exp:null,b=_!==null&&v!==null?Math.min(_,v):_??v;if(b===null)throw new h("server_error","the directory token response names no expiry",{origin:"directory"});const R=typeof f.jti=="string"&&f.jti?f.jti:M("sha256").update(m).digest("hex"),T=typeof f.payer=="string"&&f.payer?f.payer:r.sub;return{access_token:m,jti:R,exp:b,payer:T}}c(W,"mintOutboundToken");const k=new Map;async function ce(r,e={}){const t=F(r),i=e.now??Date.now,n=G(t,i());if(n)return{...n,key:t};let s=k.get(t);if(!s){const d=U(t);s=W(r,{...e,signal:void 0}).then(a=>(C(t,a,d),a)).finally(()=>{k.delete(t)}),s.catch(()=>{}),k.set(t,s)}return{...e.signal?await X(s,e.signal):await s,key:t}}c(ce,"outboundToken");function X(r,e){return e.aborted?Promise.reject(e.reason):new Promise((t,i)=>{const n=c(()=>i(e.reason),"onAbort");e.addEventListener("abort",n,{once:!0}),r.then(t,i).finally(()=>e.removeEventListener("abort",n))})}c(X,"abortable");export{Y as MINT_RETRY_DELAYS_MS,J as MINT_TIMEOUT_MS,le as discardFile,W as mintOutboundToken,ce as outboundToken};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var P=Object.defineProperty;var o=(e,t)=>P(e,"name",{value:t,configurable:!0});import{randomBytes as j}from"node:crypto";import{joinedOwner as N}from"../../core/directory/join.js";import{consumeHandoffJti as R,handoffJtiConsumed as z}from"../../core/model-auth/handoff-jti.js";import{completeModelAuthFlow as H,modelAuthFlowFailure as V,modelAuthProvider as F,readModelAuthFlow as k,startModelAuthFlow as I,waitModelAuthFlow as D,VendorSignInError as x}from"../../core/model-auth/flows.js";import{emitEvent as m}from"../../core/events/emit.js";import{RunError as d}from"../../core/runs/operations.js";import{LONG_POLL_MAX_WAIT_SECONDS as B}from"../../api/long-poll.js";const h="/signin/vendor",$=900,G=B*1e3,U="https://chatgpt.com/#settings/Security";function m1(e){return e===h||e.startsWith(`${h}/`)}o(m1,"isVendorSignInPath");function p(){return new d(401,"unauthorized","invalid vendor sign-in handoff")}o(p,"unauthorized");async function _(e,t){const r=await e.verifyHandoff(t);let n;try{n=N()}catch{n=null}if(r.sub!==n)throw p();return r}o(_,"verifyOwnerHandoff");function J(e){const t=e.headers.authorization;return t?.startsWith("Bearer ")&&t.length>7?t.slice(7):void 0}o(J,"bearer");function y(e,t){const r=e.getAll(t);return r.length===1?r[0]:void 0}o(y,"single");function Z(e,t,r,n){const a=new URL(e);return a.searchParams.set("status",t),r&&a.searchParams.set("reason",r),n&&a.searchParams.set("step",n),a.toString()}o(Z,"returnUrl");function v(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}o(v,"escapeHtml");function C(e,t,r){e.writeHead(t,{"Content-Type":"application/json; charset=utf-8","Cache-Control":"no-store"}),e.end(JSON.stringify(r))}o(C,"sendJson");async function q(e){const t=[];let r=0;for await(const a of e){if(r+=a.length,r>64*1024)throw new d(400,"invalid_request","request body too large");t.push(a)}let n;try{n=JSON.parse(Buffer.concat(t).toString("utf8"))}catch{throw new d(400,"invalid_request","invalid JSON body")}if(typeof n!="object"||n===null||Array.isArray(n))throw new d(400,"invalid_request","request body must be a JSON object");return n}o(q,"jsonBody");const W=`
|
|
2
2
|
:root{color-scheme:light dark;
|
|
3
3
|
--canvas:#f0f2f4;--surface:#fff;--sunken:#e5e7ea;--zone:#e5e7ea;--neutral:#e5e7ea;--neutral-hover:#d7d9dc;--neutral-pressed:#cbcdd0;
|
|
4
4
|
--accent:#217f51;--accent-hover:#1e6e46;--accent-pressed:#1a5e3c;--accent-border:#217f51;--accent-subtle:#d5f7e1;--accent-fg:#1e6e46;--on-accent:#fff;
|
|
@@ -84,12 +84,12 @@ form{display:flex;flex-direction:column;gap:.75rem}
|
|
|
84
84
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
85
85
|
@media (min-width:40rem){.top{padding:0 1.5rem}main{padding:3.5rem 1rem}}
|
|
86
86
|
@media (max-width:30rem){.card{padding:1.25rem}.btn{padding-left:.875rem;padding-right:.875rem}#device-link{font-size:.84375rem}.steps>li{gap:.75rem}.alert .act{margin-left:1.875rem}}
|
|
87
|
-
`,
|
|
87
|
+
`,X=`
|
|
88
88
|
(function(){
|
|
89
89
|
var cfg=JSON.parse(document.getElementById('cfg').textContent);
|
|
90
90
|
history.replaceState(null,'',location.pathname);
|
|
91
91
|
var auth={'Authorization':'Bearer '+cfg.handoff};
|
|
92
|
-
function back(status,reason){var u=new URL(cfg.returnTo);u.searchParams.set('status',status);if(reason)u.searchParams.set('reason',
|
|
92
|
+
function back(status,reason,step){var u=new URL(cfg.returnTo);u.searchParams.set('status',status);if(reason)u.searchParams.set('reason',reason);if(step)u.searchParams.set('step',step);location.assign(u.toString());}
|
|
93
93
|
function $(id){return document.getElementById(id);}
|
|
94
94
|
var error=$('error'),errorText=$('error-text');
|
|
95
95
|
function setError(msg){errorText.textContent=msg;error.className=msg?'err on':'err';}
|
|
@@ -106,8 +106,8 @@ if(cfg.kind==='anthropic'){
|
|
|
106
106
|
fetch(cfg.base,{method:'POST',headers:Object.assign({'Content-Type':'application/json'},auth),body:JSON.stringify({input:input.value})})
|
|
107
107
|
.then(function(r){return r.json().then(function(b){return {ok:r.ok,s:r.status,b:b};});})
|
|
108
108
|
.then(function(x){
|
|
109
|
-
if(x.ok){if(x.b.status==='connected')back('connected');else back('failed',x.b.reason);return;}
|
|
110
|
-
if(x.s===401||x.s===404){back('failed',
|
|
109
|
+
if(x.ok){if(x.b.status==='connected')back('connected');else back('failed',x.b.reason,x.b.step);return;}
|
|
110
|
+
if(x.s===401||x.s===404){back('failed','runtime');return;}
|
|
111
111
|
reject((x.b&&x.b.error&&x.b.error.message)||'That code was not accepted.');
|
|
112
112
|
})
|
|
113
113
|
.catch(function(){reject('Could not reach your runtime. Try again.');});
|
|
@@ -119,7 +119,7 @@ if(cfg.kind==='anthropic'){
|
|
|
119
119
|
if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(cfg.userCode).then(done,function(){});return;}
|
|
120
120
|
var r=document.createRange();r.selectNodeContents($('user-code'));var s=getSelection();s.removeAllRanges();s.addRange(r);
|
|
121
121
|
});
|
|
122
|
-
$('renew').addEventListener('click',function(){back('failed','
|
|
122
|
+
$('renew').addEventListener('click',function(){back('failed','expired');});
|
|
123
123
|
var deadline=Date.now()+cfg.remainingSeconds*1000,left=$('left'),said=-1,timer;
|
|
124
124
|
function tick(){
|
|
125
125
|
var s=Math.max(0,Math.ceil((deadline-Date.now())/1000));
|
|
@@ -136,50 +136,50 @@ if(cfg.kind==='anthropic'){
|
|
|
136
136
|
}
|
|
137
137
|
(function poll(){
|
|
138
138
|
fetch(cfg.base+'/wait',{headers:auth})
|
|
139
|
-
.then(function(r){if(r.status===401||r.status===404)return {status:'failed',reason:
|
|
139
|
+
.then(function(r){if(r.status===401||r.status===404)return {status:'failed',reason:'runtime'};if(!r.ok)throw new Error(String(r.status));return r.json();})
|
|
140
140
|
.then(function(b){
|
|
141
141
|
if(b.status==='pending'){poll();return;}
|
|
142
142
|
if(b.status==='connected'){back('connected');return;}
|
|
143
143
|
if(b.status==='expired'){expire();return;}
|
|
144
|
-
back('failed',b.reason);
|
|
144
|
+
back('failed',b.reason,b.step);
|
|
145
145
|
})
|
|
146
146
|
.catch(function(){setTimeout(poll,3000);});
|
|
147
147
|
})();
|
|
148
148
|
}
|
|
149
149
|
})();
|
|
150
|
-
`,q={arrow:'<path d="M7 7h10v10"/><path d="M7 17 17 7"/>',copy:'<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',clock:'<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',warn:'<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/>',alert:'<circle cx="12" cy="12" r="10"/><path d="M12 8v4"/><path d="M12 16h.01"/>',check:'<path d="M20 6 9 18l-5-5"/>'};function s(e,t=""){return`<svg class="ic${t}" viewBox="0 0 24 24" aria-hidden="true" focusable="false">${q[e]}</svg>`}i(s,"icon");const W='<svg viewBox="-77.5 12 931 931" aria-hidden="true" focusable="false"><path fill="currentColor" d="M234 294m0 0h2l-1 5-6 16-5 15-6 25-4 23-2 22-1 5v47l5 38 5 26 6 22 10 24 8 16 9 14 14 19 13 16 11 13 9 11 11 12 7 8 13 16 11 14 14 19 12 19 12 23 7 17 6 23 3 14 3-1 5-15 20-84 6-24 6-21 10-33 10-25 8-16 8-15 8-12 9-12 8-10 9-10 7-8 6-7 20-20h2l2-4 17-17 7-8 10-13 14-21 8-16 8-18 5-15 2-7 2 1 6 36 2 18v41l-4 28-5 19-7 19-10 21-8 13-10 15-13 17-11 13-12 14-9 10-7 8-9 11h-2l-2 4-9 11-14 19-8 12-15 25-11 21-12 26-9 23-5 12-13 38-12 39-5 18-5 17h-2l-9-29-9-27-14-38-13-29-8-16-11-18-10-13-7-9-25-25-3-2v-2l-3-1v-2l-4-2-10-9-8-7-14-12-20-18-13-13-9-11-12-16-12-20-12-25-7-19-6-23-4-24-1-11-1-3v-39l3-23 6-27 7-23 5-11 11-23 9-16 10-14 8-11 9-11 10-11zM430 22m0 0h2v2h2l7 8 10 11 9 11 12 16 9 15 8 16 6 15 5 19 2 11 1 12v18l-1 16-5 24-5 16-7 15-9 17-7 11-10 15-10 13-9 11-13 14-7 8-11 12-7 8-13 16-11 15-13 20-10 18-8 16-7 18-5 15-4 18-2 14-1 27 2 24 3 17 5 23 8 30 12 36 9 28 3 8 4 12v2l-4-2-68-102-13-22-11-23-9-24-7-26-4-23-2-16v-31l3-25 6-27 7-21 7-15 8-16 14-21 11-14 26-30 12-13 8-10h2l2-4 10-11 7-9 14-19 7-10 13-24 7-19 4-15 3-14 3-22 2-24v-20l-2-31zM615 384m0 0 2 1 6 36 2 18v41l-4 28-5 19-7 19-10 21-8 13-10 15-13 17-11 13-12 14-9 10-7 8-9 11h-2l-2 4-9 11-14 19-8 12-15 25-11 21-2 5h-3l-2 2h-13l-6-5-11-6 1-9h-2l2-14-2 3 1-8 9-36 6-21 10-33 10-25 8-16 8-15 8-12 9-12 8-10 9-10 7-8 6-7 20-20h2l2-4 17-17 7-8 10-13 14-21 8-16 8-18 5-15zM557 254m0 0 2 1 4 16 2 16v34l-2 18-5 20-8 24-14 28-10 16-11 16-13 15-12 14-11 13-11 14-14 19-12 20-8 16-7 19-6 20-4 20-4 35-3 41-3 52-4 28-1 3h-2l-8-42-10-52-7-33-7-43v-54l4-24 5-17 8-20 10-19 7-10 10-13 9-11 19-19 8-7 16-15 8-7 9-9 8-7 11-11 1-2h2l2-4 11-13 10-14 11-17 9-19 8-21zM185 615m0 0 4 5 6 10 9 12 2 1 3 5 5-2 1-1h8l5 5 2 3 7 3 6 3 4 5 8 6 5 3v2h2v2h6l4 3 6 1 1 8 10 3 4 2 9 2 5 6 6 1h6l8 8 7 1 9 7h5l1-4-4-6-10-13-7-9-1-3h-2l-7-9v-3l7 6 7 9 11 14 14 19 12 19 12 23 7 17 6 23 3 14 3-1 5-15 10-42 5-22h2l1-4 1 4-1 8h-2l3 4-1 8 11 6 5 4h13l4-3-1 6-8 17-9 23-5 12-13 38-12 39-5 18-5 17h-2l-9-29-9-27-14-38-13-29-8-16-11-18-10-13-7-9-25-25-3-2v-2l-3-1v-2l-4-2-10-9-8-7-14-12-20-18-13-13-9-11-12-16-9-15zM369 553m0 0h1l1 43 2 3 3 4-1 6-1 1v7l1 2 1 15 7 1 5-2 5 1v-2l3-1 4 1 1-2h-3l1-3 6-2 2 11-1 8-3 12h2v2l3-1 2-12h-2l1-4 1-1 3-21 4-21h1v10l-4 30-3 34-4 69-4 28-1 3h-2l-8-42-10-52-7-33-6-37zM430 22m0 0h2v2h2l7 8 10 11 9 11 4 5 1 5 2 4 1 7 2 2-1 3-5-1h-7l-8-2-5 1v7l-1 1h-5l-2-8-1-2-1-15h-2l-2-31zM417 598m0 0h1v10l-4 30-3 34-4 69-4 28-1 3h-2l-7-37-1-16h8l2 1v-2h2l1-2 2-35v-13l-2-3-3-5-1-8 1-9-1-4-5-2v-2l-3-1v-2l3-1 4 1 1-2h-3l1-3 6-2 2 11-1 8-3 12h2v2l3-1 2-12h-2l1-4 1-1 3-21zM417 598m0 0h1v10l-4 30-3 34-1 17h-2v15l-1 12h-1v-18l1-17v-13l-2-3-3-5-1-8 1-9-1-4-5-2v-2l-3-1v-2l3-1 4 1 1-2h-3l1-3 6-2 2 11-1 8-3 12h2v2l3-1 2-12h-2l1-4 1-1 3-21zM310 619m0 0 4 2 4 6 5 8 7 9 3 5 2-1 2 1 2 10 6-1 2 1v2h-5l3 7 9 13 6 9 3 1v-2l-4-14h3l5 16 3 8 4 12v2l-4-2-60-90zM255 597m0 0 5 3 12 16 9 11 11 13 9 11 11 12 4 5-1 3-7-7-4-1-7-4v-2l-4-2-4-4 4 1 3-1v-3l-5-2-3-5v-2h-2l-6-8v-2l-3-1-1-2-8-1 1-2-2-8-3-4v-2h-2l-4-6-3-4zM425 181m0 0 7 1 1 3 12 2 2 3 3 1v-2h3l1 3-6 2-1 3h-2v2h-2l-1 7-5 1-2-5 2-3h3l1-5-3 1-3 3-1-4-5-2v-5h-6l-3-3zM369 553m0 0h1l1 43 2 3 3 4-1 6-1 1v7l1 2 1 15 2 1h-2l3 13 2 10v5l-3-4-5-25-4-26zM340 614m0 0 1 2h2l6 20 6 18 3 15-2-1-5-15-2-4v-5h-6l-3-2-9-2 2-3v2l5-4h4 2l-1-5-3-12zM296 649m0 0 5 2 11 12 4 5-1 3-7-7-4-1-7-4v-2l-4-2-4-4 4 1 3-1zM403 625m0 0h2l2 11-1 8-2 7-2 1v-9l-1-4-5-2v-2l-3-1v-2l3-1 4 1 1-2h-3l1-3zM417 598m0 0h1v10l-4 30-2 24h-1l-1-17h-2l1-4 1-1 3-21zM163 561m0 0 2 3 5 16 2 6 2 1 3 9 2 4 4-1 1-2 1 3h-2l-2 5 3 6-1 2-3-3-11-24-4-11-2-10zM439 685m0 0 1 2h9l3 4v6l7 2 3 7-4-1-6-4-5-4v-7h-9l-2-2zM369 553m0 0h1l1 43 2 3 3 4-1 6-1 1-1 7-2-2v-7h-2zM434 687m0 0h1v6l-3 9-7 29-4 5 1-8 9-36zM317 672m0 0 6 4 9 11 11 14 6 8v2l-4-2-10-13-7-9-1-3h-2l-7-9zM276 622m0 0 5 5 11 13 7 8-1 2-7-4-3-5v-2h-2l-6-8v-2l-3-1-2-4zM310 619m0 0 4 2 4 6 5 8 7 9-1 4-4-5-8-11-7-11zM416 172m0 0 2 1-1 7h-2v5l4-1-1 3-5 2-3-1v-7l4-8zM381 664m0 0 2 4 6 27v5l-1-2h-2l-5-24zM443 655m0 0 1 4-7 25-1 2h-2l1-7 6-22h2zM163 561m0 0 2 3 5 16 2 6 2 1v5l-2 1-7-18-2-10zM490 702m0 0 1 3-10 15-3 5-4 3v-5l5-5 7-11zM387 698m0 0 1 2h2l3 16v14h-1l-5-26zM426 139m0 0 1 3-1 5-7 22-2-1 2-9 5-18h2zM466 739m0 0h2l-1 6-8 15-3 1 2-7 7-12zM411 636m0 0h2v12l-1 14h-1l-1-17h-2l1-4zM349 645m0 0h3l3 9 3 15-2-1-5-15-2-4zM335 694m0 0 5 3 9 12v2l-4-2-10-13zM386 633m0 0 7 1-3 2-4 1-2 4h-4l-2-6h5zM547 287m0 0 3 2-3 7-1 5h-2l-1 4-2-1 1-5zM557 254m0 0 2 1 3 13-4-1 1-2h-2l-1 3-1-3zM560 311m0 0 3 1-2 5-3 6-9-3v-1l9-1zM409 664m0 0h1v25h-1l-2-24zM340 614m0 0 1 2h2l4 16-4-2-3-12zM438 705m0 0 10 2 1 7-3-1-4-4h-5zM502 685m0 0 1 3-8 11-3 1 2-5zM422 191m0 0 4 1 3 4-2 4-5-3zM359 675m0 0h3l4 13-1 4-4-9zM225 599m0 0 5 2 12 6-3 1-9-4-6-3zM255 597m0 0 5 3 6 8-1 2-3-1-4-6-3-4zM414 180m0 0h1v5l4-1-1 3-5 2-3-1 1-5zM341 665m0 0 3 1 6 9-1 4-8-12zM370 608m0 0h1l1 9 3 1-2 10h-1l-2-12zM439 671m0 0 1 2-4 13h-2l1-7 3-7zM341 658m0 0 6 1v2h-5l-1 5-3-3 1-4zM373 625m0 0h2l1 9 2 1h-2l-1 5h-1l-1-6zM185 615m0 0 4 5 5 8-1 3-4-5-4-7zM434 687m0 0h1v6l-3 9-2 3 1-13zM385 686m0 0h2l2 9v5l-1-2h-2l-1-5zM390 711m0 0h2l1 8v11h-1l-2-10zM355 657m0 0 2 3 4 12v3l-3-1-2-10zM413 624m0 0h1v14l-1-2h-2l1-11zM494 120m0 0h2l2 6v10l-2-3-2-9zM163 561m0 0 2 3 4 13-1 3v-2h-2l-3-13zM489 106m0 0h2l4 10-2 4-4-11zM230 609m0 0 5 1 4 5 3 2-6-1-3-3v-2l-3-1zM392 660m0 0h2v2h2l5 5 2 1-1 4-10-10zM254 615m0 0 8 1 1 1v5l-2-1v-2l-6-2zM190 587m0 0 6 1 2-1 4 2 1 3-13-2zM417 598m0 0h1v10l-1 5-2-1 1-9zM273 545m0 0 2 3 3 8v3h-2l-3-8z"/></svg>',X={claude:'<svg viewBox="0 0 48 48" aria-hidden="true" focusable="false"><rect width="48" height="48" fill="#F3E9D7"/><path transform="translate(24 24) scale(.13) translate(-124 -124)" fill="#D97757" d="M52.4285 162.873L98.7844 136.879L99.5485 134.602L98.7844 133.334H96.4921L88.7237 132.862L62.2346 132.153L39.3113 131.207L17.0249 130.026L11.4214 128.844L6.2 121.873L6.7094 118.447L11.4214 115.257L18.171 115.847L33.0711 116.911L55.485 118.447L71.6586 119.392L95.728 121.873H99.5485L100.058 120.337L98.7844 119.392L97.7656 118.447L74.5877 102.732L49.4995 86.1905L36.3823 76.62L29.3779 71.7757L25.8121 67.2858L24.2839 57.3608L30.6515 50.2716L39.3113 50.8623L41.4763 51.4531L50.2636 58.1879L68.9842 72.7209L93.4357 90.6804L97.0015 93.6343L98.4374 92.6652L98.6571 91.9801L97.0015 89.2625L83.757 65.2772L69.621 40.8192L63.2534 30.6579L61.5978 24.632C60.9565 22.1032 60.579 20.0111 60.579 17.4246L67.8381 7.49965L71.9133 6.19995L81.7193 7.49965L85.7946 11.0443L91.9074 24.9865L101.714 46.8451L116.996 76.62L121.453 85.4816L123.873 93.6343L124.764 96.1155H126.292V94.6976L127.566 77.9197L129.858 57.3608L132.15 30.8942L132.915 23.4505L136.608 14.4708L143.994 9.62643L149.725 12.344L154.437 19.0788L153.8 23.4505L150.998 41.6463L145.522 70.1215L141.957 89.2625H143.994L146.414 86.7813L156.093 74.0206L172.266 53.698L179.398 45.6635L187.803 36.802L193.152 32.5484H203.34L210.726 43.6549L207.415 55.1159L196.972 68.3492L188.312 79.5739L175.896 96.2095L168.191 109.585L168.882 110.689L170.738 110.53L198.755 104.504L213.91 101.787L231.994 98.7149L240.144 102.496L241.036 106.395L237.852 114.311L218.495 119.037L195.826 123.645L162.07 131.592L161.696 131.893L162.137 132.547L177.36 133.925L183.855 134.279H199.774L229.447 136.524L237.215 141.605L241.8 147.867L241.036 152.711L229.065 158.737L213.019 154.956L175.45 145.977L162.587 142.787H160.805V143.85L171.502 154.366L191.242 172.089L215.82 195.011L217.094 200.682L213.91 205.172L210.599 204.699L188.949 188.394L180.544 181.069L161.696 165.118H160.422V166.772L164.752 173.152L187.803 207.771L188.949 218.405L187.294 221.832L181.308 223.959L174.813 222.777L161.187 203.754L147.305 182.486L136.098 163.345L134.745 164.2L128.075 235.42L125.019 239.082L117.887 241.8L111.902 237.31L108.718 229.984L111.902 215.452L115.722 196.547L118.779 181.541L121.58 162.873L123.291 156.636L123.14 156.219L121.773 156.449L107.699 175.752L86.304 204.699L69.3663 222.777L65.291 224.431L58.2867 220.768L58.9235 214.27L62.8713 208.48L86.304 178.705L100.44 160.155L109.551 149.507L109.462 147.967L108.959 147.924L46.6977 188.512L35.6182 189.93L30.7788 185.44L31.4156 178.115L33.7079 175.752L52.4285 162.873Z"/></svg>',chatgpt:'<svg viewBox="0 0 48 48" aria-hidden="true" focusable="false"><rect width="48" height="48" fill="#0D0D0D"/><path transform="translate(24 24) scale(2.35) translate(-7.98 -8)" fill="#FFF" d="M12.9851 6.93777C13.1163 6.54364 13.1618 6.12607 13.1186 5.71295C13.0754 5.29983 12.9444 4.90072 12.7344 4.54231C12.4232 4.00021 11.9478 3.57101 11.3768 3.31664C10.8058 3.06227 10.1687 2.99589 9.55751 3.12706C9.28184 2.81639 8.943 2.56818 8.56362 2.39905C8.18425 2.22993 7.77314 2.14379 7.3578 2.1464C6.73292 2.1449 6.1237 2.34187 5.61797 2.70892C5.11225 3.07598 4.73616 3.59415 4.54391 4.18873C4.13684 4.27206 3.75225 4.44139 3.41591 4.68539C3.07957 4.92938 2.79924 5.24241 2.59368 5.60352C2.27994 6.14413 2.14602 6.77039 2.21123 7.39205C2.27644 8.01367 2.5374 8.59851 2.95649 9.06224C2.82527 9.45637 2.77975 9.87394 2.82297 10.2871C2.8662 10.7002 2.99718 11.0993 3.20713 11.4577C3.51841 11.9998 3.99385 12.4289 4.56484 12.6833C5.13584 12.9377 5.77289 13.0041 6.38407 12.8729C6.65975 13.1836 6.99862 13.4318 7.37796 13.601C7.75734 13.7701 8.16844 13.8562 8.58379 13.8536C9.20899 13.8552 9.81853 13.6582 10.3244 13.2909C10.8304 12.9236 11.2065 12.4051 11.3986 11.8101C11.8057 11.7268 12.1903 11.5575 12.5266 11.3135C12.8629 11.0695 13.1433 10.7564 13.3488 10.3953C13.6622 9.85477 13.7958 9.22866 13.7304 8.60726C13.6651 7.98587 13.4041 7.40129 12.9851 6.93777ZM8.58472 13.0883C8.0715 13.089 7.57441 12.9093 7.18031 12.5805C7.19808 12.5708 7.22925 12.5537 7.24956 12.5413L9.58061 11.1948C9.63911 11.1615 9.6877 11.1133 9.72136 11.055C9.75505 10.9967 9.77258 10.9305 9.77217 10.8632V7.57684L10.7575 8.14576C10.7626 8.14834 10.7671 8.15214 10.7704 8.15685C10.7738 8.16157 10.7759 8.16704 10.7766 8.17275V10.8943C10.7759 11.4756 10.5448 12.0329 10.1339 12.4441C9.72308 12.8554 9.16599 13.087 8.58472 13.0883ZM3.87091 11.075C3.61385 10.6309 3.5212 10.1105 3.60918 9.60485C3.62649 9.61524 3.65673 9.6337 3.67842 9.64617L6.00947 10.9926C6.06756 11.0266 6.13365 11.0445 6.20094 11.0445C6.2682 11.0445 6.33428 11.0266 6.39238 10.9926L9.23834 9.34936V10.4872C9.23867 10.493 9.23755 10.4988 9.23509 10.5041C9.23264 10.5094 9.22892 10.5139 9.22427 10.5174L6.86782 11.878C6.36379 12.1683 5.76516 12.2467 5.20333 12.0962C4.64148 11.9457 4.1623 11.5784 3.87091 11.075ZM3.25768 5.98617C3.51363 5.54141 3.91789 5.2009 4.39966 5.02421C4.39966 5.04429 4.39851 5.07985 4.39851 5.10452V7.79747C4.3981 7.86473 4.41561 7.93087 4.44924 7.98915C4.48287 8.04739 4.5314 8.09563 4.58984 8.1289L7.4358 9.77197L6.45054 10.3409C6.44568 10.3441 6.44009 10.346 6.43429 10.3465C6.4285 10.3471 6.42265 10.3462 6.41729 10.3439L4.06062 8.98216C3.55747 8.69077 3.19039 8.21176 3.03987 7.65013C2.88935 7.08853 2.96768 6.49014 3.25768 5.98617ZM11.3527 7.86994L8.50669 6.22667L9.49198 5.65799C9.49684 5.6548 9.50243 5.65283 9.50823 5.65231C9.51402 5.65178 9.51985 5.65272 9.5252 5.655L11.8819 7.01554C12.2429 7.2241 12.5371 7.53119 12.7299 7.90087C12.9228 8.27056 13.0063 8.68752 12.9707 9.10295C12.9351 9.51839 12.7819 9.91509 12.5291 10.2466C12.2762 10.5781 11.9341 10.8307 11.5428 10.9749C11.5428 10.9546 11.5428 10.919 11.5428 10.8943V8.20137C11.5434 8.13423 11.5261 8.06814 11.4926 8.0099C11.4592 7.95165 11.4109 7.90336 11.3527 7.86994ZM12.3333 6.394C12.316 6.38337 12.2858 6.36514 12.2641 6.3527L9.93303 5.00621C9.87493 4.97232 9.80887 4.95444 9.74158 4.95444C9.67432 4.95444 9.60824 4.97232 9.55014 5.00621L6.70418 6.64951V5.51167C6.70386 5.50585 6.70497 5.50005 6.70743 5.49479C6.70988 5.48952 6.7136 5.48492 6.71826 5.48144L9.0747 4.12203C9.4357 3.91387 9.84853 3.81281 10.2649 3.83068C10.6812 3.84856 11.0838 3.98462 11.4257 4.22296C11.7675 4.46129 12.0344 4.79204 12.1951 5.17652C12.3559 5.56099 12.4038 5.98327 12.3333 6.394ZM6.16851 8.42203L5.18299 7.85311C5.17784 7.85053 5.17336 7.84673 5.17002 7.84202C5.16668 7.8373 5.16455 7.83183 5.16385 7.82609V5.10452C5.16411 4.6877 5.28311 4.27956 5.5069 3.9279C5.73071 3.57623 6.05006 3.29559 6.42756 3.11883C6.80507 2.94206 7.22509 2.87649 7.63851 2.92977C8.05195 2.98306 8.44163 3.153 8.76197 3.41971C8.74421 3.42941 8.71327 3.44649 8.69272 3.45895L6.36168 4.80542C6.30317 4.83867 6.25462 4.8869 6.22093 4.94516C6.18727 5.0034 6.16974 5.06958 6.17012 5.13683L6.16851 8.42203ZM6.70371 7.26803L7.97126 6.53595L9.23878 7.26757V8.73127L7.97126 9.46292L6.70371 8.73127V7.26803Z"/></svg>'},k="team@northlight.ai";function $(e){const t=JSON.stringify(e.config).replace(/</g,"\\u003c");return`<!doctype html>
|
|
150
|
+
`,Y={arrow:'<path d="M7 7h10v10"/><path d="M7 17 17 7"/>',copy:'<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',clock:'<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',warn:'<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/>',alert:'<circle cx="12" cy="12" r="10"/><path d="M12 8v4"/><path d="M12 16h.01"/>',check:'<path d="M20 6 9 18l-5-5"/>'};function s(e,t=""){return`<svg class="ic${t}" viewBox="0 0 24 24" aria-hidden="true" focusable="false">${Y[e]}</svg>`}o(s,"icon");const K='<svg viewBox="-77.5 12 931 931" aria-hidden="true" focusable="false"><path fill="currentColor" d="M234 294m0 0h2l-1 5-6 16-5 15-6 25-4 23-2 22-1 5v47l5 38 5 26 6 22 10 24 8 16 9 14 14 19 13 16 11 13 9 11 11 12 7 8 13 16 11 14 14 19 12 19 12 23 7 17 6 23 3 14 3-1 5-15 20-84 6-24 6-21 10-33 10-25 8-16 8-15 8-12 9-12 8-10 9-10 7-8 6-7 20-20h2l2-4 17-17 7-8 10-13 14-21 8-16 8-18 5-15 2-7 2 1 6 36 2 18v41l-4 28-5 19-7 19-10 21-8 13-10 15-13 17-11 13-12 14-9 10-7 8-9 11h-2l-2 4-9 11-14 19-8 12-15 25-11 21-12 26-9 23-5 12-13 38-12 39-5 18-5 17h-2l-9-29-9-27-14-38-13-29-8-16-11-18-10-13-7-9-25-25-3-2v-2l-3-1v-2l-4-2-10-9-8-7-14-12-20-18-13-13-9-11-12-16-12-20-12-25-7-19-6-23-4-24-1-11-1-3v-39l3-23 6-27 7-23 5-11 11-23 9-16 10-14 8-11 9-11 10-11zM430 22m0 0h2v2h2l7 8 10 11 9 11 12 16 9 15 8 16 6 15 5 19 2 11 1 12v18l-1 16-5 24-5 16-7 15-9 17-7 11-10 15-10 13-9 11-13 14-7 8-11 12-7 8-13 16-11 15-13 20-10 18-8 16-7 18-5 15-4 18-2 14-1 27 2 24 3 17 5 23 8 30 12 36 9 28 3 8 4 12v2l-4-2-68-102-13-22-11-23-9-24-7-26-4-23-2-16v-31l3-25 6-27 7-21 7-15 8-16 14-21 11-14 26-30 12-13 8-10h2l2-4 10-11 7-9 14-19 7-10 13-24 7-19 4-15 3-14 3-22 2-24v-20l-2-31zM615 384m0 0 2 1 6 36 2 18v41l-4 28-5 19-7 19-10 21-8 13-10 15-13 17-11 13-12 14-9 10-7 8-9 11h-2l-2 4-9 11-14 19-8 12-15 25-11 21-2 5h-3l-2 2h-13l-6-5-11-6 1-9h-2l2-14-2 3 1-8 9-36 6-21 10-33 10-25 8-16 8-15 8-12 9-12 8-10 9-10 7-8 6-7 20-20h2l2-4 17-17 7-8 10-13 14-21 8-16 8-18 5-15zM557 254m0 0 2 1 4 16 2 16v34l-2 18-5 20-8 24-14 28-10 16-11 16-13 15-12 14-11 13-11 14-14 19-12 20-8 16-7 19-6 20-4 20-4 35-3 41-3 52-4 28-1 3h-2l-8-42-10-52-7-33-7-43v-54l4-24 5-17 8-20 10-19 7-10 10-13 9-11 19-19 8-7 16-15 8-7 9-9 8-7 11-11 1-2h2l2-4 11-13 10-14 11-17 9-19 8-21zM185 615m0 0 4 5 6 10 9 12 2 1 3 5 5-2 1-1h8l5 5 2 3 7 3 6 3 4 5 8 6 5 3v2h2v2h6l4 3 6 1 1 8 10 3 4 2 9 2 5 6 6 1h6l8 8 7 1 9 7h5l1-4-4-6-10-13-7-9-1-3h-2l-7-9v-3l7 6 7 9 11 14 14 19 12 19 12 23 7 17 6 23 3 14 3-1 5-15 10-42 5-22h2l1-4 1 4-1 8h-2l3 4-1 8 11 6 5 4h13l4-3-1 6-8 17-9 23-5 12-13 38-12 39-5 18-5 17h-2l-9-29-9-27-14-38-13-29-8-16-11-18-10-13-7-9-25-25-3-2v-2l-3-1v-2l-4-2-10-9-8-7-14-12-20-18-13-13-9-11-12-16-9-15zM369 553m0 0h1l1 43 2 3 3 4-1 6-1 1v7l1 2 1 15 7 1 5-2 5 1v-2l3-1 4 1 1-2h-3l1-3 6-2 2 11-1 8-3 12h2v2l3-1 2-12h-2l1-4 1-1 3-21 4-21h1v10l-4 30-3 34-4 69-4 28-1 3h-2l-8-42-10-52-7-33-6-37zM430 22m0 0h2v2h2l7 8 10 11 9 11 4 5 1 5 2 4 1 7 2 2-1 3-5-1h-7l-8-2-5 1v7l-1 1h-5l-2-8-1-2-1-15h-2l-2-31zM417 598m0 0h1v10l-4 30-3 34-4 69-4 28-1 3h-2l-7-37-1-16h8l2 1v-2h2l1-2 2-35v-13l-2-3-3-5-1-8 1-9-1-4-5-2v-2l-3-1v-2l3-1 4 1 1-2h-3l1-3 6-2 2 11-1 8-3 12h2v2l3-1 2-12h-2l1-4 1-1 3-21zM417 598m0 0h1v10l-4 30-3 34-1 17h-2v15l-1 12h-1v-18l1-17v-13l-2-3-3-5-1-8 1-9-1-4-5-2v-2l-3-1v-2l3-1 4 1 1-2h-3l1-3 6-2 2 11-1 8-3 12h2v2l3-1 2-12h-2l1-4 1-1 3-21zM310 619m0 0 4 2 4 6 5 8 7 9 3 5 2-1 2 1 2 10 6-1 2 1v2h-5l3 7 9 13 6 9 3 1v-2l-4-14h3l5 16 3 8 4 12v2l-4-2-60-90zM255 597m0 0 5 3 12 16 9 11 11 13 9 11 11 12 4 5-1 3-7-7-4-1-7-4v-2l-4-2-4-4 4 1 3-1v-3l-5-2-3-5v-2h-2l-6-8v-2l-3-1-1-2-8-1 1-2-2-8-3-4v-2h-2l-4-6-3-4zM425 181m0 0 7 1 1 3 12 2 2 3 3 1v-2h3l1 3-6 2-1 3h-2v2h-2l-1 7-5 1-2-5 2-3h3l1-5-3 1-3 3-1-4-5-2v-5h-6l-3-3zM369 553m0 0h1l1 43 2 3 3 4-1 6-1 1v7l1 2 1 15 2 1h-2l3 13 2 10v5l-3-4-5-25-4-26zM340 614m0 0 1 2h2l6 20 6 18 3 15-2-1-5-15-2-4v-5h-6l-3-2-9-2 2-3v2l5-4h4 2l-1-5-3-12zM296 649m0 0 5 2 11 12 4 5-1 3-7-7-4-1-7-4v-2l-4-2-4-4 4 1 3-1zM403 625m0 0h2l2 11-1 8-2 7-2 1v-9l-1-4-5-2v-2l-3-1v-2l3-1 4 1 1-2h-3l1-3zM417 598m0 0h1v10l-4 30-2 24h-1l-1-17h-2l1-4 1-1 3-21zM163 561m0 0 2 3 5 16 2 6 2 1 3 9 2 4 4-1 1-2 1 3h-2l-2 5 3 6-1 2-3-3-11-24-4-11-2-10zM439 685m0 0 1 2h9l3 4v6l7 2 3 7-4-1-6-4-5-4v-7h-9l-2-2zM369 553m0 0h1l1 43 2 3 3 4-1 6-1 1-1 7-2-2v-7h-2zM434 687m0 0h1v6l-3 9-7 29-4 5 1-8 9-36zM317 672m0 0 6 4 9 11 11 14 6 8v2l-4-2-10-13-7-9-1-3h-2l-7-9zM276 622m0 0 5 5 11 13 7 8-1 2-7-4-3-5v-2h-2l-6-8v-2l-3-1-2-4zM310 619m0 0 4 2 4 6 5 8 7 9-1 4-4-5-8-11-7-11zM416 172m0 0 2 1-1 7h-2v5l4-1-1 3-5 2-3-1v-7l4-8zM381 664m0 0 2 4 6 27v5l-1-2h-2l-5-24zM443 655m0 0 1 4-7 25-1 2h-2l1-7 6-22h2zM163 561m0 0 2 3 5 16 2 6 2 1v5l-2 1-7-18-2-10zM490 702m0 0 1 3-10 15-3 5-4 3v-5l5-5 7-11zM387 698m0 0 1 2h2l3 16v14h-1l-5-26zM426 139m0 0 1 3-1 5-7 22-2-1 2-9 5-18h2zM466 739m0 0h2l-1 6-8 15-3 1 2-7 7-12zM411 636m0 0h2v12l-1 14h-1l-1-17h-2l1-4zM349 645m0 0h3l3 9 3 15-2-1-5-15-2-4zM335 694m0 0 5 3 9 12v2l-4-2-10-13zM386 633m0 0 7 1-3 2-4 1-2 4h-4l-2-6h5zM547 287m0 0 3 2-3 7-1 5h-2l-1 4-2-1 1-5zM557 254m0 0 2 1 3 13-4-1 1-2h-2l-1 3-1-3zM560 311m0 0 3 1-2 5-3 6-9-3v-1l9-1zM409 664m0 0h1v25h-1l-2-24zM340 614m0 0 1 2h2l4 16-4-2-3-12zM438 705m0 0 10 2 1 7-3-1-4-4h-5zM502 685m0 0 1 3-8 11-3 1 2-5zM422 191m0 0 4 1 3 4-2 4-5-3zM359 675m0 0h3l4 13-1 4-4-9zM225 599m0 0 5 2 12 6-3 1-9-4-6-3zM255 597m0 0 5 3 6 8-1 2-3-1-4-6-3-4zM414 180m0 0h1v5l4-1-1 3-5 2-3-1 1-5zM341 665m0 0 3 1 6 9-1 4-8-12zM370 608m0 0h1l1 9 3 1-2 10h-1l-2-12zM439 671m0 0 1 2-4 13h-2l1-7 3-7zM341 658m0 0 6 1v2h-5l-1 5-3-3 1-4zM373 625m0 0h2l1 9 2 1h-2l-1 5h-1l-1-6zM185 615m0 0 4 5 5 8-1 3-4-5-4-7zM434 687m0 0h1v6l-3 9-2 3 1-13zM385 686m0 0h2l2 9v5l-1-2h-2l-1-5zM390 711m0 0h2l1 8v11h-1l-2-10zM355 657m0 0 2 3 4 12v3l-3-1-2-10zM413 624m0 0h1v14l-1-2h-2l1-11zM494 120m0 0h2l2 6v10l-2-3-2-9zM163 561m0 0 2 3 4 13-1 3v-2h-2l-3-13zM489 106m0 0h2l4 10-2 4-4-11zM230 609m0 0 5 1 4 5 3 2-6-1-3-3v-2l-3-1zM392 660m0 0h2v2h2l5 5 2 1-1 4-10-10zM254 615m0 0 8 1 1 1v5l-2-1v-2l-6-2zM190 587m0 0 6 1 2-1 4 2 1 3-13-2zM417 598m0 0h1v10l-1 5-2-1 1-9zM273 545m0 0 2 3 3 8v3h-2l-3-8z"/></svg>',Q={claude:'<svg viewBox="0 0 48 48" aria-hidden="true" focusable="false"><rect width="48" height="48" fill="#F3E9D7"/><path transform="translate(24 24) scale(.13) translate(-124 -124)" fill="#D97757" d="M52.4285 162.873L98.7844 136.879L99.5485 134.602L98.7844 133.334H96.4921L88.7237 132.862L62.2346 132.153L39.3113 131.207L17.0249 130.026L11.4214 128.844L6.2 121.873L6.7094 118.447L11.4214 115.257L18.171 115.847L33.0711 116.911L55.485 118.447L71.6586 119.392L95.728 121.873H99.5485L100.058 120.337L98.7844 119.392L97.7656 118.447L74.5877 102.732L49.4995 86.1905L36.3823 76.62L29.3779 71.7757L25.8121 67.2858L24.2839 57.3608L30.6515 50.2716L39.3113 50.8623L41.4763 51.4531L50.2636 58.1879L68.9842 72.7209L93.4357 90.6804L97.0015 93.6343L98.4374 92.6652L98.6571 91.9801L97.0015 89.2625L83.757 65.2772L69.621 40.8192L63.2534 30.6579L61.5978 24.632C60.9565 22.1032 60.579 20.0111 60.579 17.4246L67.8381 7.49965L71.9133 6.19995L81.7193 7.49965L85.7946 11.0443L91.9074 24.9865L101.714 46.8451L116.996 76.62L121.453 85.4816L123.873 93.6343L124.764 96.1155H126.292V94.6976L127.566 77.9197L129.858 57.3608L132.15 30.8942L132.915 23.4505L136.608 14.4708L143.994 9.62643L149.725 12.344L154.437 19.0788L153.8 23.4505L150.998 41.6463L145.522 70.1215L141.957 89.2625H143.994L146.414 86.7813L156.093 74.0206L172.266 53.698L179.398 45.6635L187.803 36.802L193.152 32.5484H203.34L210.726 43.6549L207.415 55.1159L196.972 68.3492L188.312 79.5739L175.896 96.2095L168.191 109.585L168.882 110.689L170.738 110.53L198.755 104.504L213.91 101.787L231.994 98.7149L240.144 102.496L241.036 106.395L237.852 114.311L218.495 119.037L195.826 123.645L162.07 131.592L161.696 131.893L162.137 132.547L177.36 133.925L183.855 134.279H199.774L229.447 136.524L237.215 141.605L241.8 147.867L241.036 152.711L229.065 158.737L213.019 154.956L175.45 145.977L162.587 142.787H160.805V143.85L171.502 154.366L191.242 172.089L215.82 195.011L217.094 200.682L213.91 205.172L210.599 204.699L188.949 188.394L180.544 181.069L161.696 165.118H160.422V166.772L164.752 173.152L187.803 207.771L188.949 218.405L187.294 221.832L181.308 223.959L174.813 222.777L161.187 203.754L147.305 182.486L136.098 163.345L134.745 164.2L128.075 235.42L125.019 239.082L117.887 241.8L111.902 237.31L108.718 229.984L111.902 215.452L115.722 196.547L118.779 181.541L121.58 162.873L123.291 156.636L123.14 156.219L121.773 156.449L107.699 175.752L86.304 204.699L69.3663 222.777L65.291 224.431L58.2867 220.768L58.9235 214.27L62.8713 208.48L86.304 178.705L100.44 160.155L109.551 149.507L109.462 147.967L108.959 147.924L46.6977 188.512L35.6182 189.93L30.7788 185.44L31.4156 178.115L33.7079 175.752L52.4285 162.873Z"/></svg>',chatgpt:'<svg viewBox="0 0 48 48" aria-hidden="true" focusable="false"><rect width="48" height="48" fill="#0D0D0D"/><path transform="translate(24 24) scale(2.35) translate(-7.98 -8)" fill="#FFF" d="M12.9851 6.93777C13.1163 6.54364 13.1618 6.12607 13.1186 5.71295C13.0754 5.29983 12.9444 4.90072 12.7344 4.54231C12.4232 4.00021 11.9478 3.57101 11.3768 3.31664C10.8058 3.06227 10.1687 2.99589 9.55751 3.12706C9.28184 2.81639 8.943 2.56818 8.56362 2.39905C8.18425 2.22993 7.77314 2.14379 7.3578 2.1464C6.73292 2.1449 6.1237 2.34187 5.61797 2.70892C5.11225 3.07598 4.73616 3.59415 4.54391 4.18873C4.13684 4.27206 3.75225 4.44139 3.41591 4.68539C3.07957 4.92938 2.79924 5.24241 2.59368 5.60352C2.27994 6.14413 2.14602 6.77039 2.21123 7.39205C2.27644 8.01367 2.5374 8.59851 2.95649 9.06224C2.82527 9.45637 2.77975 9.87394 2.82297 10.2871C2.8662 10.7002 2.99718 11.0993 3.20713 11.4577C3.51841 11.9998 3.99385 12.4289 4.56484 12.6833C5.13584 12.9377 5.77289 13.0041 6.38407 12.8729C6.65975 13.1836 6.99862 13.4318 7.37796 13.601C7.75734 13.7701 8.16844 13.8562 8.58379 13.8536C9.20899 13.8552 9.81853 13.6582 10.3244 13.2909C10.8304 12.9236 11.2065 12.4051 11.3986 11.8101C11.8057 11.7268 12.1903 11.5575 12.5266 11.3135C12.8629 11.0695 13.1433 10.7564 13.3488 10.3953C13.6622 9.85477 13.7958 9.22866 13.7304 8.60726C13.6651 7.98587 13.4041 7.40129 12.9851 6.93777ZM8.58472 13.0883C8.0715 13.089 7.57441 12.9093 7.18031 12.5805C7.19808 12.5708 7.22925 12.5537 7.24956 12.5413L9.58061 11.1948C9.63911 11.1615 9.6877 11.1133 9.72136 11.055C9.75505 10.9967 9.77258 10.9305 9.77217 10.8632V7.57684L10.7575 8.14576C10.7626 8.14834 10.7671 8.15214 10.7704 8.15685C10.7738 8.16157 10.7759 8.16704 10.7766 8.17275V10.8943C10.7759 11.4756 10.5448 12.0329 10.1339 12.4441C9.72308 12.8554 9.16599 13.087 8.58472 13.0883ZM3.87091 11.075C3.61385 10.6309 3.5212 10.1105 3.60918 9.60485C3.62649 9.61524 3.65673 9.6337 3.67842 9.64617L6.00947 10.9926C6.06756 11.0266 6.13365 11.0445 6.20094 11.0445C6.2682 11.0445 6.33428 11.0266 6.39238 10.9926L9.23834 9.34936V10.4872C9.23867 10.493 9.23755 10.4988 9.23509 10.5041C9.23264 10.5094 9.22892 10.5139 9.22427 10.5174L6.86782 11.878C6.36379 12.1683 5.76516 12.2467 5.20333 12.0962C4.64148 11.9457 4.1623 11.5784 3.87091 11.075ZM3.25768 5.98617C3.51363 5.54141 3.91789 5.2009 4.39966 5.02421C4.39966 5.04429 4.39851 5.07985 4.39851 5.10452V7.79747C4.3981 7.86473 4.41561 7.93087 4.44924 7.98915C4.48287 8.04739 4.5314 8.09563 4.58984 8.1289L7.4358 9.77197L6.45054 10.3409C6.44568 10.3441 6.44009 10.346 6.43429 10.3465C6.4285 10.3471 6.42265 10.3462 6.41729 10.3439L4.06062 8.98216C3.55747 8.69077 3.19039 8.21176 3.03987 7.65013C2.88935 7.08853 2.96768 6.49014 3.25768 5.98617ZM11.3527 7.86994L8.50669 6.22667L9.49198 5.65799C9.49684 5.6548 9.50243 5.65283 9.50823 5.65231C9.51402 5.65178 9.51985 5.65272 9.5252 5.655L11.8819 7.01554C12.2429 7.2241 12.5371 7.53119 12.7299 7.90087C12.9228 8.27056 13.0063 8.68752 12.9707 9.10295C12.9351 9.51839 12.7819 9.91509 12.5291 10.2466C12.2762 10.5781 11.9341 10.8307 11.5428 10.9749C11.5428 10.9546 11.5428 10.919 11.5428 10.8943V8.20137C11.5434 8.13423 11.5261 8.06814 11.4926 8.0099C11.4592 7.95165 11.4109 7.90336 11.3527 7.86994ZM12.3333 6.394C12.316 6.38337 12.2858 6.36514 12.2641 6.3527L9.93303 5.00621C9.87493 4.97232 9.80887 4.95444 9.74158 4.95444C9.67432 4.95444 9.60824 4.97232 9.55014 5.00621L6.70418 6.64951V5.51167C6.70386 5.50585 6.70497 5.50005 6.70743 5.49479C6.70988 5.48952 6.7136 5.48492 6.71826 5.48144L9.0747 4.12203C9.4357 3.91387 9.84853 3.81281 10.2649 3.83068C10.6812 3.84856 11.0838 3.98462 11.4257 4.22296C11.7675 4.46129 12.0344 4.79204 12.1951 5.17652C12.3559 5.56099 12.4038 5.98327 12.3333 6.394ZM6.16851 8.42203L5.18299 7.85311C5.17784 7.85053 5.17336 7.84673 5.17002 7.84202C5.16668 7.8373 5.16455 7.83183 5.16385 7.82609V5.10452C5.16411 4.6877 5.28311 4.27956 5.5069 3.9279C5.73071 3.57623 6.05006 3.29559 6.42756 3.11883C6.80507 2.94206 7.22509 2.87649 7.63851 2.92977C8.05195 2.98306 8.44163 3.153 8.76197 3.41971C8.74421 3.42941 8.71327 3.44649 8.69272 3.45895L6.36168 4.80542C6.30317 4.83867 6.25462 4.8869 6.22093 4.94516C6.18727 5.0034 6.16974 5.06958 6.17012 5.13683L6.16851 8.42203ZM6.70371 7.26803L7.97126 6.53595L9.23878 7.26757V8.73127L7.97126 9.46292L6.70371 8.73127V7.26803Z"/></svg>'},S="team@northlight.ai";function T(e){const t=JSON.stringify(e.config).replace(/</g,"\\u003c");return`<!doctype html>
|
|
151
151
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
152
152
|
<meta name="color-scheme" content="light dark">
|
|
153
|
-
<title>${
|
|
153
|
+
<title>${v(e.title)}</title><style nonce="${e.nonce}">${W}</style></head>
|
|
154
154
|
<body>
|
|
155
|
-
<header class="top">${
|
|
155
|
+
<header class="top">${K}<span>Northlight</span></header>
|
|
156
156
|
<main><div class="col">${e.body}</div></main>
|
|
157
|
-
<footer class="foot"><span>Need help? Email <a href="mailto:${
|
|
157
|
+
<footer class="foot"><span>Need help? Email <a href="mailto:${S}">${S}</a></span></footer>
|
|
158
158
|
<script type="application/json" id="cfg">${t}<\/script>
|
|
159
|
-
<script nonce="${e.nonce}">${
|
|
160
|
-
</body></html>`}
|
|
161
|
-
${
|
|
159
|
+
<script nonce="${e.nonce}">${X}<\/script>
|
|
160
|
+
</body></html>`}o(T,"page");function A(e,t){return`<div class="head"><div class="logo">${Q[e]}</div><div><span class="eyebrow">AI subscription</span><h1>Connect your ${t} account</h1></div></div>`}o(A,"header");function f(e,t,r="",n=""){return`<li${n}><span class="n"><span>${e}</span>${s("check")}</span><div class="body"><span class="title">${t}</span>${r}</div></li>`}o(f,"step");function e1(e,t,r,n){return T({nonce:t,title:"Connect Claude",body:`<div class="card">
|
|
161
|
+
${A("claude","Claude")}
|
|
162
162
|
<ol class="steps">
|
|
163
|
-
${
|
|
164
|
-
${
|
|
165
|
-
${
|
|
163
|
+
${f(1,"Sign in to Claude in a new tab",`<div><a class="btn" href="${v(e.url)}" target="_blank" rel="noopener noreferrer">Open claude.ai${s("arrow")}</a></div>`," data-pre")}
|
|
164
|
+
${f(2,"Copy the code Claude shows you",""," data-pre")}
|
|
165
|
+
${f(3,"Paste it here",`<form id="paste" autocomplete="off">
|
|
166
166
|
<div class="field"><label for="code">Code from Claude</label>
|
|
167
167
|
<input type="text" id="code" name="code" required spellcheck="false" autocapitalize="off" aria-describedby="error">
|
|
168
168
|
<div id="error" class="err" role="alert">${s("alert")}<span id="error-text"></span></div></div>
|
|
169
169
|
<button class="btn primary lg wide" type="submit">Connect Claude</button>
|
|
170
170
|
</form>`)}
|
|
171
171
|
</ol>
|
|
172
|
-
</div>`,config:{kind:"anthropic",base:`${
|
|
173
|
-
${
|
|
172
|
+
</div>`,config:{kind:"anthropic",base:`${h}/${encodeURIComponent(e.flow_id)}`,returnTo:r,handoff:n}})}o(e1,"anthropicPage");function t1(e,t){const r=Date.parse(e.expires_at)-t;return Math.max(0,Math.floor(Math.min(Number.isNaN(r)?$*1e3:r,$*1e3)/1e3))}o(t1,"codeSecondsLeft");function n1(e,t,r,n,a=Date.now()){const i=e.user_code??"";return T({nonce:t,title:"Connect ChatGPT",body:`<div class="card">
|
|
173
|
+
${A("chatgpt","ChatGPT")}
|
|
174
174
|
<div class="alert" id="settings" role="alert">${s("warn")}
|
|
175
175
|
<div class="text"><strong>Turn on device code sign-in first</strong>OpenAI refuses this sign-in unless device code authorization for Codex is on in ChatGPT's Security settings.</div>
|
|
176
|
-
<div class="act"><a class="btn sm" href="${
|
|
176
|
+
<div class="act"><a class="btn sm" href="${U}" target="_blank" rel="noopener noreferrer">Open settings${s("arrow")}</a></div></div>
|
|
177
177
|
<ol class="steps" id="device">
|
|
178
|
-
${
|
|
179
|
-
${
|
|
178
|
+
${f(1,"Open the OpenAI device page",`<div><a class="btn" id="device-link" href="${v(e.url)}" target="_blank" rel="noopener noreferrer">${v(e.url.replace(/^https:\/\//,""))}${s("arrow")}</a></div>`)}
|
|
179
|
+
${f(2,"Enter this code",`<div class="codebox"><span class="code" id="user-code">${v(i)}</span>
|
|
180
180
|
<span class="badge" id="expired-badge" hidden>Expired</span>
|
|
181
181
|
<button class="btn sm" type="button" id="copy">${s("copy")}<span id="copy-label">Copy</span></button></div>`)}
|
|
182
|
-
${
|
|
182
|
+
${f(3,"Approve the sign-in",`<span class="small">When OpenAI shows an account email, check it's the account you want to use: approving connects that account.</span>`)}
|
|
183
183
|
</ol>
|
|
184
184
|
<div class="wait" id="waiting">
|
|
185
185
|
<svg class="spin" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M21 12a9 9 0 1 1-9-9"/></svg>
|
|
@@ -189,4 +189,4 @@ ${h(3,"Approve the sign-in",`<span class="small">When OpenAI shows an account em
|
|
|
189
189
|
<div class="alert" id="expired" role="alert" hidden>${s("warn")}
|
|
190
190
|
<div class="text"><strong>The code expired before it was approved</strong>Codes last 15 minutes. Getting a new one starts the sign-in over.</div>
|
|
191
191
|
<div class="act"><button class="btn primary sm" type="button" id="renew">Get a new code</button></div></div>
|
|
192
|
-
</div>`,config:{kind:"openai",base:`${
|
|
192
|
+
</div>`,config:{kind:"openai",base:`${h}/${encodeURIComponent(e.flow_id)}`,returnTo:r,handoff:n,userCode:i,expiresAt:e.expires_at,remainingSeconds:t1(e,a)}})}o(n1,"openaiPage");async function r1(e,t,r,n){const a=y(r.searchParams,"handoff"),i=await _(n,a);if(z(i.jti))throw p();const l=y(r.searchParams,"return_to");let L;try{L=l===void 0?void 0:new URL(l).origin}catch{L=void 0}if(l===void 0||L!==new URL(n.issuer).origin)throw new d(400,"invalid_request","return_to must be an absolute URL on the directory origin","return_to");const u=y(r.searchParams,"vendor");if(u!=="anthropic"&&u!=="openai")throw new d(400,"invalid_request","vendor must be anthropic or openai","vendor");if(!R(i.jti,i.exp))throw p();let w;try{w=await I({provider:F(u),jti:i.jti})}catch(c){const M=c instanceof x?c.category:"runtime";m({level:"error",event:"vendor_signin.start_failed",error:c,fields:{vendor:u,...c instanceof x?c.logFields():{category:M}}}),t.writeHead(303,{Location:Z(l,"failed",M,c instanceof x?c.step:void 0),"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}),t.end();return}const b=j(16).toString("base64"),O=u==="anthropic"?e1(w,b,l,a):n1(w,b,l,a);t.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","Referrer-Policy":"no-referrer","X-Content-Type-Options":"nosniff","Content-Security-Policy":`default-src 'none'; script-src 'nonce-${b}'; style-src 'nonce-${b}'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`}),t.end(O)}o(r1,"startPage");function g(e,t,r){const{category:n,step:a,fields:i}=V(t);m({level:"warn",event:"vendor_signin.failed",fields:{flow_id:t,message:r,...i}}),C(e,200,{status:"failed",reason:n,...a===void 0?{}:{step:a}})}o(g,"sendFailed");async function E(e,t,r){let n;try{n=await _(t,J(e))}catch(i){throw m({level:"warn",event:"vendor_signin.handoff_rejected",error:i,fields:{flow_id:r}}),i}const a=k(r);if(!a){if(z(n.jti))return!1;throw m({level:"warn",event:"vendor_signin.unauthorized",fields:{flow_id:r,reason:"unknown flow, handoff never started one"}}),p()}if(a.jti===void 0||a.jti!==n.jti)throw m({level:"warn",event:"vendor_signin.unauthorized",fields:{flow_id:r,reason:"handoff did not start this flow"}}),p();return!0}o(E,"followUpFlow");async function a1(e,t,r,n){if(!await E(e,r,n)){g(t,n,"flow_gone");return}const a=await q(e);try{await H(n,a.input),C(t,200,{status:"connected"})}catch(i){const l=k(n);if(l&&l.status!=="pending"){g(t,n,l.message??l.status);return}throw i}}o(a1,"submitCode");async function i1(e,t,r,n){if(!await E(e,r,n)){g(t,n,"flow_gone");return}const a=new AbortController;t.once("close",()=>a.abort());const i=await D(n,G,a.signal);if(!i){g(t,n,"flow_gone");return}if(i.status==="failed"){g(t,n,i.message??"failed");return}C(t,200,{status:i.status})}o(i1,"waitFlow");async function p1(e,t,r,n){const a=e.method??"GET";if(r.pathname===h&&a==="GET"){await r1(e,t,r,n);return}const i=r.pathname.slice(h.length+1).split("/");let l="";try{l=i[0]?decodeURIComponent(i[0]):""}catch{throw new d(404,"not_found","unknown operation")}if(l&&i.length===1&&a==="POST"){await a1(e,t,n,l);return}if(l&&i.length===2&&i[1]==="wait"&&a==="GET"){await i1(e,t,n,l);return}throw new d(404,"not_found","unknown operation")}o(p1,"handleVendorSignIn");export{h as VENDOR_SIGNIN_PATH,e1 as anthropicPage,v as escapeHtml,p1 as handleVendorSignIn,m1 as isVendorSignInPath,n1 as openaiPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var h=Object.defineProperty;var
|
|
1
|
+
var h=Object.defineProperty;var u=(n,t)=>h(n,"name",{value:t,configurable:!0});import{openDb as d}from"../core/canvas/db.js";import{runNoticeTarget as p}from"../core/grants/mirror.js";import{emitEvent as w}from"../core/events/emit.js";import{describeOutboundFailure as m}from"../core/events/outbound.js";import{discardFile as _}from"../core/providers/token-cache.js";import{outboundToken as g}from"../core/providers/mint.js";const y="crtr:run_notice",A=[3e3,5e3],E=5e3,T=6e4,k=1e4;class l extends Error{static{u(this,"Fault")}retryable;status;where;constructor(t,e,s,o,a={step:"run_notice"}){super(t,o===void 0?void 0:{cause:o}),this.retryable=e,this.status=s,this.where=a}}class b{static{u(this,"RunNoticeOutbox")}deps;runs=new Map;constructor(t){this.deps=t}state(t){let e=this.runs.get(t);return e||(e={streams:0,owed:!1,inFlight:!1,timer:void 0,nextAllowedAt:0},this.runs.set(t,e)),e}forget(t,e){e.streams===0&&!e.owed&&!e.inFlight&&e.timer===void 0&&e.nextAllowedAt<=this.deps.now()&&this.runs.delete(t)}async streamOpened(t){const e=this.state(t);e.streams++,e.owed=!1,e.nextAllowedAt=0,e.timer&&(clearTimeout(e.timer),e.timer=void 0);let s=!1;return async o=>{if(s)return;s=!0,e.streams--;let a=null;try{a=await this.deps.latest(t)}catch{}e.streams===0&&a!==null&&a>o?await this.owe(t):this.forget(t,e)}}async appended(t){const e=this.runs.get(t);e&&e.streams>0||await this.owe(t)}async owe(t){let e;try{e=await this.deps.target(t)}catch{return}if(e===null)return;const s=this.state(t);s.streams>0||(s.owed=!0,this.schedule(t,s))}schedule(t,e){if(!e.owed||e.inFlight||e.timer!==void 0||e.streams>0)return;const s=Math.max(0,e.nextAllowedAt-this.deps.now());e.timer=setTimeout(()=>{e.timer=void 0,this.deliver(t,e)},s),e.timer.unref()}async deliver(t,e){if(!e.owed||e.streams>0){this.forget(t,e);return}const s=await this.deps.target(t),o=await this.deps.latest(t);if(s===null||o===null){e.owed=!1,this.forget(t,e);return}e.owed=!1,e.inFlight=!0;let a=0,r;try{for(;;){a++;try{await this.attempt(s,t,o),r=void 0;break}catch(i){r=i instanceof l?i:new l(i instanceof Error?i.message:String(i),!0);const c=this.deps.retryWaitsMs[a-1];if(!r.retryable||c===void 0)break;if(await new Promise(f=>{setTimeout(f,c).unref()}),e.streams>0){r=void 0;break}}}}finally{e.inFlight=!1}r?(e.nextAllowedAt=this.deps.now()+this.deps.failGapMs,w({level:"error",event:"run_notice.failed",fields:{run_id:t,grantee:s.grantee,attempts:a,latest_sequence:o,...r.status===void 0?{}:{status:r.status},error:r.message,...m(r.cause,r.where.url===void 0&&r.where.step==="run_notice"?{step:"run_notice",url:s.url}:r.where)}})):e.streams===0&&(e.nextAllowedAt=this.deps.now()+this.deps.ackGapMs),this.schedule(t,e),this.forget(t,e)}async attempt(t,e,s){for(let o=0;;o++){let a;try{a=await this.deps.token(t)}catch(i){throw new l(`token mint failed: ${i instanceof Error?i.message:String(i)}`,!0,void 0,i,{step:"notice_token"})}let r;try{r=await this.deps.fetch(t.url,{method:"POST",redirect:"manual",signal:AbortSignal.timeout(k),headers:{Authorization:`Bearer ${a.access_token}`,"Content-Type":"application/json"},body:JSON.stringify({run_id:e,latest_sequence:s})})}catch(i){throw new l(`request failed: ${i instanceof Error?i.message:String(i)}`,!0,void 0,i,{step:"run_notice",url:t.url})}if(r.body?.cancel().catch(()=>{}),r.status>=200&&r.status<300)return;if(r.status===401){if(this.deps.discard(a.key),o===0)continue;throw new l("the app refused the notice token twice (401)",!0,401)}throw r.status>=500?new l(`the app answered ${r.status}`,!0,r.status):new l(`contract_fault: the app answered ${r.status}`,!1,r.status)}}}function x(){return{target:u(async n=>{const t=await(await d()).prepare("SELECT grant_id FROM nodes WHERE node_id = ? AND run_id = ?").get(n,n);return t?.grant_id?await p(t.grant_id):null},"target"),latest:u(async n=>{const t=await(await d()).prepare("SELECT event_cursor FROM nodes WHERE node_id = ? AND run_id = ?").get(n,n);return t?.event_cursor==null?null:t.event_cursor-1},"latest"),token:u(async n=>{const t=await g({audience:n.url,sub:n.sub,grantee:n.grantee,initiating_app:n.grantee,scope:[y]});return{access_token:t.access_token,key:t.key}},"token"),discard:_,fetch:u((n,t)=>fetch(n,t),"fetch"),now:Date.now,retryWaitsMs:A,ackGapMs:E,failGapMs:T}}u(x,"databaseDeps");let M;function P(){return M??=new b(x())}u(P,"runNotices");export{E as RUN_NOTICE_ACK_GAP_MS,k as RUN_NOTICE_ATTEMPT_TIMEOUT_MS,T as RUN_NOTICE_FAIL_GAP_MS,A as RUN_NOTICE_RETRY_WAITS_MS,y as RUN_NOTICE_SCOPE,b as RunNoticeOutbox,x as databaseDeps,P as runNotices};
|
|
@@ -49,7 +49,7 @@ Then set your own session for the person. The SDK does not manage your sessions.
|
|
|
49
49
|
|
|
50
50
|
### Scopes decide what comes back
|
|
51
51
|
|
|
52
|
-
A sign-in that asks only for `openid`, optionally with `email` and `profile`, creates no connection. Finish it with `oauth.exchangeIdentity(...)`, which returns the verified `Identity` (`sub`, and `email`, `name` and `picture` when approved and present). A sign-in that asks for runtime scopes such as `crtr:llm` finishes with `exchangeCode`.
|
|
52
|
+
A sign-in that asks only for `openid`, optionally with `email` and `profile`, creates no connection. Finish it with `oauth.exchangeIdentity(...)`, which returns the verified `Identity` (`sub`, and `email`, `name` and `picture` when approved and present). A sign-in that asks for runtime scopes such as `crtr:llm` finishes with `exchangeCode`. `exchangeCode` refuses a sign-in-only answer with `oauth_exchange_mismatch`; `exchangeIdentity` accepts an answer that also carries a connection, returns the identity, and revokes the unneeded refresh token.
|
|
53
53
|
|
|
54
54
|
### When the directory refuses to sign someone in
|
|
55
55
|
|
|
@@ -116,7 +116,7 @@ It needs the app registered with the `app_token_for_user` operation, and an acti
|
|
|
116
116
|
|
|
117
117
|
## Signing in a visitor
|
|
118
118
|
|
|
119
|
-
A person who only needs to be recognized, not connected, signs in with identity only: request `openid`, optionally with `email` and `profile`, and no runtime scope. Finish with `exchangeIdentity`, not `exchangeCode`. An `Identity` is not a connection: no runtime URL, grant or refresh token, nothing goes in the store, and you keep your own session. To act for that person later, send them through `authorizeUrl` again with the runtime scopes you need.
|
|
119
|
+
A person who only needs to be recognized, not connected, signs in with identity only: request `openid`, optionally with `email` and `profile`, and no runtime scope. Finish with `exchangeIdentity`, not `exchangeCode`. An `Identity` is not a connection: no runtime URL, grant or refresh token (if the directory answers with one anyway, `exchangeIdentity` revokes the refresh token), nothing goes in the store, and you keep your own session. To act for that person later, send them through `authorizeUrl` again with the runtime scopes you need.
|
|
120
120
|
|
|
121
121
|
## Checked and not checked
|
|
122
122
|
|
package/docs/apps/errors.md
CHANGED
|
@@ -16,7 +16,7 @@ Codes the SDK raises itself are listed in `sdkErrorCodes`, and `CrouterError.cod
|
|
|
16
16
|
| Code | Means | What to do |
|
|
17
17
|
|---|---|---|
|
|
18
18
|
| `id_token_invalid` | An ID token failed verification (signature, issuer, audience, expiry, missing or mismatched nonce), or a sign-in-only answer had no ID token. A JWKS that can't be fetched or is malformed is not this code: that error is thrown as is. | Restart the sign-in. |
|
|
19
|
-
| `oauth_exchange_mismatch` |
|
|
19
|
+
| `oauth_exchange_mismatch` | `exchangeCode` got a sign-in-only answer (no connection). | Finish a sign-in-only request with `exchangeIdentity` ([Many users](/docs/guides/many-users#signing-in-a-visitor)). |
|
|
20
20
|
| `oauth_nonce_missing` | `exchangeIdentity` was called without the `nonce` from `authorizeUrl`. | Save the nonce in the visitor's session and pass it back. |
|
|
21
21
|
| `oauth_state_mismatch` | The callback's `state` was missing or differed from the saved one. | Refuse the callback. |
|
|
22
22
|
| `not_connected` | `oauth.client(userId)` or a refresh found no stored connection for that person. | Show "sign in again". |
|
|
@@ -16,7 +16,7 @@ Keep the connection and refresh token on the server, never in a visitor's browse
|
|
|
16
16
|
|
|
17
17
|
## Signing in a visitor
|
|
18
18
|
|
|
19
|
-
A visitor who only needs to be recognized, not connected, signs in with identity only. Request `openid`, adding `email` for their email and `profile` for their name and photo, and no runtime scope. The directory shows one line ("Know who you are on Northlight"), writes no grant, and answers with an ID token and an access token usable only at `/userinfo`: no refresh token, grant or runtime URL. Finish that sign-in with `exchangeIdentity`, not `exchangeCode`:
|
|
19
|
+
A visitor who only needs to be recognized, not connected, signs in with identity only. Request `openid`, adding `email` for their email and `profile` for their name and photo, and no runtime scope. The directory shows one line ("Know who you are on Northlight"), writes no grant, and answers with an ID token and an access token usable only at `/userinfo`: no refresh token, grant or runtime URL. (An app held by a team with a runtime grant gets a connection on every sign-in; `exchangeIdentity` still returns only the identity and revokes the unneeded refresh token.) Finish that sign-in with `exchangeIdentity`, not `exchangeCode`:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
22
|
const {url, codeVerifier, state, nonce} = await oauth.authorizeUrl({scopes: ['openid', 'email', 'profile'], state: sessionState});
|
|
@@ -34,7 +34,7 @@ visitor.picture; // an https URL, present only when 'profile' was approved
|
|
|
34
34
|
|
|
35
35
|
The SDK checks the ID token's ES256 signature against the directory's JWKS, its issuer, that `aud` is your client id, that it has not expired, and that its `nonce` equals the one `authorizeUrl` returned. `nonce` is required. Any failure throws `CrouterError` code `id_token_invalid`. If the directory's JWKS can't be fetched or is malformed, that error is thrown as is.
|
|
36
36
|
|
|
37
|
-
An `Identity` is not a connection. Nothing is stored in a `ConnectionStore` and nothing refreshes; your app keeps its own session for the visitor, and the visitor's runtime can't be called on their behalf. To act for the visitor later, send them through `authorizeUrl` again with the runtime scopes you need and finish with `exchangeCode`, which returns a `Connection`.
|
|
37
|
+
An `Identity` is not a connection. Nothing is stored in a `ConnectionStore` and nothing refreshes; your app keeps its own session for the visitor, and the visitor's runtime can't be called on their behalf. To act for the visitor later, send them through `authorizeUrl` again with the runtime scopes you need and finish with `exchangeCode`, which returns a `Connection`. `exchangeCode` refuses a sign-in-only answer with code `oauth_exchange_mismatch`.
|
|
38
38
|
|
|
39
39
|
## Whose action a call records
|
|
40
40
|
|
|
@@ -9,6 +9,8 @@ Changes to `@north-light/sdk` that can change what an existing app sees. Newest
|
|
|
9
9
|
|
|
10
10
|
## 0.3.539
|
|
11
11
|
|
|
12
|
+
`exchangeIdentity` now returns the verified identity when the directory's token answer also carries a runtime connection. It used to throw `oauth_exchange_mismatch`, so no member could sign in to an app held by a team with a runtime grant (the directory gives that app a connection on every sign-in). The SDK revokes the unneeded refresh token best-effort; revocation removes only that token, never the grant. `exchangeCode` still refuses a sign-in-only answer.
|
|
13
|
+
|
|
12
14
|
A run started with no `kind` and no `model`, in a profile with no `default_kind` (every app's own default profile), now starts as the `general` kind on the `medium` rung. It used to start as `developer` on that kind's model (`openai/medium`). `profile.list` reports `general` as such a profile's `default_kind`. On an app-paid grant the run lands on the app's `medium` inference rung, where it used to land on `strong`. Pass `model` on `run.start` to keep another rung.
|
|
13
15
|
|
|
14
16
|
Profiles name the machine environment they run on. Additive: no request or response an existing app sends or reads changes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.541",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
},
|
|
141
141
|
"license": "UNLICENSED",
|
|
142
142
|
"dependencies": {
|
|
143
|
-
"@north-light/identity": "0.3.
|
|
143
|
+
"@north-light/identity": "0.3.541",
|
|
144
144
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
145
145
|
"@earendil-works/pi-ai": "1.1.0",
|
|
146
146
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.541",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.541",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@milkdown/preset-gfm": "^7.22.3",
|
|
25
25
|
"@milkdown/prose": "^7.22.3",
|
|
26
26
|
"@milkdown/utils": "^7.22.3",
|
|
27
|
-
"@north-light/identity": "0.3.
|
|
27
|
+
"@north-light/identity": "0.3.541",
|
|
28
28
|
"cron-parser": "^5.6.0",
|
|
29
29
|
"esbuild": "^0.27.7",
|
|
30
30
|
"jose": "^6.2.12",
|
|
@@ -12455,17 +12455,17 @@
|
|
|
12455
12455
|
},
|
|
12456
12456
|
"packages/crouter-api": {
|
|
12457
12457
|
"name": "@north-light/api",
|
|
12458
|
-
"version": "0.3.
|
|
12458
|
+
"version": "0.3.541",
|
|
12459
12459
|
"license": "UNLICENSED"
|
|
12460
12460
|
},
|
|
12461
12461
|
"packages/crouter-env-docker": {
|
|
12462
12462
|
"name": "@north-light/env-docker",
|
|
12463
|
-
"version": "0.3.
|
|
12463
|
+
"version": "0.3.541",
|
|
12464
12464
|
"license": "UNLICENSED"
|
|
12465
12465
|
},
|
|
12466
12466
|
"packages/crouter-identity": {
|
|
12467
12467
|
"name": "@north-light/identity",
|
|
12468
|
-
"version": "0.3.
|
|
12468
|
+
"version": "0.3.541",
|
|
12469
12469
|
"license": "UNLICENSED",
|
|
12470
12470
|
"dependencies": {
|
|
12471
12471
|
"jose": "^6.2.1"
|
|
@@ -12473,20 +12473,20 @@
|
|
|
12473
12473
|
},
|
|
12474
12474
|
"packages/crouter-plugin": {
|
|
12475
12475
|
"name": "@north-light/plugin",
|
|
12476
|
-
"version": "0.3.
|
|
12476
|
+
"version": "0.3.541",
|
|
12477
12477
|
"license": "UNLICENSED",
|
|
12478
12478
|
"dependencies": {
|
|
12479
|
-
"@north-light/api": "0.3.
|
|
12480
|
-
"@north-light/identity": "0.3.
|
|
12479
|
+
"@north-light/api": "0.3.541",
|
|
12480
|
+
"@north-light/identity": "0.3.541"
|
|
12481
12481
|
}
|
|
12482
12482
|
},
|
|
12483
12483
|
"packages/crouter-sdk": {
|
|
12484
12484
|
"name": "@north-light/sdk",
|
|
12485
|
-
"version": "0.3.
|
|
12485
|
+
"version": "0.3.541",
|
|
12486
12486
|
"license": "UNLICENSED",
|
|
12487
12487
|
"dependencies": {
|
|
12488
|
-
"@north-light/api": "0.3.
|
|
12489
|
-
"@north-light/identity": "0.3.
|
|
12488
|
+
"@north-light/api": "0.3.541",
|
|
12489
|
+
"@north-light/identity": "0.3.541",
|
|
12490
12490
|
"jose": "^6.2.1"
|
|
12491
12491
|
},
|
|
12492
12492
|
"bin": {
|