gpt-connector 0.1.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/src/asset-discovery.d.ts +9 -0
  4. package/dist/src/asset-discovery.js +73 -0
  5. package/dist/src/asset-discovery.js.map +1 -0
  6. package/dist/src/cdp.d.ts +29 -0
  7. package/dist/src/cdp.js +182 -0
  8. package/dist/src/cdp.js.map +1 -0
  9. package/dist/src/cli.d.ts +2 -0
  10. package/dist/src/cli.js +63 -0
  11. package/dist/src/cli.js.map +1 -0
  12. package/dist/src/connector.d.ts +16 -0
  13. package/dist/src/connector.js +222 -0
  14. package/dist/src/connector.js.map +1 -0
  15. package/dist/src/contract.d.ts +36 -0
  16. package/dist/src/contract.js +16 -0
  17. package/dist/src/contract.js.map +1 -0
  18. package/dist/src/errors.d.ts +7 -0
  19. package/dist/src/errors.js +23 -0
  20. package/dist/src/errors.js.map +1 -0
  21. package/dist/src/index.d.ts +11 -0
  22. package/dist/src/index.js +12 -0
  23. package/dist/src/index.js.map +1 -0
  24. package/dist/src/mcp-server.d.ts +19 -0
  25. package/dist/src/mcp-server.js +96 -0
  26. package/dist/src/mcp-server.js.map +1 -0
  27. package/dist/src/mcp.d.ts +2 -0
  28. package/dist/src/mcp.js +17 -0
  29. package/dist/src/mcp.js.map +1 -0
  30. package/dist/src/model-catalog.d.ts +23 -0
  31. package/dist/src/model-catalog.js +61 -0
  32. package/dist/src/model-catalog.js.map +1 -0
  33. package/dist/src/page-bridge.d.ts +4 -0
  34. package/dist/src/page-bridge.js +364 -0
  35. package/dist/src/page-bridge.js.map +1 -0
  36. package/dist/src/redaction.d.ts +1 -0
  37. package/dist/src/redaction.js +17 -0
  38. package/dist/src/redaction.js.map +1 -0
  39. package/dist/src/runtime-evaluate.d.ts +2 -0
  40. package/dist/src/runtime-evaluate.js +14 -0
  41. package/dist/src/runtime-evaluate.js.map +1 -0
  42. package/dist/src/session-registry.d.ts +18 -0
  43. package/dist/src/session-registry.js +57 -0
  44. package/dist/src/session-registry.js.map +1 -0
  45. package/package.json +62 -0
