@nema-project/nema 0.0.0-stage → 0.1.4

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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunks/chunk-HEIQINX3.js +825 -0
  3. package/dist/chunks/chunk-WT3XUFWU.js +86 -0
  4. package/dist/claude/adapter.d.ts +19 -0
  5. package/dist/claude/adapter.js +258 -0
  6. package/dist/claude/events.d.ts +29 -0
  7. package/dist/claude/events.js +75 -0
  8. package/dist/claude/launch.d.ts +22 -0
  9. package/dist/claude/launch.js +80 -0
  10. package/dist/claude/token.d.ts +9 -0
  11. package/dist/claude/token.js +87 -0
  12. package/dist/codex/adapter.d.ts +17 -0
  13. package/dist/codex/adapter.js +151 -0
  14. package/dist/codex/events.d.ts +16 -0
  15. package/dist/codex/events.js +47 -0
  16. package/dist/codex/isolation.d.ts +33 -0
  17. package/dist/codex/isolation.js +115 -0
  18. package/dist/config.d.ts +29 -0
  19. package/dist/config.js +75 -0
  20. package/dist/configCommands.d.ts +13 -0
  21. package/dist/configCommands.js +69 -0
  22. package/dist/daemon/cleanup.d.ts +5 -0
  23. package/dist/daemon/cleanup.js +49 -0
  24. package/dist/daemon/connection.d.ts +22 -0
  25. package/dist/daemon/connection.js +166 -0
  26. package/dist/daemon/rpc.d.ts +14 -0
  27. package/dist/daemon/rpc.js +20 -0
  28. package/dist/daemon/runner.d.ts +34 -0
  29. package/dist/daemon/runner.js +94 -0
  30. package/dist/daemon/setup.d.ts +39 -0
  31. package/dist/daemon/setup.js +95 -0
  32. package/dist/direct/adapter.d.ts +30 -0
  33. package/dist/direct/adapter.js +190 -0
  34. package/dist/direct/authCommands.d.ts +54 -0
  35. package/dist/direct/authCommands.js +268 -0
  36. package/dist/direct/authStore.d.ts +120 -0
  37. package/dist/direct/authStore.js +351 -0
  38. package/dist/direct/byok.d.ts +27 -0
  39. package/dist/direct/byok.js +67 -0
  40. package/dist/direct/codexLogin.d.ts +34 -0
  41. package/dist/direct/codexLogin.js +93 -0
  42. package/dist/direct/jwt.d.ts +4 -0
  43. package/dist/direct/jwt.js +22 -0
  44. package/dist/direct/lock.d.ts +16 -0
  45. package/dist/direct/lock.js +134 -0
  46. package/dist/direct/loop.d.ts +33 -0
  47. package/dist/direct/loop.js +110 -0
  48. package/dist/direct/siwc.d.ts +63 -0
  49. package/dist/direct/siwc.js +403 -0
  50. package/dist/direct/tools.d.ts +17 -0
  51. package/dist/direct/tools.js +104 -0
  52. package/dist/direct/transport.d.ts +177 -0
  53. package/dist/direct/transport.js +437 -0
  54. package/dist/doctor.d.ts +28 -0
  55. package/dist/doctor.js +178 -0
  56. package/dist/extensions/handlers.d.ts +2 -0
  57. package/dist/extensions/handlers.js +10 -0
  58. package/dist/extensions/store.d.ts +11 -0
  59. package/dist/extensions/store.js +132 -0
  60. package/dist/github/client.d.ts +47 -0
  61. package/dist/github/client.js +383 -0
  62. package/dist/github/gh.d.ts +56 -0
  63. package/dist/github/gh.js +385 -0
  64. package/dist/index.d.ts +2 -0
  65. package/dist/index.js +144 -0
  66. package/dist/localSettings.d.ts +8 -0
  67. package/dist/localSettings.js +26 -0
  68. package/dist/mcp/server.d.ts +5 -0
  69. package/dist/mcp/server.js +83 -0
  70. package/dist/ownedProcess.d.ts +39 -0
  71. package/dist/ownedProcess.js +229 -0
  72. package/dist/platform.d.ts +10 -0
  73. package/dist/platform.js +60 -0
  74. package/dist/report/commands.d.ts +41 -0
  75. package/dist/report/commands.js +159 -0
  76. package/dist/report/desktop.d.ts +3 -0
  77. package/dist/report/desktop.js +123 -0
  78. package/dist/report/git.d.ts +5 -0
  79. package/dist/report/git.js +42 -0
  80. package/dist/report/install.d.ts +22 -0
  81. package/dist/report/install.js +183 -0
  82. package/dist/report/send.d.ts +35 -0
  83. package/dist/report/send.js +208 -0
  84. package/dist/report/token.d.ts +5 -0
  85. package/dist/report/token.js +86 -0
  86. package/dist/repos/git.d.ts +48 -0
  87. package/dist/repos/git.js +370 -0
  88. package/dist/repos/graphify.d.ts +32 -0
  89. package/dist/repos/graphify.js +292 -0
  90. package/dist/repos/handlers.d.ts +20 -0
  91. package/dist/repos/handlers.js +255 -0
  92. package/dist/repos/indexState.d.ts +10 -0
  93. package/dist/repos/indexState.js +45 -0
  94. package/dist/repos/pickDirectory.d.ts +11 -0
  95. package/dist/repos/pickDirectory.js +140 -0
  96. package/dist/repos/reportCheck.d.ts +13 -0
  97. package/dist/repos/reportCheck.js +268 -0
  98. package/dist/repos/watcher.d.ts +64 -0
  99. package/dist/repos/watcher.js +202 -0
  100. package/dist/room/client.d.ts +26 -0
  101. package/dist/room/client.js +76 -0
  102. package/dist/secrets/file.d.ts +12 -0
  103. package/dist/secrets/file.js +89 -0
  104. package/dist/secrets/github.d.ts +10 -0
  105. package/dist/secrets/github.js +29 -0
  106. package/dist/secrets/permissions.d.ts +7 -0
  107. package/dist/secrets/permissions.js +86 -0
  108. package/dist/systemGit.d.ts +14 -0
  109. package/dist/systemGit.js +75 -0
  110. package/dist/wp/commands.d.ts +15 -0
  111. package/dist/wp/commands.js +103 -0
  112. package/package.json +26 -4
  113. package/src/report/SKILL.md +21 -0
  114. package/README.md +0 -3
