@glbgpt/cli 0.1.0-test.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.
Files changed (173) hide show
  1. package/README.md +111 -0
  2. package/dist/src/auth/authClient.d.ts +32 -0
  3. package/dist/src/auth/authClient.js +88 -0
  4. package/dist/src/auth/authClient.js.map +1 -0
  5. package/dist/src/auth/credentialStore.d.ts +22 -0
  6. package/dist/src/auth/credentialStore.js +51 -0
  7. package/dist/src/auth/credentialStore.js.map +1 -0
  8. package/dist/src/auth/loginFlow.d.ts +30 -0
  9. package/dist/src/auth/loginFlow.js +43 -0
  10. package/dist/src/auth/loginFlow.js.map +1 -0
  11. package/dist/src/auth/loopbackServer.d.ts +41 -0
  12. package/dist/src/auth/loopbackServer.js +141 -0
  13. package/dist/src/auth/loopbackServer.js.map +1 -0
  14. package/dist/src/auth/token.d.ts +8 -0
  15. package/dist/src/auth/token.js +15 -0
  16. package/dist/src/auth/token.js.map +1 -0
  17. package/dist/src/commands/accountCommand.d.ts +2 -0
  18. package/dist/src/commands/accountCommand.js +56 -0
  19. package/dist/src/commands/accountCommand.js.map +1 -0
  20. package/dist/src/commands/configCommand.d.ts +4 -0
  21. package/dist/src/commands/configCommand.js +53 -0
  22. package/dist/src/commands/configCommand.js.map +1 -0
  23. package/dist/src/commands/execCommand.d.ts +32 -0
  24. package/dist/src/commands/execCommand.js +122 -0
  25. package/dist/src/commands/execCommand.js.map +1 -0
  26. package/dist/src/commands/loginCommand.d.ts +11 -0
  27. package/dist/src/commands/loginCommand.js +96 -0
  28. package/dist/src/commands/loginCommand.js.map +1 -0
  29. package/dist/src/commands/mediaCommand.d.ts +41 -0
  30. package/dist/src/commands/mediaCommand.js +277 -0
  31. package/dist/src/commands/mediaCommand.js.map +1 -0
  32. package/dist/src/config/configFile.d.ts +30 -0
  33. package/dist/src/config/configFile.js +54 -0
  34. package/dist/src/config/configFile.js.map +1 -0
  35. package/dist/src/config/env.d.ts +6 -0
  36. package/dist/src/config/env.js +24 -0
  37. package/dist/src/config/env.js.map +1 -0
  38. package/dist/src/config/paths.d.ts +6 -0
  39. package/dist/src/config/paths.js +16 -0
  40. package/dist/src/config/paths.js.map +1 -0
  41. package/dist/src/core/catalog.d.ts +49 -0
  42. package/dist/src/core/catalog.js +96 -0
  43. package/dist/src/core/catalog.js.map +1 -0
  44. package/dist/src/core/httpChatBackend.d.ts +31 -0
  45. package/dist/src/core/httpChatBackend.js +134 -0
  46. package/dist/src/core/httpChatBackend.js.map +1 -0
  47. package/dist/src/core/id.d.ts +2 -0
  48. package/dist/src/core/id.js +5 -0
  49. package/dist/src/core/id.js.map +1 -0
  50. package/dist/src/core/runChat.d.ts +28 -0
  51. package/dist/src/core/runChat.js +49 -0
  52. package/dist/src/core/runChat.js.map +1 -0
  53. package/dist/src/core/session.d.ts +44 -0
  54. package/dist/src/core/session.js +70 -0
  55. package/dist/src/core/session.js.map +1 -0
  56. package/dist/src/core/sessionStore.d.ts +39 -0
  57. package/dist/src/core/sessionStore.js +106 -0
  58. package/dist/src/core/sessionStore.js.map +1 -0
  59. package/dist/src/core/toolRegistry.d.ts +19 -0
  60. package/dist/src/core/toolRegistry.js +53 -0
  61. package/dist/src/core/toolRegistry.js.map +1 -0
  62. package/dist/src/core/tools/generateImage.d.ts +10 -0
  63. package/dist/src/core/tools/generateImage.js +34 -0
  64. package/dist/src/core/tools/generateImage.js.map +1 -0
  65. package/dist/src/core/turnLoop.d.ts +31 -0
  66. package/dist/src/core/turnLoop.js +155 -0
  67. package/dist/src/core/turnLoop.js.map +1 -0
  68. package/dist/src/core/types.d.ts +137 -0
  69. package/dist/src/core/types.js +9 -0
  70. package/dist/src/core/types.js.map +1 -0
  71. package/dist/src/exec/input.d.ts +35 -0
  72. package/dist/src/exec/input.js +85 -0
  73. package/dist/src/exec/input.js.map +1 -0
  74. package/dist/src/exec/output.d.ts +39 -0
  75. package/dist/src/exec/output.js +84 -0
  76. package/dist/src/exec/output.js.map +1 -0
  77. package/dist/src/index.d.ts +2 -0
  78. package/dist/src/index.js +253 -0
  79. package/dist/src/index.js.map +1 -0
  80. package/dist/src/mcp/server.d.ts +38 -0
  81. package/dist/src/mcp/server.js +99 -0
  82. package/dist/src/mcp/server.js.map +1 -0
  83. package/dist/src/media/aiApiClient.d.ts +53 -0
  84. package/dist/src/media/aiApiClient.js +97 -0
  85. package/dist/src/media/aiApiClient.js.map +1 -0
  86. package/dist/src/media/errors.d.ts +7 -0
  87. package/dist/src/media/errors.js +70 -0
  88. package/dist/src/media/errors.js.map +1 -0
  89. package/dist/src/media/executor.d.ts +50 -0
  90. package/dist/src/media/executor.js +55 -0
  91. package/dist/src/media/executor.js.map +1 -0
  92. package/dist/src/media/generateImageTool.d.ts +19 -0
  93. package/dist/src/media/generateImageTool.js +120 -0
  94. package/dist/src/media/generateImageTool.js.map +1 -0
  95. package/dist/src/media/manifest.d.ts +23 -0
  96. package/dist/src/media/manifest.js +558 -0
  97. package/dist/src/media/manifest.js.map +1 -0
  98. package/dist/src/media/resolution.d.ts +13 -0
  99. package/dist/src/media/resolution.js +42 -0
  100. package/dist/src/media/resolution.js.map +1 -0
  101. package/dist/src/media/taskLedger.d.ts +17 -0
  102. package/dist/src/media/taskLedger.js +44 -0
  103. package/dist/src/media/taskLedger.js.map +1 -0
  104. package/dist/src/media/taskManager.d.ts +52 -0
  105. package/dist/src/media/taskManager.js +130 -0
  106. package/dist/src/media/taskManager.js.map +1 -0
  107. package/dist/src/media/types.d.ts +80 -0
  108. package/dist/src/media/types.js +15 -0
  109. package/dist/src/media/types.js.map +1 -0
  110. package/dist/src/tui/App.d.ts +34 -0
  111. package/dist/src/tui/App.js +540 -0
  112. package/dist/src/tui/App.js.map +1 -0
  113. package/dist/src/tui/MultilineInput.d.ts +36 -0
  114. package/dist/src/tui/MultilineInput.js +221 -0
  115. package/dist/src/tui/MultilineInput.js.map +1 -0
  116. package/dist/src/tui/atPopup.d.ts +21 -0
  117. package/dist/src/tui/atPopup.js +55 -0
  118. package/dist/src/tui/atPopup.js.map +1 -0
  119. package/dist/src/tui/attachments.d.ts +24 -0
  120. package/dist/src/tui/attachments.js +91 -0
  121. package/dist/src/tui/attachments.js.map +1 -0
  122. package/dist/src/tui/branding.d.ts +22 -0
  123. package/dist/src/tui/branding.js +119 -0
  124. package/dist/src/tui/branding.js.map +1 -0
  125. package/dist/src/tui/chatController.d.ts +188 -0
  126. package/dist/src/tui/chatController.js +614 -0
  127. package/dist/src/tui/chatController.js.map +1 -0
  128. package/dist/src/tui/commands.d.ts +13 -0
  129. package/dist/src/tui/commands.js +22 -0
  130. package/dist/src/tui/commands.js.map +1 -0
  131. package/dist/src/tui/confirmationBridge.d.ts +23 -0
  132. package/dist/src/tui/confirmationBridge.js +47 -0
  133. package/dist/src/tui/confirmationBridge.js.map +1 -0
  134. package/dist/src/tui/inputHistory.d.ts +7 -0
  135. package/dist/src/tui/inputHistory.js +64 -0
  136. package/dist/src/tui/inputHistory.js.map +1 -0
  137. package/dist/src/tui/markdown.d.ts +1 -0
  138. package/dist/src/tui/markdown.js +143 -0
  139. package/dist/src/tui/markdown.js.map +1 -0
  140. package/dist/src/tui/models.d.ts +30 -0
  141. package/dist/src/tui/models.js +54 -0
  142. package/dist/src/tui/models.js.map +1 -0
  143. package/dist/src/tui/run.d.ts +29 -0
  144. package/dist/src/tui/run.js +212 -0
  145. package/dist/src/tui/run.js.map +1 -0
  146. package/dist/src/tui/slashMedia.d.ts +36 -0
  147. package/dist/src/tui/slashMedia.js +86 -0
  148. package/dist/src/tui/slashMedia.js.map +1 -0
  149. package/dist/src/tui/visualLines.d.ts +38 -0
  150. package/dist/src/tui/visualLines.js +110 -0
  151. package/dist/src/tui/visualLines.js.map +1 -0
  152. package/dist/src/util/clipboardImage.d.ts +6 -0
  153. package/dist/src/util/clipboardImage.js +69 -0
  154. package/dist/src/util/clipboardImage.js.map +1 -0
  155. package/dist/src/util/hyperlink.d.ts +10 -0
  156. package/dist/src/util/hyperlink.js +33 -0
  157. package/dist/src/util/hyperlink.js.map +1 -0
  158. package/dist/src/util/imageRef.d.ts +15 -0
  159. package/dist/src/util/imageRef.js +46 -0
  160. package/dist/src/util/imageRef.js.map +1 -0
  161. package/dist/src/util/inlineImage.d.ts +13 -0
  162. package/dist/src/util/inlineImage.js +56 -0
  163. package/dist/src/util/inlineImage.js.map +1 -0
  164. package/dist/src/util/openBrowser.d.ts +5 -0
  165. package/dist/src/util/openBrowser.js +23 -0
  166. package/dist/src/util/openBrowser.js.map +1 -0
  167. package/dist/src/util/retry.d.ts +43 -0
  168. package/dist/src/util/retry.js +116 -0
  169. package/dist/src/util/retry.js.map +1 -0
  170. package/dist/src/version.d.ts +6 -0
  171. package/dist/src/version.js +16 -0
  172. package/dist/src/version.js.map +1 -0
  173. package/package.json +45 -0