@@ -0,0 +1,364 @@
1
+ import { createHash } from "node:crypto";
2
+ export const bridgeGlobalName = "__gptConnectorBridgeV1";
3
+ const bridgeBootstrapSource = String.raw `async function(coreUrl, conversationUrl, expectedBuildId) {
4
+ const globalName = "__gptConnectorBridgeV1";
5
+ if (globalThis[globalName]?.version === 1 && globalThis[globalName]?.buildId === expectedBuildId) {
6
+ return globalThis[globalName].summary();
7
+ }
8
+
9
+ const [core, conversationModule] = await Promise.all([
10
+ import(coreUrl),
11
+ import(conversationUrl)
12
+ ]);
13
+
14
+ const entries = (module) => Object.entries(module);
15
+ const functionSource = (value) => Function.prototype.toString.call(value);
16
+ const unique = (role, candidates) => {
17
+ if (candidates.length !== 1) {
18
+ throw new Error("RUNTIME_DRIFT:" + role + ":" + candidates.length);
19
+ }
20
+ return candidates[0][1];
21
+ };
22
+
23
+ const sender = unique("sender", entries(core).filter(([, value]) => {
24
+ if (typeof value !== "function") return false;
25
+ const source = functionSource(value);
26
+ return source.includes("completion.submit.request.tpp_model_resolution") &&
27
+ source.includes("sendRequest({conduitToken") &&
28
+ source.includes("requestedModelId");
29
+ }));
30
+
31
+ const builder = unique("builder", entries(conversationModule).filter(([, value]) => {
32
+ if (typeof value !== "function") return false;
33
+ const source = functionSource(value);
34
+ return source.includes("contentToSend") &&
35
+ source.includes("allSystemHints") &&
36
+ source.includes("selectedSkillIds") &&
37
+ source.includes("build_request_params.prompt_message");
38
+ }));
39
+
40
+ const threadStore = unique("threadStore", entries(core).filter(([, value]) =>
41
+ value && typeof value === "object" &&
42
+ typeof value.initThread === "function" &&
43
+ typeof value.setServerIdForNewThread === "function" &&
44
+ typeof value.deleteThread === "function" &&
45
+ typeof value.retainThread === "function"
46
+ ));
47
+
48
+ const treeApi = unique("treeApi", entries(core).filter(([, value]) =>
49
+ value && typeof value === "object" &&
50
+ typeof value.getLastAssistantMessage === "function" &&
51
+ typeof value.getCurrentMessage === "function" &&
52
+ typeof value.getConversationTurns === "function"
53
+ ));
54
+
55
+ const apiClientCandidates = entries(core).filter(([, value]) =>
56
+ value && typeof value === "object" &&
57
+ typeof value.safeGet === "function" &&
58
+ typeof value.safePost === "function" &&
59
+ typeof value.safePatch === "function" &&
60
+ typeof value.safeDelete === "function"
61
+ );
62
+ const apiClientProbeResults = await Promise.all(apiClientCandidates.map(async ([, value]) => {
63
+ try {
64
+ const catalog = await value.safeGet("/models", {
65
+ parameters: { query: { supports_model_picker_upgrade_presets: true } }
66
+ });
67
+ return Array.isArray(catalog?.models) && typeof catalog?.default_model_slug === "string";
68
+ } catch {
69
+ return false;
70
+ }
71
+ }));
72
+ const apiClient = unique("apiClient", apiClientCandidates.filter((_, index) => apiClientProbeResults[index]));
73
+
74
+ const threadGetter = unique("threadGetter", entries(core).filter(([, value]) => {
75
+ if (typeof value !== "function" || value.length !== 1) return false;
76
+ const source = functionSource(value);
77
+ return /return [\w$]+\.threads\[[\w$]+\]\}$/.test(source);
78
+ }));
79
+
80
+ const factoryCandidates = entries(core).filter(([, value]) => {
81
+ if (typeof value !== "function") return false;
82
+ const source = functionSource(value);
83
+ return /^function [^(]+\([A-Za-z_$][\w$]*\)\{return [\w$]+\([\w$]+\(\),[\w$]+\(\),[A-Za-z_$][\w$]*\)\}$/.test(source);
84
+ });
85
+ const conversationFactory = unique("conversationFactory", factoryCandidates);
86
+
87
+ const sessions = new Map();
88
+ const operations = new Map();
89
+ const terminal = new Set(["succeeded", "failed"]);
90
+
91
+ const safeError = (error, fallbackCode) => ({
92
+ code: typeof error?.message === "string" && error.message.startsWith("RUNTIME_DRIFT:")
93
+ ? "RUNTIME_DRIFT"
94
+ : fallbackCode,
95
+ message: String(error?.message ?? error ?? "unknown error")
96
+ .slice(0, 240)
97
+ .replace(/[A-Za-z0-9_-]{32,}/g, "[redacted]")
98
+ });
99
+
100
+ const serverIdOf = (conversation) => {
101
+ const signal = conversation?.serverId$;
102
+ if (typeof signal === "function") return signal();
103
+ if (signal && typeof signal.get === "function") return signal.get();
104
+ return null;
105
+ };
106
+
107
+ const getCatalog = async () => {
108
+ const raw = await apiClient.safeGet("/models", {
109
+ parameters: { query: { supports_model_picker_upgrade_presets: true } }
110
+ });
111
+ const models = (raw.models ?? [])
112
+ .filter((model) => model?.is_work_mode_model !== true && typeof model?.slug === "string")
113
+ .map((model) => ({
114
+ id: model.slug,
115
+ title: typeof model.title === "string" ? model.title : model.slug,
116
+ reasoningType: typeof model.reasoning_type === "string" ? model.reasoning_type : null,
117
+ efforts: Array.isArray(model.thinking_efforts)
118
+ ? model.thinking_efforts.map((item) => item?.thinking_effort).filter((item) => typeof item === "string")
119
+ : [],
120
+ configurableEffort: model.configurable_thinking_effort === true,
121
+ maxTokens: typeof model.max_tokens === "number" ? model.max_tokens : null
122
+ }));
123
+ return {
124
+ defaultModel: models.some((model) => model.id === raw.default_model_slug)
125
+ ? raw.default_model_slug
126
+ : null,
127
+ models
128
+ };
129
+ };
130
+
131
+ const validateSelection = async (modelId, effort) => {
132
+ const catalog = await getCatalog();
133
+ if (modelId == null) {
134
+ if (effort != null) throw new Error("EFFORT_NOT_SUPPORTED:model_required");
135
+ return;
136
+ }
137
+ const model = catalog.models.find((candidate) => candidate.id === modelId);
138
+ if (!model) throw new Error("MODEL_NOT_AVAILABLE");
139
+ if (effort != null && !model.efforts.includes(effort)) {
140
+ throw new Error("EFFORT_NOT_SUPPORTED");
141
+ }
142
+ };
143
+
144
+ const extractResult = (conversation) => {
145
+ const thread = threadGetter(conversation.id);
146
+ const message = treeApi.getLastAssistantMessage(thread);
147
+ const text = Array.isArray(message?.content?.parts)
148
+ ? message.content.parts.filter((part) => typeof part === "string").join("")
149
+ : "";
150
+ if (!message || message.status !== "finished_successfully" || message.end_turn !== true || text.length === 0) {
151
+ throw new Error("STREAM_INCOMPLETE");
152
+ }
153
+ const metadata = message.metadata ?? {};
154
+ return {
155
+ text,
156
+ status: message.status,
157
+ endTurn: true,
158
+ resolvedModel: typeof metadata.resolved_model_slug === "string"
159
+ ? metadata.resolved_model_slug
160
+ : typeof metadata.model_slug === "string" ? metadata.model_slug : null,
161
+ resolvedEffort: typeof metadata.thinking_effort === "string"
162
+ ? metadata.thinking_effort
163
+ : null
164
+ };
165
+ };
166
+
167
+ const archive = async (conversation) => {
168
+ const serverId = serverIdOf(conversation);
169
+ if (!serverId) throw new Error("ARCHIVE_FAILED:no_server_id");
170
+ await apiClient.safePatch("/conversation/{conversation_id}", {
171
+ parameters: { path: { conversation_id: serverId } },
172
+ requestBody: { is_archived: true }
173
+ });
174
+ const data = await apiClient.safeGet("/conversation/{conversation_id}", {
175
+ parameters: { path: { conversation_id: serverId } }
176
+ });
177
+ if (data?.is_archived !== true) throw new Error("ARCHIVE_FAILED:not_confirmed");
178
+ };
179
+
180
+ const startOperation = (kind) => {
181
+ const id = crypto.randomUUID();
182
+ operations.set(id, { kind, state: "pending", result: null, error: null });
183
+ return id;
184
+ };
185
+
186
+ const finishFailure = (operation, error, fallbackCode) => {
187
+ operation.state = "failed";
188
+ operation.error = safeError(error, fallbackCode);
189
+ };
190
+
191
+ const bridge = {
192
+ version: 1,
193
+ buildId: expectedBuildId,
194
+ summary: () => ({ version: 1, buildId: expectedBuildId, ready: true }),
195
+ diagnostics: () => ({
196
+ sessionCount: sessions.size,
197
+ operationCount: operations.size
198
+ }),
199
+ startModels: () => {
200
+ const operationId = startOperation("models");
201
+ const operation = operations.get(operationId);
202
+ void getCatalog().then((catalog) => {
203
+ operation.state = "succeeded";
204
+ operation.result = catalog;
205
+ }, (error) => finishFailure(operation, error, "CHAT_FAILED"));
206
+ return { operationId };
207
+ },
208
+ startChat: (input) => {
209
+ const operationId = startOperation("chat");
210
+ const operation = operations.get(operationId);
211
+ const sessionId = input.sessionId ?? crypto.randomUUID();
212
+ let session = sessions.get(sessionId);
213
+ const createdSession = input.sessionId == null;
214
+ if (input.sessionId != null && !session) {
215
+ finishFailure(operation, new Error("SESSION_NOT_FOUND"), "SESSION_NOT_FOUND");
216
+ return { operationId };
217
+ }
218
+ if (session?.busy) {
219
+ finishFailure(operation, new Error("SESSION_BUSY"), "SESSION_BUSY");
220
+ return { operationId };
221
+ }
222
+ if (session) session.busy = true;
223
+
224
+ void (async () => {
225
+ try {
226
+ await validateSelection(input.model, input.effort);
227
+ if (!session) {
228
+ const conversation = conversationFactory();
229
+ if (!conversation || typeof conversation.id !== "string" || !conversation.id.startsWith("WEB:")) {
230
+ throw new Error("RUNTIME_DRIFT:factory_output");
231
+ }
232
+ threadStore.initThread({
233
+ clientThreadId: conversation.id,
234
+ conversationMode: { kind: "primary_assistant" },
235
+ conversationOrigin: null
236
+ });
237
+ session = { conversation, busy: true };
238
+ sessions.set(sessionId, session);
239
+ }
240
+ const prompt = String(input.prompt);
241
+ const params = await builder({
242
+ conversation: session.conversation,
243
+ attachments: [],
244
+ content: prompt,
245
+ contentToSend: { content: prompt, metadata: null },
246
+ conversationMode: { kind: "primary_assistant" },
247
+ hasSelectedApps: false,
248
+ desktopOrigin: null,
249
+ shouldCollectSidebarContext: false,
250
+ selectedApps: [],
251
+ selectedSources: undefined,
252
+ selectedMCPConnectors: undefined,
253
+ selectedConnectorIds: undefined,
254
+ searchConnectorIds: undefined,
255
+ startedWithByoMcp: false,
256
+ sourceEvent: undefined,
257
+ allSystemHints: [],
258
+ systemHints: [],
259
+ firstInputTimestampMs: performance.now(),
260
+ isN7jupdActive: false,
261
+ isForceAllowCustomMcpModeEnabled: false,
262
+ selectedSkillIds: [],
263
+ thinkingEffort: input.effort,
264
+ serviceTier: undefined
265
+ });
266
+ await sender({
267
+ ...params,
268
+ conversation: session.conversation,
269
+ requestedModelId: input.model,
270
+ thinkingEffort: input.effort,
271
+ serviceTier: undefined,
272
+ callsiteId: "request_completion.gpt_connector.1",
273
+ eventSource: "url"
274
+ });
275
+ const result = extractResult(session.conversation);
276
+ if (input.keepOpen === true) {
277
+ operation.result = { ...result, sessionId };
278
+ } else {
279
+ await archive(session.conversation);
280
+ sessions.delete(sessionId);
281
+ operation.result = result;
282
+ }
283
+ operation.state = "succeeded";
284
+ } catch (error) {
285
+ let cleanupError = null;
286
+ if (session && (createdSession || input.keepOpen !== true)) {
287
+ if (serverIdOf(session.conversation)) {
288
+ try {
289
+ await archive(session.conversation);
290
+ } catch (archiveError) {
291
+ cleanupError = archiveError;
292
+ }
293
+ }
294
+ sessions.delete(sessionId);
295
+ }
296
+ if (cleanupError) {
297
+ finishFailure(operation, cleanupError, "ARCHIVE_FAILED");
298
+ return;
299
+ }
300
+ const message = String(error?.message ?? error);
301
+ const code = message.startsWith("MODEL_NOT_AVAILABLE") ? "MODEL_NOT_AVAILABLE"
302
+ : message.startsWith("EFFORT_NOT_SUPPORTED") ? "EFFORT_NOT_SUPPORTED"
303
+ : message.startsWith("STREAM_INCOMPLETE") ? "STREAM_INCOMPLETE"
304
+ : message.startsWith("ARCHIVE_FAILED") ? "ARCHIVE_FAILED"
305
+ : message.startsWith("RUNTIME_DRIFT") ? "RUNTIME_DRIFT"
306
+ : "CHAT_FAILED";
307
+ finishFailure(operation, error, code);
308
+ } finally {
309
+ const current = sessions.get(sessionId);
310
+ if (current) current.busy = false;
311
+ }
312
+ })();
313
+ return { operationId };
314
+ },
315
+ startClose: (input) => {
316
+ const operationId = startOperation("close");
317
+ const operation = operations.get(operationId);
318
+ const session = sessions.get(input.sessionId);
319
+ if (!session) {
320
+ finishFailure(operation, new Error("SESSION_NOT_FOUND"), "SESSION_NOT_FOUND");
321
+ return { operationId };
322
+ }
323
+ if (session.busy) {
324
+ finishFailure(operation, new Error("SESSION_BUSY"), "SESSION_BUSY");
325
+ return { operationId };
326
+ }
327
+ session.busy = true;
328
+ void archive(session.conversation).then(() => {
329
+ sessions.delete(input.sessionId);
330
+ operation.state = "succeeded";
331
+ operation.result = { archived: true };
332
+ }, (error) => {
333
+ session.busy = false;
334
+ finishFailure(operation, error, "ARCHIVE_FAILED");
335
+ });
336
+ return { operationId };
337
+ },
338
+ poll: (operationId, consume) => {
339
+ const operation = operations.get(operationId);
340
+ if (!operation) return { state: "failed", error: { code: "CHAT_FAILED", message: "operation_not_found" } };
341
+ const result = {
342
+ state: operation.state,
343
+ result: operation.result,
344
+ error: operation.error
345
+ };
346
+ if (consume === true && terminal.has(operation.state)) operations.delete(operationId);
347
+ return result;
348
+ }
349
+ };
350
+
351
+ globalThis[globalName] = bridge;
352
+ return bridge.summary();
353
+ }`;
354
+ export const bridgeBuildId = createHash("sha256")
355
+ .update(bridgeBootstrapSource)
356
+ .digest("hex")
357
+ .slice(0, 16);
358
+ export function createBridgeBootstrapExpression(coreUrl, conversationUrl) {
359
+ return `(${bridgeBootstrapSource})(${JSON.stringify(coreUrl)}, ${JSON.stringify(conversationUrl)}, ${JSON.stringify(bridgeBuildId)})`;
360
+ }
361
+ export function createBridgeCallExpression(method, args) {
362
+ return `globalThis[${JSON.stringify(bridgeGlobalName)}].${method}(...${JSON.stringify(args)})`;
363
+ }
364
+ //# sourceMappingURL=page-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-bridge.js","sourceRoot":"","sources":["../../src/page-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AAEzD,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8VtC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,CAAC;KAC9C,MAAM,CAAC,qBAAqB,CAAC;KAC7B,MAAM,CAAC,KAAK,CAAC;KACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEhB,MAAM,UAAU,+BAA+B,CAC7C,OAAe,EACf,eAAuB;IAEvB,OAAO,IAAI,qBAAqB,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,CAAC;AACxI,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,MAA2D,EAC3D,IAAwB;IAExB,OAAO,cAAc,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,KAAK,MAAM,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;AACjG,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function redactDiagnostic(value: unknown): unknown;
@@ -0,0 +1,17 @@
1
+ const secretKeyPattern = /authorization|cookie|token|attestation|conduit|account.?id/i;
2
+ const longIdentifierPattern = /\b[A-Za-z0-9_-]{32,}\b/g;
3
+ export function redactDiagnostic(value) {
4
+ if (Array.isArray(value))
5
+ return value.map(redactDiagnostic);
6
+ if (typeof value === "object" && value !== null) {
7
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [
8
+ key,
9
+ secretKeyPattern.test(key) ? "[redacted]" : redactDiagnostic(child),
10
+ ]));
11
+ }
12
+ if (typeof value === "string") {
13
+ return value.replace(longIdentifierPattern, "[redacted]");
14
+ }
15
+ return value;
16
+ }
17
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../src/redaction.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,6DAA6D,CAAC;AACvF,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAExD,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAE7D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;YAC1C,GAAG;YACH,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC;SACpE,CAAC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CdpClient } from "./cdp.js";
2
+ export declare function evaluateByValue<T>(client: CdpClient, expression: string, awaitPromise?: boolean): Promise<T>;
@@ -0,0 +1,14 @@
1
+ import { ConnectorError } from "./errors.js";
2
+ export async function evaluateByValue(client, expression, awaitPromise = true) {
3
+ const response = await client.call("Runtime.evaluate", {
4
+ expression,
5
+ awaitPromise,
6
+ returnByValue: true,
7
+ userGesture: false,
8
+ });
9
+ if (response.exceptionDetails !== undefined || response.result === undefined) {
10
+ throw new ConnectorError("RUNTIME_DRIFT", "ChatGPT page main worldで式を評価できませんでした。");
11
+ }
12
+ return response.result.value;
13
+ }
14
+ //# sourceMappingURL=runtime-evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-evaluate.js","sourceRoot":"","sources":["../../src/runtime-evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAa7C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAiB,EACjB,UAAkB,EAClB,YAAY,GAAG,IAAI;IAEnB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAA0B,kBAAkB,EAAE;QAC9E,UAAU;QACV,YAAY;QACZ,aAAa,EAAE,IAAI;QACnB,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7E,MAAM,IAAI,cAAc,CACtB,eAAe,EACf,uCAAuC,CACxC,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAU,CAAC;AACpC,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface SessionRecord {
2
+ readonly id: string;
3
+ readonly createdAt: number;
4
+ lastUsedAt: number;
5
+ busy: boolean;
6
+ }
7
+ export declare class SessionRegistry {
8
+ #private;
9
+ create(now?: number): SessionRecord;
10
+ register(id: string, now?: number): SessionRecord;
11
+ get(id: string): SessionRecord;
12
+ acquire(id: string, now?: number): SessionRecord;
13
+ release(id: string, now?: number): void;
14
+ delete(id: string): void;
15
+ has(id: string): boolean;
16
+ get size(): number;
17
+ ids(): readonly string[];
18
+ }
@@ -0,0 +1,57 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ConnectorError } from "./errors.js";
3
+ export class SessionRegistry {
4
+ #sessions = new Map();
5
+ create(now = Date.now()) {
6
+ return this.register(randomUUID(), now);
7
+ }
8
+ register(id, now = Date.now()) {
9
+ if (this.#sessions.has(id)) {
10
+ throw new ConnectorError("SESSION_BUSY", "sessionはすでに登録されています。");
11
+ }
12
+ const record = {
13
+ id,
14
+ createdAt: now,
15
+ lastUsedAt: now,
16
+ busy: false,
17
+ };
18
+ this.#sessions.set(record.id, record);
19
+ return record;
20
+ }
21
+ get(id) {
22
+ const record = this.#sessions.get(id);
23
+ if (record === undefined) {
24
+ throw new ConnectorError("SESSION_NOT_FOUND", "sessionが見つかりません。");
25
+ }
26
+ return record;
27
+ }
28
+ acquire(id, now = Date.now()) {
29
+ const record = this.get(id);
30
+ if (record.busy) {
31
+ throw new ConnectorError("SESSION_BUSY", "sessionは別turnを処理中です。");
32
+ }
33
+ record.busy = true;
34
+ record.lastUsedAt = now;
35
+ return record;
36
+ }
37
+ release(id, now = Date.now()) {
38
+ const record = this.get(id);
39
+ record.busy = false;
40
+ record.lastUsedAt = now;
41
+ }
42
+ delete(id) {
43
+ if (!this.#sessions.delete(id)) {
44
+ throw new ConnectorError("SESSION_NOT_FOUND", "sessionが見つかりません。");
45
+ }
46
+ }
47
+ has(id) {
48
+ return this.#sessions.has(id);
49
+ }
50
+ get size() {
51
+ return this.#sessions.size;
52
+ }
53
+ ids() {
54
+ return [...this.#sessions.keys()];
55
+ }
56
+ }
57
+ //# sourceMappingURL=session-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../src/session-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,MAAM,OAAO,eAAe;IACjB,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEtD,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;IAED,QAAQ,CAAC,EAAU,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACnC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,cAAc,CAAC,cAAc,EAAE,sBAAsB,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,MAAM,GAAkB;YAC5B,EAAE;YACF,SAAS,EAAE,GAAG;YACd,UAAU,EAAE,GAAG;YACf,IAAI,EAAE,KAAK;SACZ,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,cAAc,CAAC,mBAAmB,EAAE,kBAAkB,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,CAAC,EAAU,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,IAAI,cAAc,CAAC,cAAc,EAAE,sBAAsB,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,CAAC,EAAU,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC;QACpB,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,EAAU;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,cAAc,CAAC,mBAAmB,EAAE,kBAAkB,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IACpC,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "gpt-connector",
3
+ "version": "0.1.0",
4
+ "description": "UIに依存せず、ログイン済みChatGPT Web runtimeの通常ChatをCodexから呼び出すローカルconnector",
5
+ "license": "MIT",
6
+ "author": "kitepon-rgb",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/kitepon-rgb/gpt-connector.git"
10
+ },
11
+ "homepage": "https://github.com/kitepon-rgb/gpt-connector#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/kitepon-rgb/gpt-connector/issues"
14
+ },
15
+ "type": "module",
16
+ "main": "dist/src/index.js",
17
+ "types": "dist/src/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/src/index.d.ts",
21
+ "import": "./dist/src/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/src",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "bin": {
30
+ "gpt-connector": "dist/src/cli.js",
31
+ "gpt-connector-mcp": "dist/src/mcp.js"
32
+ },
33
+ "engines": {
34
+ "node": ">=26"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json",
38
+ "cli": "tsx src/cli.ts",
39
+ "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "lint": "eslint .",
41
+ "test": "tsx --test test/*.test.ts",
42
+ "check": "pnpm lint && pnpm typecheck && pnpm test",
43
+ "prepublishOnly": "pnpm check && pnpm build"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "dependencies": {
49
+ "@modelcontextprotocol/sdk": "1.29.0",
50
+ "ws": "8.21.0",
51
+ "zod": "4.4.3"
52
+ },
53
+ "devDependencies": {
54
+ "@eslint/js": "10.0.1",
55
+ "@types/node": "26.1.1",
56
+ "@types/ws": "8.18.1",
57
+ "eslint": "10.7.0",
58
+ "tsx": "4.23.0",
59
+ "typescript": "6.0.3",
60
+ "typescript-eslint": "8.63.0"
61
+ }
62
+ }