@@ -0,0 +1,54 @@
1
+ import { type Refresher } from "./authStore.js";
2
+ import { type SiwcEndpoints } from "./siwc.js";
3
+ export { authByok } from "./byok.js";
4
+ export type Out = {
5
+ log: (s: string) => void;
6
+ error: (s: string) => void;
7
+ };
8
+ /** Why the saved provider's model catalog could not be read. The message is the reason, never an upstream body. */
9
+ export declare class ModelsListError extends Error {
10
+ readonly reason: "not_applicable" | "not_connected" | "list_failed";
11
+ constructor(reason: "not_applicable" | "not_connected" | "list_failed");
12
+ }
13
+ export declare const consoleOut: Out;
14
+ /** Opens the system browser without a shell; the URL is printed if launching fails. */
15
+ export declare function defaultOpenBrowser(url: string): void;
16
+ export declare function authLogin(o: {
17
+ nemaHome: string;
18
+ out: Out;
19
+ openBrowser?: (url: string) => void;
20
+ port?: number;
21
+ timeoutMs?: number;
22
+ endpoints?: SiwcEndpoints;
23
+ /** Codex CLI home for authMode=compat. Defaults to $CODEX_HOME or ~/.codex. */
24
+ codexHome?: string;
25
+ signal?: AbortSignal;
26
+ requireTrustedAuthorizeUrl?: boolean;
27
+ redactCallbackErrors?: boolean;
28
+ }): Promise<number>;
29
+ export declare function authStatus(o: {
30
+ nemaHome: string;
31
+ out: Out;
32
+ codexHome?: string;
33
+ }): number;
34
+ export declare function authLogout(o: {
35
+ nemaHome: string;
36
+ out: Out;
37
+ lockTimeoutMs?: number;
38
+ }): Promise<number>;
39
+ export declare function authProbe(o: {
40
+ nemaHome: string;
41
+ out: Out;
42
+ apiBase?: string;
43
+ refresher?: Refresher;
44
+ forceRefresh?: boolean;
45
+ codexHome?: string;
46
+ signal?: AbortSignal;
47
+ }): Promise<number>;
48
+ /** Listed models for the saved provider. Does not send a generation request. */
49
+ export declare function listConfiguredModels(o: {
50
+ nemaHome: string;
51
+ codexHome?: string;
52
+ refresher?: Refresher;
53
+ signal?: AbortSignal;
54
+ }): Promise<string[]>;
@@ -0,0 +1,268 @@
1
+ // direct/authCommands.js
2
+ import { spawn } from "node:child_process";
3
+ import { randomUUID } from "node:crypto";
4
+ import { existsSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { browserCommand } from "../platform.js";
7
+ import { configErrorMessage, loadConfig } from "../config.js";
8
+ import { AuthStore, ReloginRequiredError, UNUSABLE_FILE_MESSAGE } from "./authStore.js";
9
+ import { CodexFileTokenSource, CodexLoginError, defaultCodexHome, readCodexCliTokens } from "./codexLogin.js";
10
+ import { jwtExp } from "./jwt.js";
11
+ import { LockTimeoutError, withFileLock } from "./lock.js";
12
+ import { cliVersion, siwcLogin, siwcRefresher } from "./siwc.js";
13
+ import { BYOK_PROFILE, COMPAT_PROFILE, listModels, outputText, SIWC_PROFILE, streamResponse } from "./transport.js";
14
+ import { ByokTokenSource } from "./byok.js";
15
+ import { authByok } from "./byok.js";
16
+ var ModelsListError = class extends Error {
17
+ reason;
18
+ constructor(reason) {
19
+ super(reason);
20
+ this.reason = reason;
21
+ this.name = "ModelsListError";
22
+ }
23
+ };
24
+ var consoleOut = { log: (s) => console.log(s), error: (s) => console.error(s) };
25
+ var errMessage = (e) => e instanceof Error ? e.message : String(e);
26
+ var tail4 = (s) => s ? `\u2026${s.slice(-4)}` : "\u672A\u77E5";
27
+ var NOT_LOGGED_IN = "\u5C1A\u672A\u767B\u5F55\u76F4\u8FDE\u6A21\u5F0F\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C nema auth login";
28
+ function defaultOpenBrowser(url) {
29
+ const command = browserCommand(url);
30
+ try {
31
+ const child = spawn(command.bin, command.args, { stdio: "ignore", detached: true, windowsHide: true });
32
+ child.on("error", () => {
33
+ });
34
+ child.unref();
35
+ } catch {
36
+ }
37
+ }
38
+ async function authLogin(o) {
39
+ let authMode;
40
+ try {
41
+ ({ authMode } = loadConfig(o.nemaHome));
42
+ } catch (e) {
43
+ o.out.error(configErrorMessage(e));
44
+ return 1;
45
+ }
46
+ if (authMode === "compat")
47
+ return confirmCodexLogin(o);
48
+ const open = o.openBrowser ?? defaultOpenBrowser;
49
+ try {
50
+ const auth = await siwcLogin({
51
+ nemaHome: o.nemaHome,
52
+ port: o.port,
53
+ timeoutMs: o.timeoutMs,
54
+ endpoints: o.endpoints,
55
+ signal: o.signal,
56
+ requireTrustedAuthorizeUrl: o.requireTrustedAuthorizeUrl,
57
+ redactCallbackErrors: o.redactCallbackErrors,
58
+ openBrowser: (url) => {
59
+ o.out.log(`\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u300CSign in with ChatGPT\u300D\u767B\u5F55\u3002\u82E5\u6D4F\u89C8\u5668\u6CA1\u6709\u81EA\u52A8\u6253\u5F00\uFF0C\u8BF7\u8BBF\u95EE\uFF1A
60
+ ${url}`);
61
+ try {
62
+ open(url);
63
+ } catch {
64
+ }
65
+ }
66
+ });
67
+ o.out.log(`\u767B\u5F55\u6210\u529F\uFF1A\u6A21\u5F0F siwc\uFF0C\u8D26\u53F7 ${tail4(auth.accountId)}\uFF0Cclient id ${auth.clientId}`);
68
+ return 0;
69
+ } catch (e) {
70
+ o.out.error(`\u767B\u5F55\u5931\u8D25\uFF1A${errMessage(e)}`);
71
+ return 1;
72
+ }
73
+ }
74
+ function confirmCodexLogin(o) {
75
+ try {
76
+ const t = readCodexCliTokens(o.codexHome ?? defaultCodexHome());
77
+ const exp = t.exp === null ? "\u672A\u77E5" : new Date(t.exp * 1e3).toISOString();
78
+ o.out.log(`\u5DF2\u4F7F\u7528 Codex \u767B\u5F55\uFF1A\u8D26\u53F7 ${tail4(t.accountId)}\uFF0Caccess token \u5230\u671F ${exp}\u3002Nema \u53EA\u8BFB\u53D6\u8BE5\u6587\u4EF6\uFF0C\u4E0D\u4F1A\u5237\u65B0\u6216\u6539\u5199\u5B83\u3002`);
79
+ return 0;
80
+ } catch (e) {
81
+ o.out.error(e instanceof CodexLoginError ? e.message : `\u767B\u5F55\u5931\u8D25\uFF1A${errMessage(e)}`);
82
+ return 1;
83
+ }
84
+ }
85
+ function authStatus(o) {
86
+ let authMode;
87
+ try {
88
+ ({ authMode } = loadConfig(o.nemaHome));
89
+ } catch (e) {
90
+ o.out.error(configErrorMessage(e));
91
+ return 1;
92
+ }
93
+ if (authMode === "compat")
94
+ return confirmCodexLogin(o) === 0 ? 0 : 1;
95
+ const { auth, reason } = new AuthStore(o.nemaHome, siwcRefresher).inspect();
96
+ if (!auth) {
97
+ o.out.log(reason === "unusable" ? UNUSABLE_FILE_MESSAGE : NOT_LOGGED_IN);
98
+ return 1;
99
+ }
100
+ o.out.log(`\u6A21\u5F0F\uFF1A${auth.mode}`);
101
+ o.out.log(`\u8D26\u53F7\uFF1A${tail4(auth.accountId)}`);
102
+ o.out.log(`client id\uFF1A${auth.clientId}`);
103
+ if (auth.tokens.refresh_token === "") {
104
+ o.out.log("\u4EE4\u724C\uFF1A\u5DF2\u5931\u6548\uFF0C\u9700\u8981\u91CD\u65B0\u767B\u5F55\uFF08nema auth login\uFF09");
105
+ } else {
106
+ const exp = jwtExp(auth.tokens.access_token);
107
+ o.out.log(`access token \u8FC7\u671F\u65F6\u95F4\uFF1A${exp === null ? "\u672A\u77E5" : new Date(exp * 1e3).toISOString()}`);
108
+ }
109
+ o.out.log(`\u4E0A\u6B21\u5237\u65B0\uFF1A${auth.lastRefresh}`);
110
+ if (auth.lastAuthError) {
111
+ const e = auth.lastAuthError;
112
+ o.out.log(`\u6700\u8FD1\u4E00\u6B21\u8BA4\u8BC1\u9519\u8BEF\uFF1A${e.code}\uFF08${e.message}\uFF09\u4E8E ${e.at}`);
113
+ }
114
+ return 0;
115
+ }
116
+ async function authLogout(o) {
117
+ const store = new AuthStore(o.nemaHome, siwcRefresher);
118
+ if (!existsSync(o.nemaHome)) {
119
+ o.out.log("\u5F53\u524D\u6CA1\u6709\u76F4\u8FDE\u767B\u5F55\u3002");
120
+ return 0;
121
+ }
122
+ try {
123
+ await withFileLock(path.join(o.nemaHome, "auth.lock"), async () => store.clear(), { timeoutMs: o.lockTimeoutMs ?? 15e3 });
124
+ } catch (e) {
125
+ if (e instanceof LockTimeoutError) {
126
+ o.out.error("\u7B49\u5F85\u51ED\u8BC1\u9501\u8D85\u65F6\uFF08\u53EF\u80FD\u6709\u5176\u4ED6 nema \u8FDB\u7A0B\u6B63\u5728\u5237\u65B0\u767B\u5F55\uFF09\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5 nema auth logout");
127
+ return 1;
128
+ }
129
+ throw e;
130
+ }
131
+ o.out.log("\u5DF2\u9000\u51FA\u76F4\u8FDE\u767B\u5F55\uFF08\u5DF2\u5220\u9664 auth.json\uFF09\u3002");
132
+ return 0;
133
+ }
134
+ var PROBE_PROMPT = "\u53EA\u56DE\u590D ok";
135
+ var PROBE_INSTRUCTIONS = "You are a connectivity probe. Reply with exactly what the user asks.";
136
+ var PROBE_TIMEOUT_MS = 6e4;
137
+ async function authProbe(o) {
138
+ let authMode;
139
+ try {
140
+ ({ authMode } = loadConfig(o.nemaHome));
141
+ } catch (e) {
142
+ o.out.error(configErrorMessage(e));
143
+ return 1;
144
+ }
145
+ if (authMode === "compat") {
146
+ const source = new CodexFileTokenSource(o.codexHome ?? defaultCodexHome());
147
+ try {
148
+ await source.getAccessToken({ signal: o.signal });
149
+ } catch (e) {
150
+ o.out.error(e instanceof CodexLoginError ? e.message : errMessage(e));
151
+ return 1;
152
+ }
153
+ if (o.forceRefresh)
154
+ o.out.log("\u5DF2\u91CD\u65B0\u8BFB\u53D6 Codex \u767B\u5F55\uFF08Nema \u4E0D\u5237\u65B0\u8BE5\u51ED\u8BC1\uFF09");
155
+ const profile2 = o.apiBase ? { ...COMPAT_PROFILE, baseUrl: o.apiBase } : COMPAT_PROFILE;
156
+ return runProbe(o, source, profile2, o.signal);
157
+ }
158
+ const config = loadConfig(o.nemaHome);
159
+ if (config.authMode === "byok") {
160
+ const profile2 = { ...BYOK_PROFILE, baseUrl: config.byokBaseUrl ?? BYOK_PROFILE.baseUrl };
161
+ return runProbe(o, new ByokTokenSource(o.nemaHome), profile2, o.signal);
162
+ }
163
+ const store = new AuthStore(o.nemaHome, o.refresher ?? siwcRefresher);
164
+ const { auth, reason } = store.inspect();
165
+ if (!auth || auth.tokens.refresh_token === "") {
166
+ o.out.error(reason === "unusable" ? UNUSABLE_FILE_MESSAGE : NOT_LOGGED_IN);
167
+ return 1;
168
+ }
169
+ if (auth.mode !== "siwc") {
170
+ o.out.error("\u5F53\u524D\u767B\u5F55\u4E0D\u662F siwc \u6A21\u5F0F\uFF1Bcompat \u6A21\u5F0F\u8BF7\u628A authMode \u8BBE\u4E3A compat\u3002");
171
+ return 1;
172
+ }
173
+ const profile = o.apiBase ? { ...SIWC_PROFILE, baseUrl: o.apiBase } : SIWC_PROFILE;
174
+ if (o.forceRefresh) {
175
+ const before = auth.lastRefresh;
176
+ const { auth: current } = await store.getAccessToken({ force: true, signal: o.signal });
177
+ o.out.log(current.lastRefresh !== before ? `\u5DF2\u5F3A\u5236\u5237\u65B0\u4EE4\u724C\uFF08${current.lastRefresh}\uFF09` : "\u4EE4\u724C\u672A\u5237\u65B0");
178
+ }
179
+ return runProbe(o, store, profile, o.signal);
180
+ }
181
+ async function runProbe(o, source, profile, ownerSignal) {
182
+ try {
183
+ const ctx = { auth: source, profile, version: cliVersion(), signal: ownerSignal ? AbortSignal.any([ownerSignal, AbortSignal.timeout(PROBE_TIMEOUT_MS)]) : AbortSignal.timeout(PROBE_TIMEOUT_MS) };
184
+ const models = await listModels(ctx);
185
+ o.out.log(`\u53EF\u7528\u6A21\u578B\uFF08${models.length}\uFF09\uFF1A${models.join(", ") || "\u65E0"}`);
186
+ if (models.length === 0) {
187
+ o.out.error("\u63A2\u9488\u5931\u8D25\uFF1A\u6A21\u578B\u5217\u8868\u4E3A\u7A7A");
188
+ return 1;
189
+ }
190
+ const { model: configured } = loadConfig(o.nemaHome);
191
+ const model = configured && models.includes(configured) ? configured : models[0];
192
+ let streamed = "";
193
+ const r = await streamResponse({
194
+ model,
195
+ instructions: PROBE_INSTRUCTIONS,
196
+ input: [{ type: "message", role: "user", content: [{ type: "input_text", text: PROBE_PROMPT }] }],
197
+ tools: [],
198
+ reasoning: { effort: "low", summary: "auto" },
199
+ sessionId: randomUUID()
200
+ }, { ...ctx, onTextDelta: (t) => streamed += t });
201
+ o.out.log(`\u6A21\u578B\uFF1A${model}`);
202
+ if (r.status !== "completed") {
203
+ o.out.error(`\u63A2\u9488\u5931\u8D25\uFF1A\u54CD\u5E94\u4E0D\u5B8C\u6574\uFF08${r.incompleteReason ?? "incomplete"}\uFF09`);
204
+ return 1;
205
+ }
206
+ o.out.log(`\u7ED3\u679C\uFF1A${(outputText(r.items) || streamed).trim()}`);
207
+ return 0;
208
+ } catch (e) {
209
+ if (e instanceof ReloginRequiredError) {
210
+ o.out.error(e.message);
211
+ return 1;
212
+ }
213
+ o.out.error(`\u63A2\u9488\u5931\u8D25\uFF1A${errMessage(e)}`);
214
+ return 1;
215
+ }
216
+ }
217
+ async function listConfiguredModels(o) {
218
+ let config;
219
+ try {
220
+ config = loadConfig(o.nemaHome);
221
+ } catch {
222
+ throw new ModelsListError("list_failed");
223
+ }
224
+ if (config.adapter === "claude-code")
225
+ throw new ModelsListError("not_applicable");
226
+ const signal = o.signal ? AbortSignal.any([o.signal, AbortSignal.timeout(2e4)]) : AbortSignal.timeout(2e4);
227
+ let source;
228
+ let profile;
229
+ try {
230
+ if (config.authMode === "compat") {
231
+ source = new CodexFileTokenSource(o.codexHome ?? defaultCodexHome());
232
+ await source.getAccessToken({ signal });
233
+ profile = COMPAT_PROFILE;
234
+ } else if (config.authMode === "byok") {
235
+ source = new ByokTokenSource(o.nemaHome);
236
+ await source.getAccessToken({ signal });
237
+ profile = { ...BYOK_PROFILE, baseUrl: config.byokBaseUrl ?? BYOK_PROFILE.baseUrl };
238
+ } else {
239
+ const store = new AuthStore(o.nemaHome, o.refresher ?? siwcRefresher);
240
+ const { auth } = store.inspect();
241
+ if (!auth || auth.tokens.refresh_token === "" || auth.mode !== "siwc")
242
+ throw new ModelsListError("not_connected");
243
+ await store.getAccessToken({ signal });
244
+ source = store;
245
+ profile = SIWC_PROFILE;
246
+ }
247
+ } catch (error) {
248
+ if (error instanceof ModelsListError)
249
+ throw error;
250
+ throw new ModelsListError("not_connected");
251
+ }
252
+ try {
253
+ return await listModels({ auth: source, profile, version: cliVersion(), signal });
254
+ } catch {
255
+ throw new ModelsListError("list_failed");
256
+ }
257
+ }
258
+ export {
259
+ ModelsListError,
260
+ authByok,
261
+ authLogin,
262
+ authLogout,
263
+ authProbe,
264
+ authStatus,
265
+ consoleOut,
266
+ defaultOpenBrowser,
267
+ listConfiguredModels
268
+ };
@@ -0,0 +1,120 @@
1
+ import { type LockOptions } from "./lock.js";
2
+ export declare const REFRESH_SKEW_MS = 120000;
3
+ export declare const TERMINAL_CODES: readonly ["invalid_grant", "invalid_client", "invalid_token", "refresh_token_reused", "refresh_token_expired", "refresh_token_invalidated"];
4
+ export declare const RELOGIN_MESSAGE = "\u76F4\u8FDE\u767B\u5F55\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C nema auth login";
5
+ /** The file exists but cannot be trusted or parsed (wrong permissions, symlink, not ours, corrupt). */
6
+ export declare const UNUSABLE_FILE_MESSAGE = "\u51ED\u8BC1\u6587\u4EF6\u4E0D\u53EF\u7528\uFF08\u6743\u9650/\u7B26\u53F7\u94FE\u63A5/\u635F\u574F\uFF09\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C nema auth login";
7
+ export type StoredAuth = {
8
+ version: 1;
9
+ mode: "siwc" | "compat" | "byok";
10
+ clientId: string;
11
+ tokens: {
12
+ access_token: string;
13
+ refresh_token: string;
14
+ id_token?: string;
15
+ };
16
+ accountId?: string;
17
+ lastRefresh: string;
18
+ lastAuthError?: {
19
+ code: string;
20
+ message: string;
21
+ at: string;
22
+ };
23
+ };
24
+ export type RefreshResult = {
25
+ access_token: string;
26
+ refresh_token?: string;
27
+ id_token?: string;
28
+ };
29
+ /**
30
+ * Exchanges `auth.tokens.refresh_token`; throws RefreshError on an HTTP error response. `signal` aborts when
31
+ * AuthStore gives up waiting (refresh deadline), so the refresher should pass it to its HTTP request.
32
+ */
33
+ export type Refresher = (auth: StoredAuth, signal?: AbortSignal) => Promise<RefreshResult>;
34
+ export declare const REFRESH_TIMEOUT_MS = 20000;
35
+ export type AuthStoreOptions = {
36
+ refreshTimeoutMs?: number;
37
+ lock?: LockOptions;
38
+ };
39
+ type StatLike = {
40
+ isFile(): boolean;
41
+ mode: number;
42
+ uid: number;
43
+ };
44
+ /** A credential file is trusted only if it is a regular file, mode 0600, and (where uids exist) owned by us. */
45
+ export declare function isTrustedAuthStat(st: StatLike, uid?: number | null, platform?: NodeJS.Platform): boolean;
46
+ export declare class RefreshError extends Error {
47
+ readonly status: number;
48
+ readonly code?: string;
49
+ readonly retryAfterS?: number;
50
+ constructor(status: number, opts?: {
51
+ code?: string;
52
+ retryAfterS?: number;
53
+ message?: string;
54
+ });
55
+ }
56
+ export declare class ReloginRequiredError extends Error {
57
+ readonly code?: string | undefined;
58
+ constructor(code?: string | undefined, options?: ErrorOptions);
59
+ }
60
+ export declare class AuthRateLimitedError extends Error {
61
+ readonly retryAfterS?: number | undefined;
62
+ constructor(retryAfterS?: number | undefined, options?: ErrorOptions);
63
+ }
64
+ export declare class TransientAuthError extends Error {
65
+ constructor(message: string, options?: ErrorOptions);
66
+ }
67
+ type AuthResult = {
68
+ accessToken: string;
69
+ auth: StoredAuth;
70
+ };
71
+ export declare class AuthStore {
72
+ private readonly nemaHome;
73
+ private readonly refresher;
74
+ private readonly now;
75
+ private readonly opts;
76
+ private readonly file;
77
+ private readonly lockPath;
78
+ constructor(nemaHome: string, refresher: Refresher, now?: () => number, opts?: AuthStoreOptions);
79
+ /** The stored login, or null if missing, a symlink, not a regular file, not 0600, not ours, or malformed. */
80
+ read(): StoredAuth | null;
81
+ /** Like read(), but says why there is no login: "missing" (no file) or "unusable" (present but untrusted/corrupt). */
82
+ inspect(): {
83
+ auth: StoredAuth | null;
84
+ reason?: "missing" | "unusable";
85
+ };
86
+ /**
87
+ * Records an auth problem in the file WITHOUT clearing the tokens (used when the model API rejects a token
88
+ * that refreshing could not fix). Best effort: only if the file still holds the same refresh token.
89
+ */
90
+ recordAuthError(reason: {
91
+ code: string;
92
+ message: string;
93
+ }, forAuth: StoredAuth): Promise<void>;
94
+ /** Atomic write (exclusive 0600 temp file + fsync + rename) under a 0700 directory. */
95
+ save(auth: StoredAuth): void;
96
+ /** Without a reason: log out (delete the file). With a reason: drop the tokens and record lastAuthError. */
97
+ clear(reason?: {
98
+ code: string;
99
+ message: string;
100
+ }): void;
101
+ /**
102
+ * A valid access token, refreshing under auth.lock when it is within 120 s of expiry or when forced.
103
+ * Pass the token the server just rejected as `rejectedAccessToken` with `force`: if a peer has already
104
+ * replaced it on disk, that token is returned without a refresh POST.
105
+ */
106
+ getAccessToken(opts?: {
107
+ force?: boolean;
108
+ rejectedAccessToken?: string;
109
+ signal?: AbortSignal;
110
+ }): Promise<AuthResult>;
111
+ private expiring;
112
+ private usableWithoutRefresh;
113
+ private cleared;
114
+ private refreshLocked;
115
+ private callRefresher;
116
+ private refreshWith;
117
+ private onRefreshFailure;
118
+ private overwriteInPlace;
119
+ }
120
+ export {};