@modelprofile.com/authswitch 6.3.0 → 6.5.0

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 (43) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/authority-contract.d.ts +142 -0
  3. package/dist_ts/authority-contract.js +3 -0
  4. package/dist_ts/authority-runtime-contract.d.ts +34 -0
  5. package/dist_ts/authority-runtime-contract.js +2 -0
  6. package/dist_ts/classes.authoritybroker.d.ts +76 -0
  7. package/dist_ts/classes.authoritybroker.js +570 -0
  8. package/dist_ts/classes.authorityclient.d.ts +47 -0
  9. package/dist_ts/classes.authorityclient.js +261 -0
  10. package/dist_ts/classes.authoritydaemon.d.ts +46 -0
  11. package/dist_ts/classes.authoritydaemon.js +492 -0
  12. package/dist_ts/classes.authoritydatabase.d.ts +103 -0
  13. package/dist_ts/classes.authoritydatabase.js +835 -0
  14. package/dist_ts/classes.authorityframing.d.ts +8 -0
  15. package/dist_ts/classes.authorityframing.js +22 -0
  16. package/dist_ts/classes.authoritymodels.d.ts +239 -0
  17. package/dist_ts/classes.authoritymodels.js +893 -0
  18. package/dist_ts/classes.authoritysecrets.d.ts +9 -0
  19. package/dist_ts/classes.authoritysecrets.js +30 -0
  20. package/dist_ts/classes.authorityservice.d.ts +28 -0
  21. package/dist_ts/classes.authorityservice.js +71 -0
  22. package/dist_ts/classes.codexmanaged.d.ts +80 -0
  23. package/dist_ts/classes.codexmanaged.js +343 -0
  24. package/dist_ts/index.d.ts +3 -0
  25. package/dist_ts/index.js +27 -1
  26. package/dist_ts/plugins.d.ts +11 -3
  27. package/dist_ts/plugins.js +12 -4
  28. package/package.json +25 -4
  29. package/readme.md +59 -0
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts/authority-contract.ts +109 -0
  32. package/ts/authority-runtime-contract.ts +33 -0
  33. package/ts/classes.authoritybroker.ts +552 -0
  34. package/ts/classes.authorityclient.ts +252 -0
  35. package/ts/classes.authoritydaemon.ts +403 -0
  36. package/ts/classes.authoritydatabase.ts +818 -0
  37. package/ts/classes.authorityframing.ts +21 -0
  38. package/ts/classes.authoritymodels.ts +407 -0
  39. package/ts/classes.authoritysecrets.ts +27 -0
  40. package/ts/classes.authorityservice.ts +83 -0
  41. package/ts/classes.codexmanaged.ts +339 -0
  42. package/ts/index.ts +26 -0
  43. package/ts/plugins.ts +11 -3
