@push.rocks/smartai 2.3.0 → 4.0.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +3 -3
- package/dist_ts/index.js +2 -2
- package/dist_ts/smartai.auth.openai.d.ts +15 -15
- package/dist_ts/smartai.auth.openai.js +52 -46
- package/dist_ts/smartai.classes.smartai.js +12 -5
- package/dist_ts/smartai.interfaces.d.ts +10 -11
- package/dist_ts/smartai.middleware.openai.d.ts +6 -0
- package/dist_ts/smartai.middleware.openai.js +40 -0
- package/dist_ts_openai_chatgpt_auth/index.d.ts +43 -0
- package/dist_ts_openai_chatgpt_auth/index.js +249 -0
- package/package.json +6 -1
- package/readme.hints.md +2 -1
- package/readme.md +39 -8
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +21 -21
- package/ts/smartai.auth.openai.ts +85 -76
- package/ts/smartai.classes.smartai.ts +11 -4
- package/ts/smartai.interfaces.ts +10 -11
- package/ts/smartai.middleware.openai.ts +46 -0
- package/ts_openai_chatgpt_auth/index.ts +351 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { parseOpenAiChatGptTokenInfo, refreshOpenAiChatGptTokenData, } from '../dist_ts/smartai.auth.openai.js';
|
|
5
|
+
const defaultSources = ['smartai', 'opencode', 'codex'];
|
|
6
|
+
const refreshWindowMs = 5 * 60 * 1000;
|
|
7
|
+
export const getDefaultOpenAiChatGptAuthPath = (source, homeDir = os.homedir()) => {
|
|
8
|
+
switch (source) {
|
|
9
|
+
case 'smartai':
|
|
10
|
+
return path.join(homeDir, '.git.zone', 'ide', 'openai-chatgpt-auth.json');
|
|
11
|
+
case 'opencode':
|
|
12
|
+
return path.join(homeDir, '.local', 'share', 'opencode', 'auth.json');
|
|
13
|
+
case 'codex':
|
|
14
|
+
return path.join(homeDir, '.codex', 'auth.json');
|
|
15
|
+
default:
|
|
16
|
+
throw new Error(`Unsupported OpenAI ChatGPT auth source: ${source}`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const normalizeOpenAiChatGptAuth = (input, format = 'auto') => {
|
|
20
|
+
if (format === 'auto') {
|
|
21
|
+
return normalizeOpenAiChatGptAuth(input, 'smartai')
|
|
22
|
+
?? normalizeOpenAiChatGptAuth(input, 'opencode')
|
|
23
|
+
?? normalizeOpenAiChatGptAuth(input, 'codex');
|
|
24
|
+
}
|
|
25
|
+
if (!isRecord(input))
|
|
26
|
+
return undefined;
|
|
27
|
+
if (format === 'opencode') {
|
|
28
|
+
return normalizeOpenCodeAuth(input);
|
|
29
|
+
}
|
|
30
|
+
return normalizeTokenObject(isRecord(input.tokens) ? input.tokens : input);
|
|
31
|
+
};
|
|
32
|
+
export const readOpenAiChatGptAuthFile = async (filePath, format = 'auto') => {
|
|
33
|
+
const parsed = JSON.parse(await fs.readFile(filePath, 'utf8'));
|
|
34
|
+
return normalizeOpenAiChatGptAuth(parsed, format);
|
|
35
|
+
};
|
|
36
|
+
export const inspectOpenAiChatGptAuthSources = async (options = {}) => {
|
|
37
|
+
const now = options.now ?? new Date();
|
|
38
|
+
const sourceConfigs = normalizeSourceConfigs(options.sources, options.homeDir);
|
|
39
|
+
return Promise.all(sourceConfigs.map(async (sourceConfig) => {
|
|
40
|
+
try {
|
|
41
|
+
const tokenData = await readOpenAiChatGptAuthFile(sourceConfig.filePath, sourceConfig.format ?? sourceConfig.source);
|
|
42
|
+
if (!tokenData) {
|
|
43
|
+
return {
|
|
44
|
+
source: sourceConfig.source,
|
|
45
|
+
filePath: sourceConfig.filePath,
|
|
46
|
+
exists: true,
|
|
47
|
+
usable: false,
|
|
48
|
+
error: 'No OpenAI ChatGPT auth token found in file.',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return toInspection(sourceConfig.source, sourceConfig.filePath, tokenData, now);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const nodeError = error;
|
|
55
|
+
return {
|
|
56
|
+
source: sourceConfig.source,
|
|
57
|
+
filePath: sourceConfig.filePath,
|
|
58
|
+
exists: nodeError.code !== 'ENOENT',
|
|
59
|
+
usable: false,
|
|
60
|
+
error: nodeError.code === 'ENOENT' ? undefined : nodeError.message,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
};
|
|
65
|
+
export const resolveOpenAiChatGptAuth = async (options = {}) => {
|
|
66
|
+
const now = options.now ?? new Date();
|
|
67
|
+
const sourceConfigs = normalizeSourceConfigs(options.sources, options.homeDir);
|
|
68
|
+
const refresh = options.refresh ?? 'ifNeeded';
|
|
69
|
+
for (const sourceConfig of sourceConfigs) {
|
|
70
|
+
let tokenData;
|
|
71
|
+
try {
|
|
72
|
+
tokenData = await readOpenAiChatGptAuthFile(sourceConfig.filePath, sourceConfig.format ?? sourceConfig.source);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const nodeError = error;
|
|
76
|
+
if (nodeError.code === 'ENOENT')
|
|
77
|
+
continue;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!tokenData)
|
|
81
|
+
continue;
|
|
82
|
+
const shouldRefresh = refresh === 'ifNeeded' && shouldRefreshToken(tokenData, now);
|
|
83
|
+
const writeBack = sourceConfig.writeBack ?? options.writeBack?.[sourceConfig.source] ?? sourceConfig.source === 'smartai';
|
|
84
|
+
if (!shouldRefresh) {
|
|
85
|
+
return { source: sourceConfig.source, filePath: sourceConfig.filePath, tokenData, refreshed: false };
|
|
86
|
+
}
|
|
87
|
+
if (!writeBack) {
|
|
88
|
+
if (!isExpired(tokenData, now)) {
|
|
89
|
+
return { source: sourceConfig.source, filePath: sourceConfig.filePath, tokenData, refreshed: false };
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const refreshed = await refreshOpenAiChatGptTokenData(tokenData, options.authOptions ?? {});
|
|
95
|
+
await writeOpenAiChatGptAuthFile(sourceConfig.filePath, refreshed, sourceConfig.format ?? sourceConfig.source);
|
|
96
|
+
return { source: sourceConfig.source, filePath: sourceConfig.filePath, tokenData: refreshed, refreshed: true };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
};
|
|
104
|
+
export const writeOpenAiChatGptAuthFile = async (filePath, tokenData, format = 'smartai') => {
|
|
105
|
+
const current = await readJsonFileIfExists(filePath);
|
|
106
|
+
const payload = format === 'opencode'
|
|
107
|
+
? toOpenCodeAuthFile(current, tokenData)
|
|
108
|
+
: format === 'codex'
|
|
109
|
+
? toCodexAuthFile(current, tokenData)
|
|
110
|
+
: toSmartAiAuthFile(tokenData);
|
|
111
|
+
await writeJsonAtomic(filePath, payload);
|
|
112
|
+
};
|
|
113
|
+
const normalizeSourceConfigs = (sources, homeDir = os.homedir()) => {
|
|
114
|
+
return (sources ?? defaultSources).map((sourceInput) => {
|
|
115
|
+
const sourceConfig = typeof sourceInput === 'string' ? { source: sourceInput } : sourceInput;
|
|
116
|
+
return {
|
|
117
|
+
source: sourceConfig.source,
|
|
118
|
+
filePath: sourceConfig.filePath ?? getDefaultOpenAiChatGptAuthPath(sourceConfig.source, homeDir),
|
|
119
|
+
format: sourceConfig.format ?? sourceConfig.source,
|
|
120
|
+
writeBack: sourceConfig.writeBack,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
const toInspection = (source, filePath, tokenData, now) => {
|
|
125
|
+
const expired = isExpired(tokenData, now);
|
|
126
|
+
return {
|
|
127
|
+
source,
|
|
128
|
+
filePath,
|
|
129
|
+
exists: true,
|
|
130
|
+
usable: !expired,
|
|
131
|
+
expired,
|
|
132
|
+
accountId: tokenData.accountId ?? tokenData.tokenInfo.chatgptAccountId,
|
|
133
|
+
email: tokenData.tokenInfo.email,
|
|
134
|
+
plan: tokenData.tokenInfo.chatgptPlanType,
|
|
135
|
+
expiresAt: tokenData.tokenInfo.expiresAt,
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
const normalizeTokenObject = (input) => {
|
|
139
|
+
const accessToken = stringValue(input.accessToken) ?? stringValue(input.access_token);
|
|
140
|
+
const refreshToken = stringValue(input.refreshToken) ?? stringValue(input.refresh_token);
|
|
141
|
+
const idToken = stringValue(input.idToken) ?? stringValue(input.id_token) ?? stringValue(input.tokenInfo?.rawJwt);
|
|
142
|
+
const accountId = stringValue(input.accountId) ?? stringValue(input.account_id);
|
|
143
|
+
return createTokenDataFromValues({ accessToken, refreshToken, idToken, accountId });
|
|
144
|
+
};
|
|
145
|
+
const normalizeOpenCodeAuth = (input) => {
|
|
146
|
+
if (!isRecord(input.openai))
|
|
147
|
+
return undefined;
|
|
148
|
+
const accessToken = stringValue(input.openai.access);
|
|
149
|
+
const refreshToken = stringValue(input.openai.refresh);
|
|
150
|
+
const accountId = stringValue(input.openai.accountId);
|
|
151
|
+
const expiresAt = typeof input.openai.expires === 'number' ? new Date(input.openai.expires).toISOString() : undefined;
|
|
152
|
+
return createTokenDataFromValues({ accessToken, refreshToken, accountId, fallbackExpiresAt: expiresAt });
|
|
153
|
+
};
|
|
154
|
+
const createTokenDataFromValues = (input) => {
|
|
155
|
+
if (!input.accessToken || !input.refreshToken)
|
|
156
|
+
return undefined;
|
|
157
|
+
const parseToken = input.idToken ?? input.accessToken;
|
|
158
|
+
const tokenInfo = parseTokenInfo(parseToken, input.accountId, input.fallbackExpiresAt);
|
|
159
|
+
return {
|
|
160
|
+
accessToken: input.accessToken,
|
|
161
|
+
refreshToken: input.refreshToken,
|
|
162
|
+
idToken: input.idToken,
|
|
163
|
+
accountId: input.accountId ?? tokenInfo.chatgptAccountId,
|
|
164
|
+
tokenInfo,
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
const parseTokenInfo = (token, accountId, fallbackExpiresAt) => {
|
|
168
|
+
try {
|
|
169
|
+
const tokenInfo = parseOpenAiChatGptTokenInfo(token);
|
|
170
|
+
return {
|
|
171
|
+
...tokenInfo,
|
|
172
|
+
chatgptAccountId: tokenInfo.chatgptAccountId ?? accountId,
|
|
173
|
+
expiresAt: tokenInfo.expiresAt ?? fallbackExpiresAt,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return {
|
|
178
|
+
chatgptAccountId: accountId,
|
|
179
|
+
chatgptAccountIsFedramp: false,
|
|
180
|
+
expiresAt: fallbackExpiresAt,
|
|
181
|
+
rawJwt: token,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const toSmartAiAuthFile = (tokenData) => ({
|
|
186
|
+
accessToken: tokenData.accessToken,
|
|
187
|
+
refreshToken: tokenData.refreshToken,
|
|
188
|
+
idToken: tokenData.idToken,
|
|
189
|
+
accountId: tokenData.accountId,
|
|
190
|
+
tokenInfo: tokenData.tokenInfo,
|
|
191
|
+
});
|
|
192
|
+
const toCodexAuthFile = (current, tokenData) => ({
|
|
193
|
+
...current,
|
|
194
|
+
OPENAI_API_KEY: current.OPENAI_API_KEY ?? null,
|
|
195
|
+
tokens: {
|
|
196
|
+
...(isRecord(current.tokens) ? current.tokens : {}),
|
|
197
|
+
id_token: tokenData.idToken,
|
|
198
|
+
access_token: tokenData.accessToken,
|
|
199
|
+
refresh_token: tokenData.refreshToken,
|
|
200
|
+
account_id: tokenData.accountId ?? tokenData.tokenInfo.chatgptAccountId,
|
|
201
|
+
},
|
|
202
|
+
last_refresh: new Date().toISOString(),
|
|
203
|
+
});
|
|
204
|
+
const toOpenCodeAuthFile = (current, tokenData) => ({
|
|
205
|
+
...current,
|
|
206
|
+
openai: {
|
|
207
|
+
...(isRecord(current.openai) ? current.openai : {}),
|
|
208
|
+
type: 'oauth',
|
|
209
|
+
refresh: tokenData.refreshToken,
|
|
210
|
+
access: tokenData.accessToken,
|
|
211
|
+
expires: tokenData.tokenInfo.expiresAt ? Date.parse(tokenData.tokenInfo.expiresAt) : undefined,
|
|
212
|
+
accountId: tokenData.accountId ?? tokenData.tokenInfo.chatgptAccountId,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
const shouldRefreshToken = (tokenData, now) => {
|
|
216
|
+
if (!tokenData.tokenInfo.expiresAt)
|
|
217
|
+
return false;
|
|
218
|
+
return Date.parse(tokenData.tokenInfo.expiresAt) - now.getTime() < refreshWindowMs;
|
|
219
|
+
};
|
|
220
|
+
const isExpired = (tokenData, now) => {
|
|
221
|
+
if (!tokenData.tokenInfo.expiresAt)
|
|
222
|
+
return false;
|
|
223
|
+
return Date.parse(tokenData.tokenInfo.expiresAt) <= now.getTime();
|
|
224
|
+
};
|
|
225
|
+
const readJsonFileIfExists = async (filePath) => {
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse(await fs.readFile(filePath, 'utf8'));
|
|
228
|
+
return isRecord(parsed) ? parsed : {};
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const nodeError = error;
|
|
232
|
+
if (nodeError.code === 'ENOENT')
|
|
233
|
+
return {};
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
const writeJsonAtomic = async (filePath, payload) => {
|
|
238
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
239
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
240
|
+
await fs.writeFile(tempPath, `${JSON.stringify(payload, undefined, 2)}\n`, { mode: 0o600 });
|
|
241
|
+
await fs.rename(tempPath, filePath);
|
|
242
|
+
};
|
|
243
|
+
const isRecord = (value) => {
|
|
244
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
245
|
+
};
|
|
246
|
+
const stringValue = (value) => {
|
|
247
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
248
|
+
};
|
|
249
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c19vcGVuYWlfY2hhdGdwdF9hdXRoL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDdkMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxFQUNMLDJCQUEyQixFQUMzQiw2QkFBNkIsR0FDOUIsTUFBTSw4QkFBOEIsQ0FBQztBQXdEdEMsTUFBTSxjQUFjLEdBQStCLENBQUMsU0FBUyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNwRixNQUFNLGVBQWUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUV0QyxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FBRyxDQUM3QyxNQUFnQyxFQUNoQyxPQUFPLEdBQUcsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUNkLEVBQUU7SUFDVixRQUFRLE1BQU0sRUFBRSxDQUFDO1FBQ2YsS0FBSyxTQUFTO1lBQ1osT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLDBCQUEwQixDQUFDLENBQUM7UUFDNUUsS0FBSyxVQUFVO1lBQ2IsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUN4RSxLQUFLLE9BQU87WUFDVixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNuRDtZQUNFLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLE1BQXNCLEVBQUUsQ0FBQyxDQUFDO0lBQ3pGLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLENBQUMsTUFBTSww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QUFRLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQzVGLE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDdEMsQ0FBQyxDQUFDO0FBRUYsTUFBTSxRQUFRLEdBQUcsQ0FBQyxLQUFjLEVBQW9DLEVBQUU7SUFDcEUsT0FBTyxDQUFDLENBQUMsS0FBSyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDdkUsQ0FBQyxDQUFDO0FBRUYsTUFBTSxXQUFXLEdBQUcsQ0FBQyxLQUFjLEVBQXNCLEVBQUU7SUFDekQsT0FBTyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQzNFLENBQUMsQ0FBQyJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartai",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Provider registry and capability utilities for ai-sdk (Vercel AI SDK). Core export returns LanguageModel; subpath exports provide vision, audio, image, document and research capabilities.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"./research": {
|
|
31
31
|
"import": "./dist_ts_research/index.js",
|
|
32
32
|
"types": "./dist_ts_research/index.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./openai-chatgpt-auth": {
|
|
35
|
+
"import": "./dist_ts_openai_chatgpt_auth/index.js",
|
|
36
|
+
"types": "./dist_ts_openai_chatgpt_auth/index.d.ts"
|
|
33
37
|
}
|
|
34
38
|
},
|
|
35
39
|
"author": "Task Venture Capital GmbH",
|
|
@@ -78,6 +82,7 @@
|
|
|
78
82
|
"ts_image/**/*",
|
|
79
83
|
"ts_document/**/*",
|
|
80
84
|
"ts_research/**/*",
|
|
85
|
+
"ts_openai_chatgpt_auth/**/*",
|
|
81
86
|
"dist_*/**/*",
|
|
82
87
|
"assets/**/*",
|
|
83
88
|
".smartconfig.json",
|
package/readme.hints.md
CHANGED
|
@@ -10,7 +10,8 @@ The package is a **provider registry** built on the Vercel AI SDK (`ai` v6). The
|
|
|
10
10
|
- Providers: anthropic, openai, google, groq, mistral, xai, perplexity, ollama
|
|
11
11
|
- Anthropic prompt caching via `wrapLanguageModel` middleware (enabled by default)
|
|
12
12
|
- Custom Ollama provider implementing `LanguageModelV3` directly (for think, num_ctx support)
|
|
13
|
-
- OpenAI ChatGPT/
|
|
13
|
+
- OpenAI ChatGPT/Codex device-code auth in `smartai.auth.openai.ts`; `openAiChatGptAuth` routes OpenAI models to the ChatGPT Codex backend
|
|
14
|
+
- Node-only local auth source helpers live in `ts_openai_chatgpt_auth/` and support SmartAI, OpenCode, and Codex auth file formats
|
|
14
15
|
|
|
15
16
|
### Subpath Exports
|
|
16
17
|
- `@push.rocks/smartai/vision` — `analyzeImage()` using `generateText` with image content
|
package/readme.md
CHANGED
|
@@ -107,29 +107,60 @@ console.log(result.text);
|
|
|
107
107
|
|
|
108
108
|
OpenAI `reasoningEffort` supports `'none'`, `'minimal'`, `'low'`, `'medium'`, `'high'`, and `'xhigh'`. Model IDs are accepted as strings, so new IDs like `'gpt-5.5'` can be used before upstream model unions are updated.
|
|
109
109
|
|
|
110
|
-
### OpenAI
|
|
110
|
+
### OpenAI ChatGPT / Codex Auth
|
|
111
111
|
|
|
112
|
-
SmartAI can request ChatGPT subscription-backed Codex credentials with OpenAI's device-code flow. The returned credentials are passed to `getModel()` through `
|
|
112
|
+
SmartAI can request ChatGPT subscription-backed Codex credentials with OpenAI's device-code flow. The returned credentials are passed to `getModel()` through `openAiChatGptAuth`; SmartAI then routes OpenAI model calls through the ChatGPT Codex backend with the required account headers.
|
|
113
113
|
|
|
114
114
|
```typescript
|
|
115
115
|
import {
|
|
116
|
-
|
|
116
|
+
completeOpenAiChatGptDeviceCodeLogin,
|
|
117
117
|
getModel,
|
|
118
|
-
|
|
118
|
+
requestOpenAiChatGptDeviceCode,
|
|
119
119
|
} from '@push.rocks/smartai';
|
|
120
120
|
|
|
121
|
-
const deviceCode = await
|
|
121
|
+
const deviceCode = await requestOpenAiChatGptDeviceCode();
|
|
122
122
|
console.log(`Open ${deviceCode.verificationUrl} and enter ${deviceCode.userCode}`);
|
|
123
123
|
|
|
124
|
-
const
|
|
124
|
+
const openAiChatGptAuth = await completeOpenAiChatGptDeviceCodeLogin(deviceCode);
|
|
125
125
|
const model = getModel({
|
|
126
126
|
provider: 'openai',
|
|
127
127
|
model: 'gpt-5.5',
|
|
128
|
-
|
|
128
|
+
openAiChatGptAuth,
|
|
129
129
|
});
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
Use `
|
|
132
|
+
Use `refreshOpenAiChatGptTokenData(openAiChatGptAuth)` before stored credentials expire, or after receiving an unauthorized response.
|
|
133
|
+
|
|
134
|
+
Node.js consumers can inspect and resolve local ChatGPT auth files through the Node-only subpath. This supports SmartAI's canonical auth file, OpenCode's `~/.local/share/opencode/auth.json`, and Codex's `~/.codex/auth.json` without exposing token values in inspection results.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import {
|
|
138
|
+
inspectOpenAiChatGptAuthSources,
|
|
139
|
+
resolveOpenAiChatGptAuth,
|
|
140
|
+
} from '@push.rocks/smartai/openai-chatgpt-auth';
|
|
141
|
+
|
|
142
|
+
const sources = await inspectOpenAiChatGptAuthSources({
|
|
143
|
+
sources: ['smartai', 'opencode', 'codex'],
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const resolved = await resolveOpenAiChatGptAuth({
|
|
147
|
+
sources: ['smartai', 'opencode', 'codex'],
|
|
148
|
+
refresh: 'ifNeeded',
|
|
149
|
+
writeBack: {
|
|
150
|
+
smartai: true,
|
|
151
|
+
opencode: false,
|
|
152
|
+
codex: false,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (resolved) {
|
|
157
|
+
const model = getModel({
|
|
158
|
+
provider: 'openai',
|
|
159
|
+
model: 'gpt-5.5',
|
|
160
|
+
openAiChatGptAuth: resolved.tokenData,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
```
|
|
133
164
|
|
|
134
165
|
### Re-exported AI SDK Functions
|
|
135
166
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartai',
|
|
6
|
-
version: '
|
|
6
|
+
version: '4.0.1',
|
|
7
7
|
description: 'Provider registry and capability utilities for ai-sdk (Vercel AI SDK). Core export returns LanguageModel; subpath exports provide vision, audio, image, document and research capabilities.'
|
|
8
8
|
}
|
package/ts/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export { getModel, getModelSetup } from './smartai.classes.smartai.js';
|
|
2
2
|
export type {
|
|
3
3
|
IOpenAiProviderOptions,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
IOpenAiChatGptAuthCredentials,
|
|
5
|
+
IOpenAiChatGptAuthOptions,
|
|
6
|
+
IOpenAiChatGptCompleteDeviceCodeOptions,
|
|
7
|
+
IOpenAiChatGptDeviceCode,
|
|
8
|
+
IOpenAiChatGptDeviceCodePollOptions,
|
|
9
|
+
IOpenAiChatGptTokenData,
|
|
10
|
+
IOpenAiChatGptTokenInfo,
|
|
11
11
|
ISmartAiModelSetup,
|
|
12
12
|
ISmartAiOptions,
|
|
13
13
|
TOpenAiReasoningEffort,
|
|
@@ -36,21 +36,21 @@ export type {
|
|
|
36
36
|
} from './smartai.cache.js';
|
|
37
37
|
export { createOllamaModel } from './smartai.provider.ollama.js';
|
|
38
38
|
export {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
OPENAI_CHATGPT_AUTH_ISSUER,
|
|
40
|
+
OPENAI_CHATGPT_CLIENT_ID,
|
|
41
|
+
OPENAI_CHATGPT_CODEX_BASE_URL,
|
|
42
|
+
OPENAI_CHATGPT_DEFAULT_ORIGINATOR,
|
|
43
|
+
OpenAiChatGptAuthError,
|
|
44
|
+
completeOpenAiChatGptDeviceCodeLogin,
|
|
45
|
+
createOpenAiChatGptProviderSettings,
|
|
46
|
+
ensureOpenAiChatGptWorkspaceAllowed,
|
|
47
|
+
exchangeOpenAiChatGptAuthorizationCode,
|
|
48
|
+
parseOpenAiChatGptTokenInfo,
|
|
49
|
+
pollOpenAiChatGptDeviceCode,
|
|
50
|
+
refreshOpenAiChatGptTokenData,
|
|
51
|
+
requestOpenAiChatGptDeviceCode,
|
|
52
52
|
} from './smartai.auth.openai.js';
|
|
53
|
-
export type {
|
|
53
|
+
export type { IOpenAiChatGptAuthorizationCode } from './smartai.auth.openai.js';
|
|
54
54
|
|
|
55
55
|
// Re-export commonly used ai-sdk functions for consumer convenience
|
|
56
56
|
export { generateText, streamText, tool, jsonSchema } from 'ai';
|