@theokit/agents 4.9.0 → 4.9.1
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/auth.js
CHANGED
|
@@ -5,6 +5,38 @@ import {
|
|
|
5
5
|
// src/auth/auth-provider.ts
|
|
6
6
|
import { authFilePath, ensureFreshCredential, openaiDeviceLogin, persistOAuthTokens, readStoredOAuth } from "@theokit/sdk/auth";
|
|
7
7
|
import { withFileLock } from "@theokit/sdk/persistence";
|
|
8
|
+
var RefreshFailure = class extends Error {
|
|
9
|
+
static {
|
|
10
|
+
__name(this, "RefreshFailure");
|
|
11
|
+
}
|
|
12
|
+
transitorio;
|
|
13
|
+
constructor(message, transitorio) {
|
|
14
|
+
super(message), this.transitorio = transitorio;
|
|
15
|
+
this.name = "RefreshFailure";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var MOTIVOS_TRANSITORIOS = [
|
|
19
|
+
"ETIMEDOUT",
|
|
20
|
+
"ECONNRESET",
|
|
21
|
+
"ECONNREFUSED",
|
|
22
|
+
"EAI_AGAIN",
|
|
23
|
+
"ENOTFOUND",
|
|
24
|
+
"AbortError"
|
|
25
|
+
];
|
|
26
|
+
function classificarFalhaDeRefresh(err) {
|
|
27
|
+
const texto = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
28
|
+
if (/invalid_grant|invalid_request|unauthorized_client/i.test(texto)) {
|
|
29
|
+
return new RefreshFailure("o refresh token n\xE3o \xE9 mais v\xE1lido \u2014 refa\xE7a o login. Tentar de novo n\xE3o muda o resultado.", false);
|
|
30
|
+
}
|
|
31
|
+
const transitorio = MOTIVOS_TRANSITORIOS.some((m) => texto.includes(m)) || /\b(5\d{2})\b|timeout|network/i.test(texto);
|
|
32
|
+
return new RefreshFailure(transitorio ? "falha transit\xF3ria ao refrescar a credencial" : "falha ao refrescar a credencial", transitorio);
|
|
33
|
+
}
|
|
34
|
+
__name(classificarFalhaDeRefresh, "classificarFalhaDeRefresh");
|
|
35
|
+
function esperaComJitter(tentativa, baseMs = 200, aleatorio = Math.random) {
|
|
36
|
+
const base = baseMs * 2 ** tentativa;
|
|
37
|
+
return Math.round(base * (0.75 + aleatorio() * 0.5));
|
|
38
|
+
}
|
|
39
|
+
__name(esperaComJitter, "esperaComJitter");
|
|
8
40
|
var AuthProvider = class _AuthProvider {
|
|
9
41
|
static {
|
|
10
42
|
__name(this, "AuthProvider");
|
|
@@ -56,11 +88,20 @@ var AuthProvider = class _AuthProvider {
|
|
|
56
88
|
apiKey: doDisco.access,
|
|
57
89
|
expiresAt: doDisco.expires
|
|
58
90
|
} : resolved;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
91
|
+
const MAX_TENTATIVAS = 3;
|
|
92
|
+
for (let tentativa = 0; ; tentativa++) {
|
|
93
|
+
try {
|
|
94
|
+
return await ensureFreshCredential(atual, {
|
|
95
|
+
config: this.config,
|
|
96
|
+
store: this.store,
|
|
97
|
+
env
|
|
98
|
+
}, deps);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
const falha = classificarFalhaDeRefresh(err);
|
|
101
|
+
if (!falha.transitorio || tentativa >= MAX_TENTATIVAS - 1) throw falha;
|
|
102
|
+
await new Promise((resolve) => setTimeout(resolve, esperaComJitter(tentativa)));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
64
105
|
});
|
|
65
106
|
}
|
|
66
107
|
/**
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/auth-provider.ts","../src/auth-entry.ts"],"sourcesContent":["import {\n authFilePath,\n ensureFreshCredential,\n openaiDeviceLogin,\n persistOAuthTokens,\n readStoredOAuth,\n} from '@theokit/sdk/auth'\nimport type {\n CredentialStoreConfig,\n OAuthProviderConfig,\n OAuthTokens,\n OpenAIDeviceConfig,\n ResolvedCredential,\n} from '@theokit/sdk/auth'\nimport { withFileLock } from '@theokit/sdk/persistence'\n\n/**\n * M60 — `AuthProvider`, the OO contract that unifies the SDK's free OAuth-lifecycle functions\n * (`openaiDeviceLogin` → `persistOAuthTokens` → `ensureFreshCredential`). Those are stateful across a\n * SHARED `config` (`OAuthProviderConfig`) + `store` (`CredentialStoreConfig`); this class holds that\n * state so a consumer authors `new AuthProvider(config, store).persist(...)` / `.ensureFresh(...)`\n * instead of threading `config`/`store` through every call — the `SDK → Theokit → AgentBuilder`\n * boundary applied to the auth domain (ENRICH, per blueprint D2: auth carries orchestration + state).\n *\n * It DELEGATES, never reimplements (parsimony Rung 9): each method forwards verbatim to the SDK\n * function, so login → persist → refresh produces byte-identical state to calling the SDK directly.\n *\n * SECRET-SAFETY (hard rule): this class NEVER logs, stringifies, or otherwise surfaces token material.\n * Methods return exactly what the SDK returns and add no observability — a token is data that flows\n * through, never something this layer emits. The parity/secret tests pin both properties.\n */\n\n/** The HTTP deps of `ensureFreshCredential` (`{ fetch, now }`) — typed off the SDK to avoid drift. */\ntype EnsureFreshHttpDeps = Parameters<typeof ensureFreshCredential>[2]\n/** The device-flow deps + prompt hook of `openaiDeviceLogin` — typed off the SDK. */\ntype DeviceLoginDeps = Parameters<typeof openaiDeviceLogin>[1]\ntype DeviceLoginHooks = Parameters<typeof openaiDeviceLogin>[2]\n\n/**\n * Falha de refresh, classificada — porque a decisão de tentar de novo depende da classe, não do texto.\n *\n * M74: repetir um `invalid_grant` não é resiliência, é ruído. O refresh token foi revogado e nenhuma\n * tentativa muda isso; o usuário que revogou o login espera três backoffs para ler uma mensagem que já\n * era conhecida na primeira resposta. Rede e 5xx são o oposto: quase sempre passam na segunda.\n *\n * SECRET-SAFETY: a mensagem nunca carrega material de token — só a classe e o motivo.\n */\nexport class RefreshFailure extends Error {\n constructor(\n message: string,\n /** `true` ⇒ vale tentar de novo com backoff. `false` ⇒ terminal, falha na primeira. */\n readonly transitorio: boolean,\n ) {\n super(message)\n this.name = 'RefreshFailure'\n }\n}\n\n/**\n * O que conta como transitório. Lista NOMEADA de propósito: é decisão de produto que envelhece, e\n * espalhá-la em `if` faz cada sítio envelhecer sozinho.\n */\nconst MOTIVOS_TRANSITORIOS = [\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'EAI_AGAIN',\n 'ENOTFOUND',\n 'AbortError',\n]\n\n/** Classifica uma falha de refresh. `invalid_grant` é terminal; rede e 5xx são transitórios. */\nexport function classificarFalhaDeRefresh(err: unknown): RefreshFailure {\n const texto = err instanceof Error ? `${err.name}: ${err.message}` : String(err)\n if (/invalid_grant|invalid_request|unauthorized_client/i.test(texto)) {\n return new RefreshFailure(\n 'o refresh token não é mais válido — refaça o login. Tentar de novo não muda o resultado.',\n false,\n )\n }\n const transitorio =\n MOTIVOS_TRANSITORIOS.some((m) => texto.includes(m)) ||\n /\\b(5\\d{2})\\b|timeout|network/i.test(texto)\n return new RefreshFailure(\n transitorio ? 'falha transitória ao refrescar a credencial' : 'falha ao refrescar a credencial',\n transitorio,\n )\n}\n\n/** Espera com jitter: backoff exponencial ±25%, para dois processos não retentarem em uníssono. */\nexport function esperaComJitter(tentativa: number, baseMs = 200, aleatorio = Math.random): number {\n const base = baseMs * 2 ** tentativa\n return Math.round(base * (0.75 + aleatorio() * 0.5))\n}\n\nexport class AuthProvider {\n constructor(\n private readonly config: OAuthProviderConfig,\n private readonly store: CredentialStoreConfig,\n ) {}\n\n /**\n * Refresh a resolved credential if stale. Delegates to `ensureFreshCredential` with the held\n * `config` + `store`; `env` (for reading the store's env overrides) + the `{ fetch, now }` HTTP deps\n * thread through. Returns the fresh `ResolvedCredential` — never logs the rotated token.\n */\n async ensureFresh(\n resolved: ResolvedCredential,\n deps: EnsureFreshHttpDeps,\n env?: Record<string, string | undefined>,\n ): Promise<ResolvedCredential> {\n if (resolved.kind !== 'oauth') {\n // Chave de API não expira: nenhum lock, nenhuma releitura, nenhum I/O. Manter o caminho quente\n // livre é o que torna o resolvedor por-run barato (M74, Risco 1).\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- `CredentialStoreConfig` do subpath /auth resolve sob tsc; o projeto type-aware do eslint o lê como error-typed.\n return ensureFreshCredential(resolved, { config: this.config, store: this.store, env }, deps)\n }\n\n const caminho: string = authFilePath(this.store, env)\n\n // SINGLE-FLIGHT in-process, ANTES do lock (M74, EC-2 do edge-case review).\n //\n // `withFileLock` (proper-lockfile) NÃO é reentrante: se uma run começar de dentro de um contexto\n // que já segura o lock — uma run aninhada, ou um time disparando membros enquanto o pai refresca —\n // a segunda aquisição espera até o timeout e o sintoma é \"a run travou\", sem erro nenhum. Com o\n // resolvedor por-run do M74 isso deixa de ser hipotético: `ensureFresh` passa a ser chamado de\n // dentro do stream. A promise em voo faz a reentrância resolver por composição, não por lock.\n const emVoo = AuthProvider.refreshEmVoo.get(caminho)\n if (emVoo !== undefined) return emVoo\n\n const promessa = this.refrescarSobLock(caminho, resolved, deps, env)\n AuthProvider.refreshEmVoo.set(caminho, promessa)\n try {\n return await promessa\n } finally {\n AuthProvider.refreshEmVoo.delete(caminho)\n }\n }\n\n /** Refresh em voo por caminho de store — a chave é o arquivo, não a instância. */\n private static readonly refreshEmVoo = new Map<string, Promise<ResolvedCredential>>()\n\n /**\n * O refresh propriamente dito, serializado entre PROCESSOS e com re-leitura.\n *\n * A re-leitura não é detalhe: sem ela o lock apenas serializa, e o segundo processo decide com o\n * estado que leu ANTES de esperar — refrescando de novo e invalidando o token que o primeiro acabou\n * de gravar. É o double-checked locking clássico, e é o que o teste de dois processos pega.\n */\n private refrescarSobLock(\n caminho: string,\n resolved: ResolvedCredential,\n deps: EnsureFreshHttpDeps,\n env?: Record<string, string | undefined>,\n ): Promise<ResolvedCredential> {\n /* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- os subpaths `/auth` e `/persistence` do SDK resolvem sob tsc (typecheck da raiz limpo); o projeto type-aware do eslint os lê como error-typed. Mesma nota que este arquivo já carregava para `CredentialStoreConfig`. */\n return withFileLock(caminho, async () => {\n // RE-LEITURA depois de adquirir o lock: outro processo pode ter refrescado enquanto esperávamos.\n const doDisco = readStoredOAuth(this.store, env)\n const atual =\n doDisco !== undefined\n ? { ...resolved, apiKey: doDisco.access, expiresAt: doDisco.expires }\n : resolved\n return await ensureFreshCredential(\n atual,\n { config: this.config, store: this.store, env },\n deps,\n )\n }) as Promise<ResolvedCredential>\n /* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n }\n\n /**\n * Run the headless OpenAI device-login flow. Delegates to `openaiDeviceLogin` (which JWT-extracts the\n * account id). `deviceConfig` is passed per-call because it is a distinct endpoint set from the\n * refresh `config`. Returns `OAuthTokens` — the caller persists them via {@link AuthProvider.persist}.\n */\n deviceLogin(\n deviceConfig: OpenAIDeviceConfig,\n deps: DeviceLoginDeps,\n hooks: DeviceLoginHooks,\n ): Promise<OAuthTokens> {\n return openaiDeviceLogin(deviceConfig, deps, hooks)\n }\n\n /**\n * Persist freshly-obtained tokens through the held `store`. Delegates to `persistOAuthTokens` and\n * returns the credential-file path (never the token). `env` selects the store's home override.\n */\n persist(provider: string, tokens: OAuthTokens, env?: Record<string, string | undefined>): string {\n return persistOAuthTokens(provider, tokens, this.store, env)\n }\n}\n","// M60 — `@theokit/agents/auth`: the OO auth contract. `AuthProvider` (ENRICH — it holds the shared\n// `config`+`store` state and delegates to the SDK's free OAuth-lifecycle functions), plus the auth\n// domain's types re-exported so a consumer types the whole surface from the Theokit layer, never\n// reaching back to `@theokit/sdk/auth`.\nexport { AuthProvider } from './auth/auth-provider.js'\n\n// M73 — \"enriquecer nunca reduz\". A mecânica de store do SDK atravessa como PASS-THROUGH PURO.\n//\n// Antes disto, este subpath exportava 1 valor e 6 tipos enquanto `@theokit/sdk/auth` exportava 19\n// símbolos: nenhuma função atravessava. Como o consumidor tem regra INQUEBRÁVEL de nunca importar\n// `@theokit/sdk*` direto, **reimplementar era a única saída legal** — e o agent-builder reescreveu\n// seis destes nomes, ~120 linhas de mecânica duplicada. A lacuna era daqui, não indisciplina de lá.\n//\n// PURO, e não wrapper (parsimony Rung 9): estas são funções de I/O sem estado a segurar. A camada\n// enriquece onde há orquestração — é o que `AuthProvider` faz com o par `config`+`store`. Envolver\n// isto acrescentaria indireção sem nada dentro, e **quebraria `instanceof`**: o consumidor faz\n// `err instanceof CredentialError`, o que só vale enquanto a classe for a MESMA referência do SDK.\n// `tests/unit/auth-parity.test.ts` trava essa identidade com `toBe`.\n//\n// `resolveCredential` NÃO atravessa, de propósito: o SDK e o agent-builder têm funções DIFERENTES com\n// esse nome (sync vs async, lança vs `undefined`, lê env vs não lê, infere provider vs recusa), e o\n// próprio SDK declara que a precedência de env, a inferência por prefixo e o provider declarado são\n// app policy do consumidor (`internal/auth/credential-store.ts`). Expor os dois no mesmo escopo seria\n// convite a importar o errado, com falha silenciosa.\nexport {\n authFilePath,\n CredentialError,\n credentialHome,\n readAuthFile,\n readStoredOAuth,\n writeCredential,\n} from '@theokit/sdk/auth'\nexport type { ResolveCredentialOptions } from '@theokit/sdk/auth'\nexport type {\n CredentialStoreConfig,\n DeviceDeps,\n OAuthProviderConfig,\n OAuthTokens,\n OpenAIDeviceConfig,\n ResolvedCredential,\n} from '@theokit/sdk/auth'\n"],"mappings":";;;;;AAAA,SACEA,cACAC,uBACAC,mBACAC,oBACAC,uBACK;AAQP,SAASC,oBAAoB;AAiFtB,IAAMC,eAAN,MAAMA,cAAAA;EA/Fb,OA+FaA;;;;;EACX,YACmBC,QACAC,OACjB;SAFiBD,SAAAA;SACAC,QAAAA;EAChB;;;;;;EAOH,MAAMC,YACJC,UACAC,MACAC,KAC6B;AAC7B,QAAIF,SAASG,SAAS,SAAS;AAI7B,aAAOC,sBAAsBJ,UAAU;QAAEH,QAAQ,KAAKA;QAAQC,OAAO,KAAKA;QAAOI;MAAI,GAAGD,IAAAA;IAC1F;AAEA,UAAMI,UAAkBC,aAAa,KAAKR,OAAOI,GAAAA;AASjD,UAAMK,QAAQX,cAAaY,aAAaC,IAAIJ,OAAAA;AAC5C,QAAIE,UAAUG,OAAW,QAAOH;AAEhC,UAAMI,WAAW,KAAKC,iBAAiBP,SAASL,UAAUC,MAAMC,GAAAA;AAChEN,kBAAaY,aAAaK,IAAIR,SAASM,QAAAA;AACvC,QAAI;AACF,aAAO,MAAMA;IACf,UAAA;AACEf,oBAAaY,aAAaM,OAAOT,OAAAA;IACnC;EACF;;EAGA,OAAwBG,eAAe,oBAAIO,IAAAA;;;;;;;;EASnCH,iBACNP,SACAL,UACAC,MACAC,KAC6B;AAE7B,WAAOc,aAAaX,SAAS,YAAA;AAE3B,YAAMY,UAAUC,gBAAgB,KAAKpB,OAAOI,GAAAA;AAC5C,YAAMiB,QACJF,YAAYP,SACR;QAAE,GAAGV;QAAUoB,QAAQH,QAAQI;QAAQC,WAAWL,QAAQM;MAAQ,IAClEvB;AACN,aAAO,MAAMI,sBACXe,OACA;QAAEtB,QAAQ,KAAKA;QAAQC,OAAO,KAAKA;QAAOI;MAAI,GAC9CD,IAAAA;IAEJ,CAAA;EAEF;;;;;;EAOAuB,YACEC,cACAxB,MACAyB,OACsB;AACtB,WAAOC,kBAAkBF,cAAcxB,MAAMyB,KAAAA;EAC/C;;;;;EAMAE,QAAQC,UAAkBC,QAAqB5B,KAAkD;AAC/F,WAAO6B,mBAAmBF,UAAUC,QAAQ,KAAKhC,OAAOI,GAAAA;EAC1D;AACF;;;ACxKA,SACE8B,gBAAAA,eACAC,iBACAC,gBACAC,cACAC,mBAAAA,kBACAC,uBACK;","names":["authFilePath","ensureFreshCredential","openaiDeviceLogin","persistOAuthTokens","readStoredOAuth","withFileLock","AuthProvider","config","store","ensureFresh","resolved","deps","env","kind","ensureFreshCredential","caminho","authFilePath","emVoo","refreshEmVoo","get","undefined","promessa","refrescarSobLock","set","delete","Map","withFileLock","doDisco","readStoredOAuth","atual","apiKey","access","expiresAt","expires","deviceLogin","deviceConfig","hooks","openaiDeviceLogin","persist","provider","tokens","persistOAuthTokens","authFilePath","CredentialError","credentialHome","readAuthFile","readStoredOAuth","writeCredential"]}
|
|
1
|
+
{"version":3,"sources":["../src/auth/auth-provider.ts","../src/auth-entry.ts"],"sourcesContent":["import {\n authFilePath,\n ensureFreshCredential,\n openaiDeviceLogin,\n persistOAuthTokens,\n readStoredOAuth,\n} from '@theokit/sdk/auth'\nimport type {\n CredentialStoreConfig,\n OAuthProviderConfig,\n OAuthTokens,\n OpenAIDeviceConfig,\n ResolvedCredential,\n} from '@theokit/sdk/auth'\nimport { withFileLock } from '@theokit/sdk/persistence'\n\n/**\n * M60 — `AuthProvider`, the OO contract that unifies the SDK's free OAuth-lifecycle functions\n * (`openaiDeviceLogin` → `persistOAuthTokens` → `ensureFreshCredential`). Those are stateful across a\n * SHARED `config` (`OAuthProviderConfig`) + `store` (`CredentialStoreConfig`); this class holds that\n * state so a consumer authors `new AuthProvider(config, store).persist(...)` / `.ensureFresh(...)`\n * instead of threading `config`/`store` through every call — the `SDK → Theokit → AgentBuilder`\n * boundary applied to the auth domain (ENRICH, per blueprint D2: auth carries orchestration + state).\n *\n * It DELEGATES, never reimplements (parsimony Rung 9): each method forwards verbatim to the SDK\n * function, so login → persist → refresh produces byte-identical state to calling the SDK directly.\n *\n * SECRET-SAFETY (hard rule): this class NEVER logs, stringifies, or otherwise surfaces token material.\n * Methods return exactly what the SDK returns and add no observability — a token is data that flows\n * through, never something this layer emits. The parity/secret tests pin both properties.\n */\n\n/** The HTTP deps of `ensureFreshCredential` (`{ fetch, now }`) — typed off the SDK to avoid drift. */\ntype EnsureFreshHttpDeps = Parameters<typeof ensureFreshCredential>[2]\n/** The device-flow deps + prompt hook of `openaiDeviceLogin` — typed off the SDK. */\ntype DeviceLoginDeps = Parameters<typeof openaiDeviceLogin>[1]\ntype DeviceLoginHooks = Parameters<typeof openaiDeviceLogin>[2]\n\n/**\n * Falha de refresh, classificada — porque a decisão de tentar de novo depende da classe, não do texto.\n *\n * M74: repetir um `invalid_grant` não é resiliência, é ruído. O refresh token foi revogado e nenhuma\n * tentativa muda isso; o usuário que revogou o login espera três backoffs para ler uma mensagem que já\n * era conhecida na primeira resposta. Rede e 5xx são o oposto: quase sempre passam na segunda.\n *\n * SECRET-SAFETY: a mensagem nunca carrega material de token — só a classe e o motivo.\n */\nexport class RefreshFailure extends Error {\n constructor(\n message: string,\n /** `true` ⇒ vale tentar de novo com backoff. `false` ⇒ terminal, falha na primeira. */\n readonly transitorio: boolean,\n ) {\n super(message)\n this.name = 'RefreshFailure'\n }\n}\n\n/**\n * O que conta como transitório. Lista NOMEADA de propósito: é decisão de produto que envelhece, e\n * espalhá-la em `if` faz cada sítio envelhecer sozinho.\n */\nconst MOTIVOS_TRANSITORIOS = [\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'EAI_AGAIN',\n 'ENOTFOUND',\n 'AbortError',\n]\n\n/** Classifica uma falha de refresh. `invalid_grant` é terminal; rede e 5xx são transitórios. */\nexport function classificarFalhaDeRefresh(err: unknown): RefreshFailure {\n const texto = err instanceof Error ? `${err.name}: ${err.message}` : String(err)\n if (/invalid_grant|invalid_request|unauthorized_client/i.test(texto)) {\n return new RefreshFailure(\n 'o refresh token não é mais válido — refaça o login. Tentar de novo não muda o resultado.',\n false,\n )\n }\n const transitorio =\n MOTIVOS_TRANSITORIOS.some((m) => texto.includes(m)) ||\n /\\b(5\\d{2})\\b|timeout|network/i.test(texto)\n return new RefreshFailure(\n transitorio ? 'falha transitória ao refrescar a credencial' : 'falha ao refrescar a credencial',\n transitorio,\n )\n}\n\n/** Espera com jitter: backoff exponencial ±25%, para dois processos não retentarem em uníssono. */\nexport function esperaComJitter(tentativa: number, baseMs = 200, aleatorio = Math.random): number {\n const base = baseMs * 2 ** tentativa\n return Math.round(base * (0.75 + aleatorio() * 0.5))\n}\n\nexport class AuthProvider {\n constructor(\n private readonly config: OAuthProviderConfig,\n private readonly store: CredentialStoreConfig,\n ) {}\n\n /**\n * Refresh a resolved credential if stale. Delegates to `ensureFreshCredential` with the held\n * `config` + `store`; `env` (for reading the store's env overrides) + the `{ fetch, now }` HTTP deps\n * thread through. Returns the fresh `ResolvedCredential` — never logs the rotated token.\n */\n async ensureFresh(\n resolved: ResolvedCredential,\n deps: EnsureFreshHttpDeps,\n env?: Record<string, string | undefined>,\n ): Promise<ResolvedCredential> {\n if (resolved.kind !== 'oauth') {\n // Chave de API não expira: nenhum lock, nenhuma releitura, nenhum I/O. Manter o caminho quente\n // livre é o que torna o resolvedor por-run barato (M74, Risco 1).\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- `CredentialStoreConfig` do subpath /auth resolve sob tsc; o projeto type-aware do eslint o lê como error-typed.\n return ensureFreshCredential(resolved, { config: this.config, store: this.store, env }, deps)\n }\n\n const caminho: string = authFilePath(this.store, env)\n\n // SINGLE-FLIGHT in-process, ANTES do lock (M74, EC-2 do edge-case review).\n //\n // `withFileLock` (proper-lockfile) NÃO é reentrante: se uma run começar de dentro de um contexto\n // que já segura o lock — uma run aninhada, ou um time disparando membros enquanto o pai refresca —\n // a segunda aquisição espera até o timeout e o sintoma é \"a run travou\", sem erro nenhum. Com o\n // resolvedor por-run do M74 isso deixa de ser hipotético: `ensureFresh` passa a ser chamado de\n // dentro do stream. A promise em voo faz a reentrância resolver por composição, não por lock.\n const emVoo = AuthProvider.refreshEmVoo.get(caminho)\n if (emVoo !== undefined) return emVoo\n\n const promessa = this.refrescarSobLock(caminho, resolved, deps, env)\n AuthProvider.refreshEmVoo.set(caminho, promessa)\n try {\n return await promessa\n } finally {\n AuthProvider.refreshEmVoo.delete(caminho)\n }\n }\n\n /** Refresh em voo por caminho de store — a chave é o arquivo, não a instância. */\n private static readonly refreshEmVoo = new Map<string, Promise<ResolvedCredential>>()\n\n /**\n * O refresh propriamente dito, serializado entre PROCESSOS e com re-leitura.\n *\n * A re-leitura não é detalhe: sem ela o lock apenas serializa, e o segundo processo decide com o\n * estado que leu ANTES de esperar — refrescando de novo e invalidando o token que o primeiro acabou\n * de gravar. É o double-checked locking clássico, e é o que o teste de dois processos pega.\n */\n private refrescarSobLock(\n caminho: string,\n resolved: ResolvedCredential,\n deps: EnsureFreshHttpDeps,\n env?: Record<string, string | undefined>,\n ): Promise<ResolvedCredential> {\n /* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- os subpaths `/auth` e `/persistence` do SDK resolvem sob tsc (typecheck da raiz limpo); o projeto type-aware do eslint os lê como error-typed. Mesma nota que este arquivo já carregava para `CredentialStoreConfig`. */\n return withFileLock(caminho, async () => {\n // RE-LEITURA depois de adquirir o lock: outro processo pode ter refrescado enquanto esperávamos.\n const doDisco = readStoredOAuth(this.store, env)\n const atual =\n doDisco !== undefined\n ? { ...resolved, apiKey: doDisco.access, expiresAt: doDisco.expires }\n : resolved\n // Retry SÓ no transitório. Um `invalid_grant` falha na PRIMEIRA: o token foi revogado e nenhuma\n // tentativa muda isso — insistir só atrasa a mensagem que o usuário precisa ler.\n //\n // Este laço já existiu e foi APAGADO por um lint-fix meu que reescreveu o bloco inteiro; o\n // review pegou (`tentativas de POST = 1`, contra as 3 que a DoD exige). Testar o classificador\n // isolado prova que ele classifica, não que está LIGADO — por isso há um gate estrutural.\n const MAX_TENTATIVAS = 3\n for (let tentativa = 0; ; tentativa++) {\n try {\n return await ensureFreshCredential(\n atual,\n { config: this.config, store: this.store, env },\n deps,\n )\n } catch (err) {\n const falha = classificarFalhaDeRefresh(err)\n if (!falha.transitorio || tentativa >= MAX_TENTATIVAS - 1) throw falha\n await new Promise((resolve) => setTimeout(resolve, esperaComJitter(tentativa)))\n }\n }\n }) as Promise<ResolvedCredential>\n /* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n }\n\n /**\n * Run the headless OpenAI device-login flow. Delegates to `openaiDeviceLogin` (which JWT-extracts the\n * account id). `deviceConfig` is passed per-call because it is a distinct endpoint set from the\n * refresh `config`. Returns `OAuthTokens` — the caller persists them via {@link AuthProvider.persist}.\n */\n deviceLogin(\n deviceConfig: OpenAIDeviceConfig,\n deps: DeviceLoginDeps,\n hooks: DeviceLoginHooks,\n ): Promise<OAuthTokens> {\n return openaiDeviceLogin(deviceConfig, deps, hooks)\n }\n\n /**\n * Persist freshly-obtained tokens through the held `store`. Delegates to `persistOAuthTokens` and\n * returns the credential-file path (never the token). `env` selects the store's home override.\n */\n persist(provider: string, tokens: OAuthTokens, env?: Record<string, string | undefined>): string {\n return persistOAuthTokens(provider, tokens, this.store, env)\n }\n}\n","// M60 — `@theokit/agents/auth`: the OO auth contract. `AuthProvider` (ENRICH — it holds the shared\n// `config`+`store` state and delegates to the SDK's free OAuth-lifecycle functions), plus the auth\n// domain's types re-exported so a consumer types the whole surface from the Theokit layer, never\n// reaching back to `@theokit/sdk/auth`.\nexport { AuthProvider } from './auth/auth-provider.js'\n\n// M73 — \"enriquecer nunca reduz\". A mecânica de store do SDK atravessa como PASS-THROUGH PURO.\n//\n// Antes disto, este subpath exportava 1 valor e 6 tipos enquanto `@theokit/sdk/auth` exportava 19\n// símbolos: nenhuma função atravessava. Como o consumidor tem regra INQUEBRÁVEL de nunca importar\n// `@theokit/sdk*` direto, **reimplementar era a única saída legal** — e o agent-builder reescreveu\n// seis destes nomes, ~120 linhas de mecânica duplicada. A lacuna era daqui, não indisciplina de lá.\n//\n// PURO, e não wrapper (parsimony Rung 9): estas são funções de I/O sem estado a segurar. A camada\n// enriquece onde há orquestração — é o que `AuthProvider` faz com o par `config`+`store`. Envolver\n// isto acrescentaria indireção sem nada dentro, e **quebraria `instanceof`**: o consumidor faz\n// `err instanceof CredentialError`, o que só vale enquanto a classe for a MESMA referência do SDK.\n// `tests/unit/auth-parity.test.ts` trava essa identidade com `toBe`.\n//\n// `resolveCredential` NÃO atravessa, de propósito: o SDK e o agent-builder têm funções DIFERENTES com\n// esse nome (sync vs async, lança vs `undefined`, lê env vs não lê, infere provider vs recusa), e o\n// próprio SDK declara que a precedência de env, a inferência por prefixo e o provider declarado são\n// app policy do consumidor (`internal/auth/credential-store.ts`). Expor os dois no mesmo escopo seria\n// convite a importar o errado, com falha silenciosa.\nexport {\n authFilePath,\n CredentialError,\n credentialHome,\n readAuthFile,\n readStoredOAuth,\n writeCredential,\n} from '@theokit/sdk/auth'\nexport type { ResolveCredentialOptions } from '@theokit/sdk/auth'\nexport type {\n CredentialStoreConfig,\n DeviceDeps,\n OAuthProviderConfig,\n OAuthTokens,\n OpenAIDeviceConfig,\n ResolvedCredential,\n} from '@theokit/sdk/auth'\n"],"mappings":"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","names":["authFilePath","ensureFreshCredential","openaiDeviceLogin","persistOAuthTokens","readStoredOAuth","withFileLock","RefreshFailure","Error","message","transitorio","name","MOTIVOS_TRANSITORIOS","classificarFalhaDeRefresh","err","texto","String","test","some","m","includes","esperaComJitter","tentativa","baseMs","aleatorio","Math","random","base","round","AuthProvider","config","store","ensureFresh","resolved","deps","env","kind","ensureFreshCredential","caminho","authFilePath","emVoo","refreshEmVoo","get","undefined","promessa","refrescarSobLock","set","delete","Map","withFileLock","doDisco","readStoredOAuth","atual","apiKey","access","expiresAt","expires","MAX_TENTATIVAS","falha","Promise","resolve","setTimeout","deviceLogin","deviceConfig","hooks","openaiDeviceLogin","persist","provider","tokens","persistOAuthTokens","authFilePath","CredentialError","credentialHome","readAuthFile","readStoredOAuth","writeCredential"]}
|
package/dist/bridge.js
CHANGED
|
@@ -1164,10 +1164,7 @@ function buildSdkTools(compiledTools, defineTool, extraSdkTools = [], runContext
|
|
|
1164
1164
|
];
|
|
1165
1165
|
}
|
|
1166
1166
|
__name(buildSdkTools, "buildSdkTools");
|
|
1167
|
-
async function
|
|
1168
|
-
return typeof k === "function" ? await k() : k;
|
|
1169
|
-
}
|
|
1170
|
-
__name(resolverApiKey, "resolverApiKey");
|
|
1167
|
+
var resolverApiKey = /* @__PURE__ */ __name(async (k) => typeof k === "function" ? await k() : k, "resolverApiKey");
|
|
1171
1168
|
function createSdkAgentStream(compiled, compiledTools, apiKey, overrides = {}) {
|
|
1172
1169
|
const model = overrides.model ?? compiled.model ?? "openai/gpt-4o-mini";
|
|
1173
1170
|
const reasoningEffort = overrides.reasoningEffort ?? compiled.reasoningEffort;
|
|
@@ -1205,9 +1202,8 @@ function createSdkAgentStream(compiled, compiledTools, apiKey, overrides = {}) {
|
|
|
1205
1202
|
keys: runContext !== void 0 ? Object.keys(runContext) : []
|
|
1206
1203
|
});
|
|
1207
1204
|
try {
|
|
1208
|
-
const apiKeyResolvida = await resolverApiKey(apiKey);
|
|
1209
1205
|
yield* streamSdkAgent(rt, compiled, sdkTools, {
|
|
1210
|
-
apiKey:
|
|
1206
|
+
apiKey: await resolverApiKey(apiKey),
|
|
1211
1207
|
model,
|
|
1212
1208
|
reasoningEffort,
|
|
1213
1209
|
overrides,
|
|
@@ -1315,9 +1311,8 @@ function toAgentFactory(def, opts) {
|
|
|
1315
1311
|
baseDir: overrides.baseDir
|
|
1316
1312
|
};
|
|
1317
1313
|
const extra = buildExtraCreateOptions(overrides, compiled);
|
|
1318
|
-
const apiKeyResolvida = await resolverApiKey(opts.apiKey);
|
|
1319
1314
|
const agent = await rt.Agent.getOrCreate(sessionId, {
|
|
1320
|
-
apiKey:
|
|
1315
|
+
apiKey: await resolverApiKey(opts.apiKey),
|
|
1321
1316
|
model: buildModelSelection(model, reasoningEffort),
|
|
1322
1317
|
tools: sdkTools,
|
|
1323
1318
|
...m8,
|
|
@@ -3511,4 +3506,4 @@ export {
|
|
|
3511
3506
|
generateAgentManifest,
|
|
3512
3507
|
agentsPlugin
|
|
3513
3508
|
};
|
|
3514
|
-
//# sourceMappingURL=chunk-
|
|
3509
|
+
//# sourceMappingURL=chunk-ITHCIDRN.js.map
|