@@ -0,0 +1,339 @@
1
+ import * as plugins from './plugins.js';
2
+ import { commitinfo } from './00_commitinfo_data.js';
3
+ import type { AuthSwitchAuthorityBroker } from './classes.authoritybroker.js';
4
+
5
+ export interface IAuthSwitchManagedCodexOptions {
6
+ /** Private runtime root; only the disposable control socket lives here. */
7
+ runtimeDirectory: string;
8
+ /** Caller-owned, durable native Codex state home, reserved to one account and scope by SmartData. */
9
+ codexHomeDirectory: string;
10
+ workspaceDirectory: string;
11
+ executable?: string;
12
+ /** The daemon is the sole owner of this binding and its capability. */
13
+ broker: AuthSwitchAuthorityBroker;
14
+ bindingId: string;
15
+ capability: string;
16
+ onNotification?: (notification: plugins.crossharness.ICodexAppServerNotification) => void;
17
+ onServerRequest?: (request: plugins.crossharness.ICodexAppServerRequest,
18
+ reply: { respond: (result: unknown) => boolean; reject: (code: number, message: string) => boolean }) => void;
19
+ onUnavailable?: () => void;
20
+ onPreparedRun: (socketPath: string) => Promise<void>;
21
+ onChildSpawned: (pid: number, startedAt: string) => Promise<void>;
22
+ }
23
+
24
+ export interface IAuthSwitchCodexInteractiveCommand {
25
+ executable: string;
26
+ args: string[];
27
+ cwd: string;
28
+ env: NodeJS.ProcessEnv;
29
+ }
30
+
31
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
32
+ value !== null && typeof value === 'object' && !Array.isArray(value);
33
+ const isAlive = (child: plugins.childProcess.ChildProcess): boolean => child.exitCode === null && child.signalCode === null;
34
+ const wait = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));
35
+
36
+ /** Same-user process inspection: a unique private --listen socket is the pre-PID crash identity. */
37
+ export const findManagedCodexProcess = async (socketPath: string): Promise<{ pid: number; startedAt: string } | null> => {
38
+ if (process.platform === 'win32') throw new Error('Managed Codex Unix socket process inspection is unavailable on Windows.');
39
+ const output = await new Promise<string>((resolve, reject) => {
40
+ plugins.childProcess.execFile('ps', ['-ww', '-u', String(process.getuid?.()), '-o', 'pid=,lstart=,args='],
41
+ { timeout: 5_000, maxBuffer: 8 * 1024 * 1024, env: { ...process.env, LC_ALL: 'C' } },
42
+ (error, stdout) => error ? reject(new Error('Managed Codex process inspection failed.')) : resolve(stdout));
43
+ });
44
+ const marker = `app-server --listen unix://${socketPath}`;
45
+ const matches: Array<{ pid: number; startedAt: string }> = [];
46
+ for (const line of output.split('\n')) {
47
+ const parts = line.trim().split(/\s+/);
48
+ if (parts.length < 8) continue;
49
+ const pid = Number(parts[0]);
50
+ const date = Date.parse(parts.slice(1, 6).join(' '));
51
+ const args = parts.slice(6).join(' ');
52
+ if (Number.isSafeInteger(pid) && pid > 0 && Number.isFinite(date) && args.includes(marker)) {
53
+ matches.push({ pid, startedAt: new Date(date).toISOString() });
54
+ }
55
+ }
56
+ if (matches.length > 1) throw new Error('Multiple managed Codex processes claim one private socket.');
57
+ return matches[0] ?? null;
58
+ };
59
+
60
+ /** Daemon-owned Codex app-server. Only access tokens, never refresh grants, enter Codex. */
61
+ export class AuthSwitchManagedCodex {
62
+ private child?: plugins.childProcess.ChildProcess;
63
+ private childExited?: Promise<void>;
64
+ private client?: plugins.crossharness.CodexAppServerClient;
65
+ private runDirectory?: string;
66
+ private homeDirectory?: string;
67
+ private socketPath?: string;
68
+ private workspaceId?: string;
69
+ private grantedGeneration?: number;
70
+ private startPromise?: Promise<void>;
71
+ private closePromise?: Promise<void>;
72
+ private closed = false;
73
+ private ready = false;
74
+
75
+ constructor(private readonly options: IAuthSwitchManagedCodexOptions) {
76
+ if (!plugins.path.isAbsolute(options.runtimeDirectory) || !plugins.path.isAbsolute(options.workspaceDirectory)
77
+ || !plugins.path.isAbsolute(options.codexHomeDirectory)) {
78
+ throw new Error('Managed Codex requires absolute runtime, home and workspace directories.');
79
+ }
80
+ }
81
+
82
+ public get isReady(): boolean { return this.ready && this.client?.state === 'connected' && !this.closed; }
83
+
84
+ private environment(): NodeJS.ProcessEnv {
85
+ const picked: NodeJS.ProcessEnv = {};
86
+ for (const key of ['PATH', 'HOME', 'USER', 'LOGNAME', 'LANG', 'LC_ALL', 'TERM', 'TMPDIR', 'XDG_RUNTIME_DIR']) {
87
+ if (process.env[key] !== undefined) picked[key] = process.env[key];
88
+ }
89
+ picked.CODEX_HOME = this.homeDirectory;
90
+ return picked;
91
+ }
92
+
93
+ private async requireVersion(): Promise<void> {
94
+ const executable = this.options.executable ?? 'codex';
95
+ const output = await new Promise<string>((resolve, reject) => {
96
+ plugins.childProcess.execFile(executable, ['--version'], { timeout: 5_000, maxBuffer: 4096,
97
+ windowsHide: true, env: this.environment() }, (error, stdout) => {
98
+ if (error) reject(new Error('Managed Codex version probe failed.'));
99
+ else resolve(stdout.trim());
100
+ });
101
+ });
102
+ if (output !== 'codex-cli 0.155.1') throw new Error('Managed Codex requires verified codex-cli 0.155.1.');
103
+ }
104
+
105
+ public start(): Promise<void> {
106
+ if (this.closed || this.startPromise) throw new Error('Managed Codex can start only once.');
107
+ this.startPromise = this.performStart();
108
+ return this.startPromise;
109
+ }
110
+
111
+ private async performStart(): Promise<void> {
112
+ try {
113
+ const root = this.options.runtimeDirectory;
114
+ await plugins.fs.promises.mkdir(root, { recursive: true, mode: 0o700 });
115
+ const rootStat = await plugins.fs.promises.lstat(root);
116
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || rootStat.uid !== process.getuid?.()) {
117
+ throw new Error('Managed Codex runtime directory is not private.');
118
+ }
119
+ await plugins.fs.promises.chmod(root, 0o700);
120
+ this.runDirectory = await plugins.fs.promises.mkdtemp(plugins.path.join(root, 'codex-'));
121
+ await plugins.fs.promises.chmod(this.runDirectory, 0o700);
122
+ this.homeDirectory = this.options.codexHomeDirectory;
123
+ this.socketPath = plugins.path.join(this.runDirectory, 'app.sock');
124
+ if (Buffer.byteLength(this.socketPath, 'utf8') > 100) throw new Error('Managed Codex socket path is too long.');
125
+ const homeStat = await plugins.fs.promises.lstat(this.homeDirectory);
126
+ if (!homeStat.isDirectory() || homeStat.isSymbolicLink() || homeStat.uid !== process.getuid?.()
127
+ || (homeStat.mode & 0o077) !== 0 || await plugins.fs.promises.realpath(this.homeDirectory) !== this.homeDirectory) {
128
+ throw new Error('Managed Codex home is not a private owned directory.');
129
+ }
130
+ try { await plugins.fs.promises.lstat(plugins.path.join(this.homeDirectory, 'auth.json'));
131
+ throw new Error('Managed Codex home contains a native credential file.');
132
+ } catch (error) {
133
+ if (!isRecord(error) || error.code !== 'ENOENT') throw error;
134
+ }
135
+ await this.requireVersion();
136
+ if (this.closed) throw new Error('Managed Codex startup was cancelled.');
137
+ const initial = await this.options.broker.resolveAccess(this.options.bindingId,
138
+ this.options.capability, 60_000);
139
+ if (this.closed) throw new Error('Managed Codex startup was cancelled.');
140
+ this.workspaceId = initial.accountId;
141
+ this.grantedGeneration = initial.grantGeneration;
142
+ await this.options.onPreparedRun(this.socketPath);
143
+ if (this.closed) throw new Error('Managed Codex startup was cancelled.');
144
+ const child = plugins.childProcess.spawn(this.options.executable ?? 'codex', [
145
+ 'app-server', '--listen', `unix://${this.socketPath}`, '--strict-config',
146
+ '-c', 'cli_auth_credentials_store="ephemeral"',
147
+ ], { cwd: this.options.workspaceDirectory, env: this.environment(), shell: false,
148
+ detached: process.platform !== 'win32', stdio: 'ignore', windowsHide: true });
149
+ this.child = child;
150
+ this.childExited = new Promise(resolve => {
151
+ child.once('error', () => resolve());
152
+ child.once('exit', () => resolve());
153
+ });
154
+ if (!child.pid) throw new Error('Managed Codex app-server process has no PID.');
155
+ let observed: Awaited<ReturnType<typeof findManagedCodexProcess>> = null;
156
+ for (let attempt = 0; attempt < 20 && !observed; attempt++) {
157
+ observed = await findManagedCodexProcess(this.socketPath);
158
+ if (!observed) await wait(25);
159
+ }
160
+ if (!observed || observed.pid !== child.pid) throw new Error('Managed Codex app-server process identity could not be verified.');
161
+ await this.options.onChildSpawned(observed.pid, observed.startedAt);
162
+ const deadline = Date.now() + 10_000;
163
+ while (true) {
164
+ if (this.closed) throw new Error('Managed Codex startup was cancelled.');
165
+ if (!isAlive(child)) throw new Error('Managed Codex app-server exited before accepting connections.');
166
+ try {
167
+ if ((await plugins.fs.promises.lstat(this.socketPath)).isSocket()) break;
168
+ } catch (error) {
169
+ if (!isRecord(error) || error.code !== 'ENOENT') throw error;
170
+ }
171
+ if (Date.now() >= deadline) throw new Error('Managed Codex app-server socket startup timed out.');
172
+ await wait(25);
173
+ }
174
+ const client = new plugins.crossharness.CodexAppServerClient({
175
+ transport: { type: 'unix', socketPath: this.socketPath },
176
+ clientInfo: { name: 'authswitch', title: 'Authswitch', version: commitinfo.version },
177
+ experimentalApi: true, handshakeTimeoutMs: 5_000, initializeTimeoutMs: 5_000,
178
+ onNotification: notification => this.options.onNotification?.(notification),
179
+ onServerRequest: request => { void this.handleServerRequest(request); },
180
+ onClose: () => {
181
+ const wasReady = this.ready;
182
+ this.ready = false;
183
+ if (wasReady && !this.closed) this.options.onUnavailable?.();
184
+ },
185
+ });
186
+ this.client = client;
187
+ await client.connect();
188
+ if (client.serverVersion !== '0.155.1') throw new Error('Managed Codex app-server version changed.');
189
+ const login: unknown = await client.request('account/login/start', {
190
+ type: 'chatgptAuthTokens', accessToken: initial.accessToken, chatgptAccountId: initial.accountId,
191
+ }, 5_000);
192
+ if (!isRecord(login) || login.type !== 'chatgptAuthTokens') {
193
+ throw new Error('Managed Codex external-token login was not acknowledged.');
194
+ }
195
+ if (this.closed) throw new Error('Managed Codex startup was cancelled.');
196
+ this.ready = true;
197
+ } catch (error) {
198
+ try { await this.close(); } catch (cleanupError) {
199
+ throw new AggregateError([error, cleanupError], 'Managed Codex startup and cleanup failed.');
200
+ }
201
+ throw error;
202
+ }
203
+ }
204
+
205
+ private async handleServerRequest(request: plugins.crossharness.ICodexAppServerRequest): Promise<void> {
206
+ const client = this.client;
207
+ if (!client || this.closed || !this.ready) return;
208
+ if (request.method !== 'account/chatgptAuthTokens/refresh') {
209
+ if (!this.options.onServerRequest) {
210
+ client.rejectRequest(request.id, -32601, 'Managed Codex request requires an owner handler.');
211
+ return;
212
+ }
213
+ try {
214
+ this.options.onServerRequest(request, {
215
+ respond: result => client.respond(request.id, result),
216
+ reject: (code, message) => client.rejectRequest(request.id, code, message),
217
+ });
218
+ } catch {
219
+ client.rejectRequest(request.id, -32603, 'Managed Codex owner request failed.');
220
+ }
221
+ return;
222
+ }
223
+ const prior = this.grantedGeneration;
224
+ const workspaceId = this.workspaceId;
225
+ const params = request.params;
226
+ if (params.reason !== 'unauthorized' || !workspaceId || prior === undefined
227
+ || (params.previousAccountId !== undefined && params.previousAccountId !== null
228
+ && params.previousAccountId !== workspaceId)) {
229
+ client.rejectRequest(request.id, -32602, 'Managed Codex account identity is invalid.');
230
+ return;
231
+ }
232
+ try {
233
+ const refreshed = await this.options.broker.resolveAccess(this.options.bindingId,
234
+ this.options.capability, 60_000, prior);
235
+ if (this.closed || request.signal.aborted) return;
236
+ if (refreshed.accountId !== workspaceId || refreshed.grantGeneration <= prior) {
237
+ client.rejectRequest(request.id, -32603, 'Managed Codex account refresh did not advance.');
238
+ return;
239
+ }
240
+ this.grantedGeneration = refreshed.grantGeneration;
241
+ client.respond(request.id, { accessToken: refreshed.accessToken,
242
+ chatgptAccountId: refreshed.accountId, chatgptPlanType: null });
243
+ } catch {
244
+ if (!request.signal.aborted) client.rejectRequest(request.id, -32603, 'Managed Codex account needs reauthentication.');
245
+ }
246
+ }
247
+
248
+ /** The interactive CLI attaches to the owned app-server; the daemon remains its auth control peer. */
249
+ public interactiveCommand(): IAuthSwitchCodexInteractiveCommand {
250
+ if (!this.isReady || !this.socketPath) throw new Error('Managed Codex is unavailable.');
251
+ return { executable: this.options.executable ?? 'codex', args: ['--remote', `unix://${this.socketPath}`],
252
+ cwd: this.options.workspaceDirectory, env: this.environment() };
253
+ }
254
+
255
+ /** Native app-server thread/turn API for noninteractive consumers; notifications go to onNotification. */
256
+ public async startThread(options: { approvalPolicy: 'never' | 'on-request'; model?: string;
257
+ signal?: AbortSignal }): Promise<string> {
258
+ if (!this.isReady || !this.client || !options || !['never', 'on-request'].includes(options.approvalPolicy)
259
+ || (options.model !== undefined && (typeof options.model !== 'string' || options.model.length > 128))) {
260
+ throw new Error('Managed Codex thread request is unavailable or invalid.');
261
+ }
262
+ const started: unknown = await this.client.request('thread/start', {
263
+ cwd: this.options.workspaceDirectory, approvalPolicy: options.approvalPolicy,
264
+ ...(options.model ? { model: options.model } : {}),
265
+ }, 30_000, options.signal);
266
+ if (!isRecord(started) || !isRecord(started.thread) || typeof started.thread.id !== 'string') {
267
+ throw new Error('Managed Codex returned an invalid thread.');
268
+ }
269
+ return started.thread.id;
270
+ }
271
+
272
+ public async resumeThread(threadId: string, options: { approvalPolicy: 'never' | 'on-request';
273
+ model?: string; signal?: AbortSignal }): Promise<void> {
274
+ if (!this.isReady || !this.client || typeof threadId !== 'string' || !threadId || threadId.length > 256
275
+ || !options || !['never', 'on-request'].includes(options.approvalPolicy)
276
+ || (options.model !== undefined && (typeof options.model !== 'string' || options.model.length > 128))) {
277
+ throw new Error('Managed Codex resume request is unavailable or invalid.');
278
+ }
279
+ const resumed: unknown = await this.client.request('thread/resume', { threadId,
280
+ cwd: this.options.workspaceDirectory, approvalPolicy: options.approvalPolicy,
281
+ ...(options.model ? { model: options.model } : {}),
282
+ }, 30_000, options.signal);
283
+ if (!isRecord(resumed) || !isRecord(resumed.thread) || resumed.thread.id !== threadId) {
284
+ throw new Error('Managed Codex could not resume the requested thread from its assigned home.');
285
+ }
286
+ }
287
+
288
+ public async startTextTurn(text: string, options: { approvalPolicy: 'never' | 'on-request';
289
+ model?: string; threadId?: string; signal?: AbortSignal }): Promise<{ threadId: string; turnId: string }> {
290
+ if (!this.isReady || !this.client || typeof text !== 'string' || !text.trim() || text.length > 1_000_000
291
+ || !options || !['never', 'on-request'].includes(options.approvalPolicy)
292
+ || (options.model !== undefined && (typeof options.model !== 'string' || options.model.length > 128))
293
+ || (options.threadId !== undefined && (typeof options.threadId !== 'string' || options.threadId.length > 256))) {
294
+ throw new Error('Managed Codex turn request is unavailable or invalid.');
295
+ }
296
+ let threadId = options.threadId;
297
+ if (threadId) {
298
+ await this.resumeThread(threadId, options);
299
+ } else {
300
+ threadId = await this.startThread(options);
301
+ }
302
+ const turn: unknown = await this.client.request('turn/start', { threadId,
303
+ input: [{ type: 'text', text, text_elements: [] }],
304
+ approvalPolicy: options.approvalPolicy,
305
+ ...(options.model ? { model: options.model } : {}),
306
+ }, 30_000, options.signal);
307
+ if (!isRecord(turn) || !isRecord(turn.turn) || typeof turn.turn.id !== 'string') {
308
+ throw new Error('Managed Codex returned an invalid turn.');
309
+ }
310
+ return { threadId, turnId: turn.turn.id };
311
+ }
312
+
313
+ public close(): Promise<void> {
314
+ if (this.closePromise) return this.closePromise;
315
+ const wasReady = this.ready;
316
+ this.closed = true;
317
+ this.ready = false;
318
+ this.closePromise = this.performClose();
319
+ if (wasReady) this.options.onUnavailable?.();
320
+ return this.closePromise;
321
+ }
322
+
323
+ private async performClose(): Promise<void> {
324
+ this.client?.close('Managed Codex stopped.');
325
+ const child = this.child;
326
+ if (child && isAlive(child) && child.pid) {
327
+ try { process.kill(process.platform === 'win32' ? child.pid : -child.pid, 'SIGTERM'); }
328
+ catch (error) { if (!isRecord(error) || error.code !== 'ESRCH') throw error; }
329
+ await Promise.race([this.childExited, wait(3_000)]);
330
+ if (isAlive(child)) {
331
+ try { process.kill(process.platform === 'win32' ? child.pid : -child.pid, 'SIGKILL'); }
332
+ catch (error) { if (!isRecord(error) || error.code !== 'ESRCH') throw error; }
333
+ await Promise.race([this.childExited, wait(3_000)]);
334
+ }
335
+ if (isAlive(child)) throw new Error('Managed Codex process did not stop.');
336
+ }
337
+ if (this.runDirectory) await plugins.fs.promises.rm(this.runDirectory, { recursive: true, force: true });
338
+ }
339
+ }
package/ts/index.ts CHANGED
@@ -23,10 +23,36 @@ export * from './classes.service.js';
23
23
  export * from './classes.watch.js';