@@ -0,0 +1,31 @@
1
+ import type { ChatBackend, EngineEvent } from './types.js';
2
+ import type { Session } from './session.js';
3
+ import type { ToolRegistry } from './toolRegistry.js';
4
+ export type Emit = (event: EngineEvent) => void;
5
+ /** Per-turn options. `exec --output-schema` passes a `response_format` value here. */
6
+ export interface TurnOptions {
7
+ responseFormat?: unknown;
8
+ /** Abort signal for user interruption (Esc in the TUI). */
9
+ signal?: AbortSignal;
10
+ }
11
+ /**
12
+ * Drives one user turn to completion, following the OpenAI tool-calling protocol:
13
+ *
14
+ * stream model call -> accumulate assistant text + tool_calls
15
+ * -> if tool_calls: run each tool locally, append results, stream again
16
+ * -> else: turn complete
17
+ *
18
+ * The model's "intelligence loop" lives here on the client; the backend only streams
19
+ * one model call at a time. Image/video tools execute locally (via the registry) and
20
+ * return e.g. a task id, which is fed back so the conversation can continue.
21
+ */
22
+ export declare class TurnLoop {
23
+ private readonly backend;
24
+ private readonly registry;
25
+ constructor(backend: ChatBackend, registry: ToolRegistry);
26
+ runTurn(session: Session, userContent: string | import('./types.js').ContentPart[], emit: Emit, opts?: TurnOptions): Promise<void>;
27
+ /** Stream a single model call, emitting text deltas and accumulating tool_calls. */
28
+ private streamOneCall;
29
+ /** Iterate the backend stream, forwarding deltas/usage and accumulating tool-call pieces. */
30
+ private consumeStream;
31
+ }
@@ -0,0 +1,155 @@
1
+ /** Safety cap on tool-calling rounds within a single user turn. */
2
+ const MAX_ROUNDS = 8;
3
+ /**
4
+ * Drives one user turn to completion, following the OpenAI tool-calling protocol:
5
+ *
6
+ * stream model call -> accumulate assistant text + tool_calls
7
+ * -> if tool_calls: run each tool locally, append results, stream again
8
+ * -> else: turn complete
9
+ *
10
+ * The model's "intelligence loop" lives here on the client; the backend only streams
11
+ * one model call at a time. Image/video tools execute locally (via the registry) and
12
+ * return e.g. a task id, which is fed back so the conversation can continue.
13
+ */
14
+ export class TurnLoop {
15
+ backend;
16
+ registry;
17
+ constructor(backend, registry) {
18
+ this.backend = backend;
19
+ this.registry = registry;
20
+ }
21
+ async runTurn(session, userContent, emit, opts = {}) {
22
+ session.addUserMessage(userContent);
23
+ for (let round = 0; round < MAX_ROUNDS; round++) {
24
+ let content;
25
+ let toolCalls;
26
+ try {
27
+ ({ content, toolCalls } = await this.streamOneCall(session, emit, opts));
28
+ }
29
+ catch (err) {
30
+ // User interruption (Esc): keep whatever partial text already streamed so the
31
+ // conversation history matches what the user saw, then end the turn quietly.
32
+ if (opts.signal?.aborted) {
33
+ const partial = partialContentOf(err);
34
+ if (partial)
35
+ session.addAssistantMessage(partial, []);
36
+ emit({ type: 'turn_interrupted' });
37
+ return;
38
+ }
39
+ throw err;
40
+ }
41
+ // A call that produced neither text nor tool calls is a failure, not a completed turn
42
+ // (e.g. an upstream that closed the stream early). Don't record an empty assistant
43
+ // message and don't pretend the turn succeeded.
44
+ if (content.length === 0 && toolCalls.length === 0) {
45
+ emit({ type: 'error', message: 'The model returned no output. Please try again.' });
46
+ return;
47
+ }
48
+ session.addAssistantMessage(content.length > 0 ? content : null, toolCalls);
49
+ if (toolCalls.length === 0) {
50
+ emit({ type: 'turn_complete' });
51
+ return;
52
+ }
53
+ // Execute each requested tool locally and feed results back into the conversation.
54
+ // Tool execution itself is never aborted mid-flight (a paid submit must not be torn),
55
+ // but an interrupt takes effect before the next model call.
56
+ for (const call of toolCalls) {
57
+ const args = safeParseArgs(call.function.arguments);
58
+ emit({ type: 'tool_call_begin', id: call.id, name: call.function.name, args });
59
+ const result = await this.registry.dispatch(call.function.name, call.function.arguments);
60
+ emit({ type: 'tool_call_end', id: call.id, name: call.function.name, result });
61
+ session.addToolMessage(call.id, call.function.name, result);
62
+ }
63
+ if (opts.signal?.aborted) {
64
+ emit({ type: 'turn_interrupted' });
65
+ return;
66
+ }
67
+ // Loop: stream again now that tool results are in the messages.
68
+ }
69
+ emit({ type: 'error', message: `Turn exceeded ${MAX_ROUNDS} tool-calling rounds` });
70
+ }
71
+ /** Stream a single model call, emitting text deltas and accumulating tool_calls. */
72
+ async streamOneCall(session, emit, opts) {
73
+ const req = {
74
+ model: session.model,
75
+ messages: session.messages,
76
+ tools: this.registry.toOpenAITools(),
77
+ stream: true,
78
+ ...(opts.responseFormat !== undefined ? { response_format: opts.responseFormat } : {}),
79
+ };
80
+ let content = '';
81
+ const toolAcc = new Map();
82
+ try {
83
+ await this.consumeStream(req, opts, emit, (text) => (content += text), toolAcc);
84
+ }
85
+ catch (err) {
86
+ // Attach the partial text so an interrupt can preserve what already streamed.
87
+ if (opts.signal?.aborted)
88
+ throw new InterruptedStreamError(content);
89
+ throw err;
90
+ }
91
+ const toolCalls = [...toolAcc.entries()]
92
+ .sort((a, b) => a[0] - b[0])
93
+ .map(([, e]) => ({
94
+ id: e.id,
95
+ type: 'function',
96
+ function: { name: e.name, arguments: e.args },
97
+ }))
98
+ .filter((c) => c.function.name.length > 0);
99
+ return { content, toolCalls };
100
+ }
101
+ /** Iterate the backend stream, forwarding deltas/usage and accumulating tool-call pieces. */
102
+ async consumeStream(req, opts, emit, onText, toolAcc) {
103
+ for await (const chunk of this.backend.stream(req, opts.signal)) {
104
+ // The final chunk (include_usage) carries token counts with empty/no choices; surface
105
+ // them before the delta guard below skips it.
106
+ if (chunk.usage) {
107
+ emit({
108
+ type: 'usage',
109
+ promptTokens: chunk.usage.prompt_tokens ?? 0,
110
+ completionTokens: chunk.usage.completion_tokens ?? 0,
111
+ });
112
+ }
113
+ const choice = chunk.choices?.[0];
114
+ const delta = choice?.delta;
115
+ if (!delta)
116
+ continue;
117
+ if (typeof delta.content === 'string' && delta.content.length > 0) {
118
+ onText(delta.content);
119
+ emit({ type: 'assistant_delta', text: delta.content });
120
+ }
121
+ if (delta.tool_calls) {
122
+ for (const tc of delta.tool_calls) {
123
+ const entry = toolAcc.get(tc.index) ?? { id: '', name: '', args: '' };
124
+ if (tc.id)
125
+ entry.id = tc.id;
126
+ if (tc.function?.name)
127
+ entry.name = tc.function.name;
128
+ if (tc.function?.arguments)
129
+ entry.args += tc.function.arguments;
130
+ toolAcc.set(tc.index, entry);
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ /** Thrown when the user aborts mid-stream; carries the text streamed so far. */
137
+ class InterruptedStreamError extends Error {
138
+ partialContent;
139
+ constructor(partialContent) {
140
+ super('Interrupted');
141
+ this.partialContent = partialContent;
142
+ }
143
+ }
144
+ function partialContentOf(err) {
145
+ return err instanceof InterruptedStreamError ? err.partialContent : '';
146
+ }
147
+ function safeParseArgs(raw) {
148
+ try {
149
+ return raw && raw.trim() ? JSON.parse(raw) : {};
150
+ }
151
+ catch {
152
+ return {};
153
+ }
154
+ }
155
+ //# sourceMappingURL=turnLoop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnLoop.js","sourceRoot":"","sources":["../../../src/core/turnLoop.ts"],"names":[],"mappings":"AAIA,mEAAmE;AACnE,MAAM,UAAU,GAAG,CAAC,CAAC;AAWrB;;;;;;;;;;GAUG;AACH,MAAM,OAAO,QAAQ;IAEA;IACA;IAFnB,YACmB,OAAoB,EACpB,QAAsB;QADtB,YAAO,GAAP,OAAO,CAAa;QACpB,aAAQ,GAAR,QAAQ,CAAc;IACtC,CAAC;IAEJ,KAAK,CAAC,OAAO,CACX,OAAgB,EAChB,WAAwD,EACxD,IAAU,EACV,OAAoB,EAAE;QAEtB,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;QAEpC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;YAChD,IAAI,OAAe,CAAC;YACpB,IAAI,SAAqB,CAAC;YAC1B,IAAI,CAAC;gBACH,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;YAC3E,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,8EAA8E;gBAC9E,6EAA6E;gBAC7E,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;oBACzB,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;oBACtC,IAAI,OAAO;wBAAE,OAAO,CAAC,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;oBACtD,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBACnC,OAAO;gBACT,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,sFAAsF;YACtF,mFAAmF;YACnF,gDAAgD;YAChD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,iDAAiD,EAAE,CAAC,CAAC;gBACpF,OAAO;YACT,CAAC;YAED,OAAO,CAAC,mBAAmB,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAE5E,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;gBAChC,OAAO;YACT,CAAC;YAED,mFAAmF;YACnF,sFAAsF;YACtF,4DAA4D;YAC5D,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpD,IAAI,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACzF,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC/E,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC9D,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBACzB,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBACnC,OAAO;YACT,CAAC;YACD,gEAAgE;QAClE,CAAC;QAED,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,UAAU,sBAAsB,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,oFAAoF;IAC5E,KAAK,CAAC,aAAa,CACzB,OAAgB,EAChB,IAAU,EACV,IAAiB;QAEjB,MAAM,GAAG,GAAG;YACV,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE;YACpC,MAAM,EAAE,IAAa;YACrB,GAAG,CAAC,IAAI,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvF,CAAC;QAEF,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsD,CAAC;QAE9E,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAClF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,8EAA8E;YAC9E,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC;YACpE,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,SAAS,GAAe,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;aACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACf,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,UAAmB;YACzB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE;SAC9C,CAAC,CAAC;aACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE7C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,6FAA6F;IACrF,KAAK,CAAC,aAAa,CACzB,GAAyC,EACzC,IAAiB,EACjB,IAAU,EACV,MAA8B,EAC9B,OAAgE;QAEhE,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAChE,sFAAsF;YACtF,8CAA8C;YAC9C,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC;oBACH,IAAI,EAAE,OAAO;oBACb,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC;oBAC5C,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,iBAAiB,IAAI,CAAC;iBACrD,CAAC,CAAC;YACL,CAAC;YAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,CAAC;YAC5B,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACtB,IAAI,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzD,CAAC;YAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrB,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;oBAClC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;oBACtE,IAAI,EAAE,CAAC,EAAE;wBAAE,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;oBAC5B,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI;wBAAE,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;oBACrD,IAAI,EAAE,CAAC,QAAQ,EAAE,SAAS;wBAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;oBAChE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,gFAAgF;AAChF,MAAM,sBAAuB,SAAQ,KAAK;IACnB;IAArB,YAAqB,cAAsB;QACzC,KAAK,CAAC,aAAa,CAAC,CAAC;QADF,mBAAc,GAAd,cAAc,CAAQ;IAE3C,CAAC;CACF;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,OAAO,GAAG,YAAY,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Core domain types for the glbgpt CLI chat engine.
3
+ *
4
+ * The chat backend speaks the OpenAI-compatible chat/completions protocol
5
+ * (see backend `/ai-api/cli/v1/chat/completions`). The CLI owns the turn loop
6
+ * and the client-side tools; the backend is a stateless streaming passthrough.
7
+ */
8
+ /** One part of a multimodal user message (OpenAI content-parts shape). */
9
+ export type ContentPart = {
10
+ type: 'text';
11
+ text: string;
12
+ } | {
13
+ type: 'image_url';
14
+ image_url: {
15
+ url: string;
16
+ };
17
+ };
18
+ /** OpenAI-compatible chat message. */
19
+ export interface ChatMessage {
20
+ role: 'system' | 'user' | 'assistant' | 'tool';
21
+ /**
22
+ * Text content, or content parts for a multimodal user message (vision).
23
+ * Null/undefined for an assistant message that only carries tool_calls.
24
+ */
25
+ content?: string | ContentPart[] | null;
26
+ /** Present on assistant messages that request tool calls. */
27
+ tool_calls?: ToolCall[];
28
+ /** Present on `tool` messages: which tool call this result answers. */
29
+ tool_call_id?: string;
30
+ /** Optional name (used by some tool messages). */
31
+ name?: string;
32
+ }
33
+ /** A single tool call requested by the model. */
34
+ export interface ToolCall {
35
+ id: string;
36
+ type: 'function';
37
+ function: {
38
+ name: string;
39
+ /** JSON-encoded arguments string (may arrive in streamed pieces). */
40
+ arguments: string;
41
+ };
42
+ }
43
+ /** A streaming chunk in the OpenAI chat.completion.chunk shape (only the fields we consume). */
44
+ export interface ChatChunk {
45
+ choices?: Array<{
46
+ delta?: {
47
+ role?: string;
48
+ content?: string | null;
49
+ tool_calls?: Array<{
50
+ index: number;
51
+ id?: string;
52
+ type?: string;
53
+ function?: {
54
+ name?: string;
55
+ arguments?: string;
56
+ };
57
+ }>;
58
+ };
59
+ finish_reason?: string | null;
60
+ }>;
61
+ usage?: {
62
+ prompt_tokens?: number;
63
+ completion_tokens?: number;
64
+ } | null;
65
+ }
66
+ /** Request sent to the chat backend for one streamed model call. */
67
+ export interface ChatRequest {
68
+ model: string;
69
+ messages: ChatMessage[];
70
+ tools?: OpenAITool[];
71
+ tool_choice?: unknown;
72
+ stream: true;
73
+ /**
74
+ * OpenAI `response_format`. Forwarded verbatim by the backend to the upstream provider
75
+ * (raw-body passthrough). `exec --output-schema` sets a `json_schema` value here.
76
+ */
77
+ response_format?: unknown;
78
+ /** OpenAI streaming options (e.g. `{ include_usage: true }`). */
79
+ stream_options?: {
80
+ include_usage?: boolean;
81
+ };
82
+ }
83
+ /** OpenAI tool definition (function calling). */
84
+ export interface OpenAITool {
85
+ type: 'function';
86
+ function: {
87
+ name: string;
88
+ description?: string;
89
+ parameters?: Record<string, unknown>;
90
+ };
91
+ }
92
+ /**
93
+ * A client-side tool the model can call. Its `execute` runs locally in the CLI
94
+ * (e.g. submitting an image generation task), and the returned value is fed back
95
+ * to the model as the `tool` message content.
96
+ */
97
+ export interface ClientTool {
98
+ name: string;
99
+ description: string;
100
+ parameters: Record<string, unknown>;
101
+ execute(args: Record<string, unknown>): Promise<ToolResult>;
102
+ }
103
+ /** Result of running a client tool. Serialized to JSON as the tool message content. */
104
+ export type ToolResult = Record<string, unknown>;
105
+ /**
106
+ * Typed events emitted by the engine. Both the TUI and `exec` front-ends
107
+ * consume the same event stream, so their behavior stays consistent.
108
+ */
109
+ export type EngineEvent = {
110
+ type: 'assistant_delta';
111
+ text: string;
112
+ } | {
113
+ type: 'tool_call_begin';
114
+ id: string;
115
+ name: string;
116
+ args: Record<string, unknown>;
117
+ } | {
118
+ type: 'tool_call_end';
119
+ id: string;
120
+ name: string;
121
+ result: ToolResult;
122
+ } | {
123
+ type: 'usage';
124
+ promptTokens: number;
125
+ completionTokens: number;
126
+ } | {
127
+ type: 'turn_complete';
128
+ } | {
129
+ type: 'turn_interrupted';
130
+ } | {
131
+ type: 'error';
132
+ message: string;
133
+ };
134
+ /** The chat backend abstraction: yields parsed OpenAI chunks for one model call. */
135
+ export interface ChatBackend {
136
+ stream(req: ChatRequest, signal?: AbortSignal): AsyncIterable<ChatChunk>;
137
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Core domain types for the glbgpt CLI chat engine.
3
+ *
4
+ * The chat backend speaks the OpenAI-compatible chat/completions protocol
5
+ * (see backend `/ai-api/cli/v1/chat/completions`). The CLI owns the turn loop
6
+ * and the client-side tools; the backend is a stateless streaming passthrough.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Input assembly for `glbgpt exec`: prompt/stdin resolution, `--file` context injection,
3
+ * and `--output-schema` loading. Kept free of process/network concerns so it is unit-testable;
4
+ * stdin, file reads, and the TTY check are injected.
5
+ */
6
+ /** A user/input error. The caller maps this to a stderr message + exit code 1. */
7
+ export declare class UsageError extends Error {
8
+ }
9
+ export interface PromptSources {
10
+ /** The positional PROMPT arg, if any. The literal `-` forces reading from stdin. */
11
+ positional?: string;
12
+ /** Whether stdin is a TTY (interactive) rather than piped. */
13
+ stdinIsTty: boolean;
14
+ /** Read all of stdin as a string. Called at most once. */
15
+ readStdin: () => Promise<string>;
16
+ }
17
+ /**
18
+ * Resolve the prompt text following the exec stdin contract:
19
+ * - positional `-` → stdin is the prompt (forced).
20
+ * - positional + piped stdin → stdin appended as a `<stdin>` block.
21
+ * - positional + TTY stdin → positional only.
22
+ * - no positional + piped → stdin is the prompt.
23
+ * - no positional + TTY → UsageError.
24
+ */
25
+ export declare function resolvePromptText(src: PromptSources): Promise<string>;
26
+ /**
27
+ * Append each `--file` as a `<file path="...">…</file>` block to the prompt. Reading is injected
28
+ * for tests; a read failure raises UsageError (unreadable input is a user error, exit 1).
29
+ */
30
+ export declare function injectFiles(base: string, files: string[], readFile?: (p: string) => Promise<string>): Promise<string>;
31
+ /**
32
+ * Load a JSON Schema file and wrap it as an OpenAI `response_format` value. A missing file or
33
+ * invalid JSON is a UsageError (exit 1). `strict: true` asks the provider to enforce the schema.
34
+ */
35
+ export declare function loadOutputSchema(schemaPath: string, readFile?: (p: string) => Promise<string>): Promise<unknown>;
@@ -0,0 +1,85 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Input assembly for `glbgpt exec`: prompt/stdin resolution, `--file` context injection,
5
+ * and `--output-schema` loading. Kept free of process/network concerns so it is unit-testable;
6
+ * stdin, file reads, and the TTY check are injected.
7
+ */
8
+ /** A user/input error. The caller maps this to a stderr message + exit code 1. */
9
+ export class UsageError extends Error {
10
+ }
11
+ const NO_PROMPT = 'No prompt provided. Either specify one as an argument or pipe the prompt into stdin.';
12
+ /**
13
+ * Resolve the prompt text following the exec stdin contract:
14
+ * - positional `-` → stdin is the prompt (forced).
15
+ * - positional + piped stdin → stdin appended as a `<stdin>` block.
16
+ * - positional + TTY stdin → positional only.
17
+ * - no positional + piped → stdin is the prompt.
18
+ * - no positional + TTY → UsageError.
19
+ */
20
+ export async function resolvePromptText(src) {
21
+ const positional = src.positional?.trim() ? src.positional : src.positional === '-' ? '-' : undefined;
22
+ if (positional === '-') {
23
+ const stdin = (await src.readStdin()).trim();
24
+ if (!stdin)
25
+ throw new UsageError(NO_PROMPT);
26
+ return stdin;
27
+ }
28
+ if (positional) {
29
+ if (!src.stdinIsTty) {
30
+ const stdin = (await src.readStdin()).trim();
31
+ if (stdin)
32
+ return `${positional}\n\n<stdin>\n${stdin}\n</stdin>`;
33
+ }
34
+ return positional;
35
+ }
36
+ if (src.stdinIsTty)
37
+ throw new UsageError(NO_PROMPT);
38
+ const stdin = (await src.readStdin()).trim();
39
+ if (!stdin)
40
+ throw new UsageError(NO_PROMPT);
41
+ return stdin;
42
+ }
43
+ /**
44
+ * Append each `--file` as a `<file path="...">…</file>` block to the prompt. Reading is injected
45
+ * for tests; a read failure raises UsageError (unreadable input is a user error, exit 1).
46
+ */
47
+ export async function injectFiles(base, files, readFile = (p) => fs.readFile(p, 'utf8')) {
48
+ if (files.length === 0)
49
+ return base;
50
+ const blocks = [];
51
+ for (const file of files) {
52
+ let content;
53
+ try {
54
+ content = await readFile(file);
55
+ }
56
+ catch (err) {
57
+ throw new UsageError(`Cannot read --file ${file}: ${err instanceof Error ? err.message : String(err)}`);
58
+ }
59
+ blocks.push(`<file path="${path.basename(file)}">\n${content}\n</file>`);
60
+ }
61
+ return `${base}\n\n${blocks.join('\n\n')}`;
62
+ }
63
+ /**
64
+ * Load a JSON Schema file and wrap it as an OpenAI `response_format` value. A missing file or
65
+ * invalid JSON is a UsageError (exit 1). `strict: true` asks the provider to enforce the schema.
66
+ */
67
+ export async function loadOutputSchema(schemaPath, readFile = (p) => fs.readFile(p, 'utf8')) {
68
+ let raw;
69
+ try {
70
+ raw = await readFile(schemaPath);
71
+ }
72
+ catch (err) {
73
+ throw new UsageError(`Cannot read --output-schema ${schemaPath}: ${err instanceof Error ? err.message : String(err)}`);
74
+ }
75
+ let schema;
76
+ try {
77
+ schema = JSON.parse(raw);
78
+ }
79
+ catch (err) {
80
+ throw new UsageError(`--output-schema ${schemaPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
81
+ }
82
+ const name = path.basename(schemaPath).replace(/\.json$/i, '').replace(/[^a-zA-Z0-9_-]/g, '_') || 'output';
83
+ return { type: 'json_schema', json_schema: { name, strict: true, schema } };
84
+ }
85
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../../src/exec/input.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Output emitters for `glbgpt exec`. Three modes share one accumulator:
3
+ * - `text` (default): the final message as plain text on stdout.
4
+ * - `json` : a single final result object on stdout.
5
+ * - `jsonl` : streaming protocol events, one JSON per line, on stdout.
6
+ *
7
+ * In every mode stdout carries only the result/events; progress and diagnostics go to stderr.
8
+ * The write sinks are injected so tests can capture output without touching process streams.
9
+ */
10
+ export type OutputMode = 'text' | 'json' | 'jsonl';
11
+ export interface OutputSinks {
12
+ out: (s: string) => void;
13
+ err: (s: string) => void;
14
+ }
15
+ export interface ExecUsage {
16
+ prompt_tokens: number;
17
+ completion_tokens: number;
18
+ }
19
+ export declare class ExecOutput {
20
+ private readonly mode;
21
+ private readonly model;
22
+ private readonly sinks;
23
+ private content;
24
+ private usage;
25
+ constructor(mode: OutputMode, model: string, sinks: OutputSinks);
26
+ /** Emitted once before streaming (jsonl only). */
27
+ start(): void;
28
+ /** An assistant text delta. */
29
+ delta(text: string): void;
30
+ /** Token usage from the final stream chunk. */
31
+ usageEvent(promptTokens: number, completionTokens: number): void;
32
+ /** Successful completion. `structured` is the parsed object when --output-schema was used. */
33
+ finishResult(structured: unknown | null): void;
34
+ /** Turn failed. Keeps the active output structure and returns via a non-zero exit upstream. */
35
+ finishError(message: string): void;
36
+ /** Accumulated assistant text (used to write the --output-last-message file). */
37
+ getContent(): string;
38
+ private line;
39
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Output emitters for `glbgpt exec`. Three modes share one accumulator:
3
+ * - `text` (default): the final message as plain text on stdout.
4
+ * - `json` : a single final result object on stdout.
5
+ * - `jsonl` : streaming protocol events, one JSON per line, on stdout.
6
+ *
7
+ * In every mode stdout carries only the result/events; progress and diagnostics go to stderr.
8
+ * The write sinks are injected so tests can capture output without touching process streams.
9
+ */
10
+ export class ExecOutput {
11
+ mode;
12
+ model;
13
+ sinks;
14
+ content = '';
15
+ usage = { prompt_tokens: 0, completion_tokens: 0 };
16
+ constructor(mode, model, sinks) {
17
+ this.mode = mode;
18
+ this.model = model;
19
+ this.sinks = sinks;
20
+ }
21
+ /** Emitted once before streaming (jsonl only). */
22
+ start() {
23
+ if (this.mode === 'jsonl')
24
+ this.line({ type: 'turn_started', model: this.model });
25
+ }
26
+ /** An assistant text delta. */
27
+ delta(text) {
28
+ if (!text)
29
+ return;
30
+ this.content += text;
31
+ if (this.mode === 'jsonl')
32
+ this.line({ type: 'delta', text });
33
+ }
34
+ /** Token usage from the final stream chunk. */
35
+ usageEvent(promptTokens, completionTokens) {
36
+ this.usage = { prompt_tokens: promptTokens, completion_tokens: completionTokens };
37
+ if (this.mode === 'jsonl') {
38
+ this.line({ type: 'usage', prompt_tokens: promptTokens, completion_tokens: completionTokens });
39
+ }
40
+ }
41
+ /** Successful completion. `structured` is the parsed object when --output-schema was used. */
42
+ finishResult(structured) {
43
+ if (this.mode === 'text') {
44
+ if (this.content)
45
+ this.sinks.out(this.content.endsWith('\n') ? this.content : `${this.content}\n`);
46
+ return;
47
+ }
48
+ this.line({
49
+ type: 'result',
50
+ model: this.model,
51
+ content: this.content,
52
+ structured,
53
+ usage: this.usage,
54
+ error: null,
55
+ });
56
+ }
57
+ /** Turn failed. Keeps the active output structure and returns via a non-zero exit upstream. */
58
+ finishError(message) {
59
+ if (this.mode === 'json') {
60
+ this.line({
61
+ type: 'result',
62
+ model: this.model,
63
+ content: this.content,
64
+ structured: null,
65
+ usage: this.usage,
66
+ error: { message },
67
+ });
68
+ }
69
+ else if (this.mode === 'jsonl') {
70
+ this.line({ type: 'error', message });
71
+ }
72
+ else {
73
+ this.sinks.err(`${message}\n`);
74
+ }
75
+ }
76
+ /** Accumulated assistant text (used to write the --output-last-message file). */
77
+ getContent() {
78
+ return this.content;
79
+ }
80
+ line(obj) {
81
+ this.sinks.out(`${JSON.stringify(obj)}\n`);
82
+ }
83
+ }
84
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/exec/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};