24
24
  export * from './classes.watchlock.js';
25
25
  export * from './watchpolicy.js';
26
+ export * from './classes.authorityclient.js';
27
+ export * from './classes.authoritydaemon.js';
28
+ export * from './classes.authorityservice.js';
26
29
 
27
30
  import { AuthSwitchCli } from './classes.cli.js';
31
+ import { AuthSwitchAuthorityService, resolveAuthSwitchAuthorityPaths, runAuthSwitchAuthorityDaemon } from './classes.authorityservice.js';
28
32
 
29
33
  export const runCli = async (argvArg: string[] = process.argv.slice(2)): Promise<void> => {
34
+ if (argvArg[0] === 'authority') {
35
+ const paths = resolveAuthSwitchAuthorityPaths();
36
+ if (argvArg[1] === 'daemon' && argvArg.length === 2) {
37
+ await runAuthSwitchAuthorityDaemon(paths);
38
+ return;
39
+ }
40
+ if (argvArg[1] === 'service' && argvArg.length === 3) {
41
+ const service = new AuthSwitchAuthorityService(paths.runtimeDirectory);
42
+ const action = argvArg[2];
43
+ const result = action === 'install' ? await service.install()
44
+ : action === 'status' ? await service.inspect()
45
+ : action === 'enable' ? await service.enable()
46
+ : action === 'start' ? await service.start()
47
+ : action === 'stop' ? await service.stop()
48
+ : null;
49
+ if (result !== null) {
50
+ process.stdout.write(JSON.stringify(result) + '\n');
51
+ return;
52
+ }
53
+ }
54
+ throw new Error('Usage: authswitch authority daemon | service install|status|enable|start|stop');
55
+ }
30
56
  const exitCode = await new AuthSwitchCli().run(argvArg);
31
57
  process.exitCode = exitCode;
32
58
  };
package/ts/plugins.ts CHANGED
@@ -4,10 +4,12 @@ import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import * as childProcess from 'node:child_process';
6
6
  import * as crypto from 'node:crypto';
7
+ import * as net from 'node:net';
7
8
  import { createRequire } from 'node:module';
9
+ import { fileURLToPath } from 'node:url';
8
10
  import type { DatabaseSync } from 'node:sqlite';
9
11
 
10
- export { childProcess, crypto, fs, os, path };
12
+ export { childProcess, crypto, fileURLToPath, fs, net, os, path };
11
13
  export type { DatabaseSync };
12
14
  const nativeRequire = createRequire(import.meta.url);
13
15
  /** SQLite is needed only when accessing a Codex state database, not when hosting account APIs. */
@@ -24,11 +26,17 @@ export const loadProperLockfile = (): Promise<TProperLockfile> => import('proper
24
26
 
25
27
  // @push.rocks modules
26
28
  import * as smartconsole from '@push.rocks/smartconsole';
27
- export { smartconsole };
29
+ import * as smartdata from '@push.rocks/smartdata';
30
+ import * as smartdb from '@push.rocks/smartdb';
31
+ import * as smartsecret from '@push.rocks/smartsecret';
32
+ import * as smartdaemon from '@push.rocks/smartdaemon';
33
+ import * as typedrequest from '@api.global/typedrequest';
34
+ export { smartconsole, smartdata, smartdb, smartsecret, smartdaemon, typedrequest };
28
35
 
29
36
  // @modelprofile.com modules
30
37
  import * as flexModels from '@modelprofile.com/flexharness-models';
31
38
  import * as flexOpenAi from '@modelprofile.com/flexharness-providers/openai';
32
39
  import * as flexAuth from '@modelprofile.com/flexharness-providers/auth';
33
40
  import * as flexAccounts from '@modelprofile.com/flexharness-providers/accounts';
34
- export { flexModels, flexOpenAi, flexAuth, flexAccounts };
41
+ import * as crossharness from '@modelprofile.com/mcp-crossharness';
42
+ export { flexModels, flexOpenAi, flexAuth, flexAccounts, crossharness };