@oracle-agent/oracle 0.9.6 → 0.9.8

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.
@@ -0,0 +1,501 @@
1
+ const EMPTY = Buffer.alloc(0);
2
+
3
+ const SPECIAL_NAMES = new Set([
4
+ "return",
5
+ "backspace",
6
+ "tab",
7
+ "escape",
8
+ "up",
9
+ "down",
10
+ "left",
11
+ "right",
12
+ "home",
13
+ "end",
14
+ "delete",
15
+ "insert",
16
+ "pageup",
17
+ "pagedown",
18
+ "unknown",
19
+ ]);
20
+
21
+ const CSI_FINALS = {
22
+ A: "up",
23
+ B: "down",
24
+ C: "right",
25
+ D: "left",
26
+ H: "home",
27
+ F: "end",
28
+ P: "f1",
29
+ Q: "f2",
30
+ R: "f3",
31
+ S: "f4",
32
+ };
33
+
34
+ const TILDE_CODES = {
35
+ 1: "home",
36
+ 2: "insert",
37
+ 3: "delete",
38
+ 4: "end",
39
+ 5: "pageup",
40
+ 6: "pagedown",
41
+ 7: "home",
42
+ 8: "end",
43
+ };
44
+
45
+ function makeKey({ name = null, ctrl = false, meta = false, shift = false, sequence = "" }) {
46
+ return { name, ctrl, meta, shift, sequence };
47
+ }
48
+
49
+ function utf8Length(lead) {
50
+ if (lead < 0x80) return 1;
51
+ if (lead >= 0xf0) return 4;
52
+ if (lead >= 0xe0) return 3;
53
+ if (lead >= 0xc0) return 2;
54
+ return 1;
55
+ }
56
+
57
+ function isFinalByte(b) {
58
+ return b >= 0x40 && b <= 0x7e;
59
+ }
60
+
61
+ function charKey(text) {
62
+ const lower = text.toLowerCase();
63
+ return makeKey({
64
+ name: lower,
65
+ shift: text !== lower,
66
+ sequence: text,
67
+ });
68
+ }
69
+
70
+ function controlKey(b) {
71
+ if (b === 0x0d) return makeKey({ name: "return", sequence: "\r" });
72
+ if (b === 0x0a) return makeKey({ name: "return", sequence: "\n" });
73
+ if (b === 0x09) return makeKey({ name: "tab", sequence: "\t" });
74
+ if (b === 0x7f) return makeKey({ name: "backspace", sequence: "\u007f" });
75
+ if (b === 0x08) return makeKey({ name: "backspace", sequence: "\b" });
76
+ if (b >= 0x01 && b <= 0x1a) {
77
+ return makeKey({
78
+ name: String.fromCharCode(b + 0x60),
79
+ ctrl: true,
80
+ sequence: String.fromCharCode(b),
81
+ });
82
+ }
83
+ return makeKey({ name: "unknown", sequence: String.fromCharCode(b) });
84
+ }
85
+
86
+ function csiKey(sequence) {
87
+ const match = /^\u001b[\[O](\d*)(?:;(\d+))?(.)$/s.exec(sequence);
88
+ if (!match) return makeKey({ name: "unknown", sequence });
89
+ const first = match[1] === "" ? null : Number(match[1]);
90
+ const second = match[2] === undefined ? null : Number(match[2]);
91
+ const final = match[3];
92
+
93
+ let name = null;
94
+ let modifier = second;
95
+ if (final === "~") {
96
+ name = TILDE_CODES[first] ?? null;
97
+ } else {
98
+ name = CSI_FINALS[final] ?? null;
99
+ if (modifier === null && first !== null && first > 1) modifier = first;
100
+ }
101
+ if (!name) return makeKey({ name: "unknown", sequence });
102
+
103
+ const bits = modifier === null ? 0 : Math.max(0, modifier - 1);
104
+ return makeKey({
105
+ name,
106
+ shift: (bits & 1) !== 0,
107
+ meta: (bits & 2) !== 0,
108
+ ctrl: (bits & 4) !== 0,
109
+ sequence,
110
+ });
111
+ }
112
+
113
+ function decodeEscape(buf, start, keys, flush) {
114
+ const escape = () => {
115
+ keys.push(makeKey({ name: "escape", sequence: "\u001b" }));
116
+ return 1;
117
+ };
118
+ if (start + 1 >= buf.length) return flush ? escape() : 0;
119
+
120
+ const next = buf[start + 1];
121
+ if (next === 0x5b || next === 0x4f) {
122
+ let j = start + 2;
123
+ while (j < buf.length && !isFinalByte(buf[j])) j += 1;
124
+ if (j >= buf.length) return flush ? escape() : 0;
125
+ keys.push(csiKey(buf.toString("utf8", start, j + 1)));
126
+ return j + 1 - start;
127
+ }
128
+ if (next === 0x1b) return escape();
129
+
130
+ const width = utf8Length(next);
131
+ if (start + 1 + width > buf.length) return flush ? escape() : 0;
132
+ const text = buf.toString("utf8", start + 1, start + 1 + width);
133
+ const lower = text.toLowerCase();
134
+ keys.push(makeKey({
135
+ name: lower,
136
+ meta: true,
137
+ shift: text !== lower,
138
+ sequence: `\u001b${text}`,
139
+ }));
140
+ return 1 + width;
141
+ }
142
+
143
+ function decodeChunk(buf, flush) {
144
+ const keys = [];
145
+ let i = 0;
146
+ while (i < buf.length) {
147
+ const b = buf[i];
148
+ if (b === 0x1b) {
149
+ const consumed = decodeEscape(buf, i, keys, flush);
150
+ if (consumed === 0) return { keys, rest: Buffer.from(buf.subarray(i)) };
151
+ i += consumed;
152
+ continue;
153
+ }
154
+ if (b < 0x20 || b === 0x7f) {
155
+ keys.push(controlKey(b));
156
+ i += 1;
157
+ continue;
158
+ }
159
+ const width = utf8Length(b);
160
+ if (i + width > buf.length) {
161
+ if (!flush) return { keys, rest: Buffer.from(buf.subarray(i)) };
162
+ keys.push(charKey(buf.toString("utf8", i)));
163
+ return { keys, rest: EMPTY };
164
+ }
165
+ keys.push(charKey(buf.toString("utf8", i, i + width)));
166
+ i += width;
167
+ }
168
+ return { keys, rest: EMPTY };
169
+ }
170
+
171
+ function toBuffer(chunk) {
172
+ if (Buffer.isBuffer(chunk)) return chunk;
173
+ if (chunk === null || chunk === undefined) return EMPTY;
174
+ if (typeof chunk === "string") return Buffer.from(chunk, "utf8");
175
+ return Buffer.from(chunk);
176
+ }
177
+
178
+ let decodeKeysPending = EMPTY;
179
+
180
+ export function decodeKeys(chunk) {
181
+ const buf = decodeKeysPending.length === 0
182
+ ? toBuffer(chunk)
183
+ : Buffer.concat([decodeKeysPending, toBuffer(chunk)]);
184
+ const { keys, rest } = decodeChunk(buf, false);
185
+ if (rest.length === 1 && rest[0] === 0x1b) {
186
+ decodeKeysPending = EMPTY;
187
+ return [...keys, makeKey({ name: "escape", sequence: "\u001b" })];
188
+ }
189
+ decodeKeysPending = rest;
190
+ return keys;
191
+ }
192
+
193
+ export function createDecoder() {
194
+ let pending = EMPTY;
195
+ return {
196
+ push(chunk) {
197
+ const buf = pending.length === 0
198
+ ? toBuffer(chunk)
199
+ : Buffer.concat([pending, toBuffer(chunk)]);
200
+ const { keys, rest } = decodeChunk(buf, false);
201
+ pending = rest;
202
+ return keys;
203
+ },
204
+ flush() {
205
+ if (pending.length === 0) return [];
206
+ const { keys } = decodeChunk(pending, true);
207
+ pending = EMPTY;
208
+ return keys;
209
+ },
210
+ get pending() {
211
+ return pending.length;
212
+ },
213
+ };
214
+ }
215
+
216
+ export function createEditorState({ history = [] } = {}) {
217
+ const entries = [...history];
218
+ return {
219
+ buffer: "",
220
+ cursor: 0,
221
+ history: entries,
222
+ historyIndex: entries.length,
223
+ draft: "",
224
+ };
225
+ }
226
+
227
+ function stepLeft(text, index) {
228
+ if (index <= 0) return 0;
229
+ const prev = index - 1;
230
+ const code = text.charCodeAt(prev);
231
+ if (code >= 0xdc00 && code <= 0xdfff && prev > 0) {
232
+ const lead = text.charCodeAt(prev - 1);
233
+ if (lead >= 0xd800 && lead <= 0xdbff) return prev - 1;
234
+ }
235
+ return prev;
236
+ }
237
+
238
+ function stepRight(text, index) {
239
+ if (index >= text.length) return text.length;
240
+ const code = text.charCodeAt(index);
241
+ if (code >= 0xd800 && code <= 0xdbff && index + 1 < text.length) return index + 2;
242
+ return index + 1;
243
+ }
244
+
245
+ function next(state, patch) {
246
+ return { ...state, ...patch };
247
+ }
248
+
249
+ function live(state, patch) {
250
+ return next(state, { ...patch, historyIndex: state.history.length, draft: "" });
251
+ }
252
+
253
+ function isPrintable(key) {
254
+ if (key.ctrl || key.meta) return false;
255
+ if (!key.sequence) return false;
256
+ if (SPECIAL_NAMES.has(key.name)) return false;
257
+ for (const ch of key.sequence) {
258
+ const code = ch.codePointAt(0);
259
+ if (code < 0x20 || code === 0x7f) return false;
260
+ }
261
+ return true;
262
+ }
263
+
264
+ function killPrevWord(text, cursor) {
265
+ let i = cursor;
266
+ while (i > 0 && /\s/.test(text[i - 1])) i -= 1;
267
+ while (i > 0 && !/\s/.test(text[i - 1])) i -= 1;
268
+ return i;
269
+ }
270
+
271
+ function historyUp(state) {
272
+ if (state.historyIndex <= 0) return { state: next(state, {}), action: null };
273
+ const atLive = state.historyIndex >= state.history.length;
274
+ const index = state.historyIndex - 1;
275
+ const buffer = state.history[index] ?? "";
276
+ return {
277
+ state: next(state, {
278
+ buffer,
279
+ cursor: buffer.length,
280
+ historyIndex: index,
281
+ draft: atLive ? state.buffer : state.draft,
282
+ }),
283
+ action: null,
284
+ };
285
+ }
286
+
287
+ function historyDown(state) {
288
+ if (state.historyIndex >= state.history.length) return { state: next(state, {}), action: null };
289
+ const index = state.historyIndex + 1;
290
+ const restored = index >= state.history.length;
291
+ const buffer = restored ? state.draft : (state.history[index] ?? "");
292
+ return {
293
+ state: next(state, {
294
+ buffer,
295
+ cursor: buffer.length,
296
+ historyIndex: index,
297
+ draft: restored ? "" : state.draft,
298
+ }),
299
+ action: null,
300
+ };
301
+ }
302
+
303
+ function submit(state) {
304
+ const value = state.buffer.trim();
305
+ if (value === "") {
306
+ return {
307
+ state: live(state, { buffer: "", cursor: 0 }),
308
+ action: null,
309
+ };
310
+ }
311
+ const history = state.history[state.history.length - 1] === value
312
+ ? [...state.history]
313
+ : [...state.history, value];
314
+ return {
315
+ state: next(state, {
316
+ buffer: "",
317
+ cursor: 0,
318
+ history,
319
+ historyIndex: history.length,
320
+ draft: "",
321
+ }),
322
+ action: { type: "submit", value },
323
+ };
324
+ }
325
+
326
+ export function applyKey(state, key) {
327
+ if (!key) return { state: next(state, {}), action: null };
328
+ const { buffer, cursor } = state;
329
+
330
+ if (key.ctrl && !key.meta) {
331
+ switch (key.name) {
332
+ case "c":
333
+ return { state: live(state, { buffer: "", cursor: 0 }), action: { type: "cancel" } };
334
+ case "d":
335
+ if (buffer === "") return { state: next(state, {}), action: { type: "eof" } };
336
+ return { state: next(state, {}), action: null };
337
+ case "l":
338
+ return { state: next(state, {}), action: { type: "clear" } };
339
+ case "a":
340
+ return { state: next(state, { cursor: 0 }), action: null };
341
+ case "e":
342
+ return { state: next(state, { cursor: buffer.length }), action: null };
343
+ case "u":
344
+ return {
345
+ state: next(state, { buffer: buffer.slice(cursor), cursor: 0 }),
346
+ action: null,
347
+ };
348
+ case "k":
349
+ return { state: next(state, { buffer: buffer.slice(0, cursor) }), action: null };
350
+ case "w": {
351
+ const start = killPrevWord(buffer, cursor);
352
+ return {
353
+ state: next(state, {
354
+ buffer: buffer.slice(0, start) + buffer.slice(cursor),
355
+ cursor: start,
356
+ }),
357
+ action: null,
358
+ };
359
+ }
360
+ case "b":
361
+ return { state: next(state, { cursor: stepLeft(buffer, cursor) }), action: null };
362
+ case "f":
363
+ return { state: next(state, { cursor: stepRight(buffer, cursor) }), action: null };
364
+ default:
365
+ return { state: next(state, {}), action: null };
366
+ }
367
+ }
368
+
369
+ switch (key.name) {
370
+ case "return":
371
+ return submit(state);
372
+ case "left":
373
+ return { state: next(state, { cursor: stepLeft(buffer, cursor) }), action: null };
374
+ case "right":
375
+ return { state: next(state, { cursor: stepRight(buffer, cursor) }), action: null };
376
+ case "home":
377
+ return { state: next(state, { cursor: 0 }), action: null };
378
+ case "end":
379
+ return { state: next(state, { cursor: buffer.length }), action: null };
380
+ case "up":
381
+ return historyUp(state);
382
+ case "down":
383
+ return historyDown(state);
384
+ case "backspace": {
385
+ if (cursor === 0) return { state: next(state, {}), action: null };
386
+ const start = stepLeft(buffer, cursor);
387
+ return {
388
+ state: next(state, {
389
+ buffer: buffer.slice(0, start) + buffer.slice(cursor),
390
+ cursor: start,
391
+ }),
392
+ action: null,
393
+ };
394
+ }
395
+ case "delete": {
396
+ if (cursor >= buffer.length) return { state: next(state, {}), action: null };
397
+ const end = stepRight(buffer, cursor);
398
+ return {
399
+ state: next(state, { buffer: buffer.slice(0, cursor) + buffer.slice(end) }),
400
+ action: null,
401
+ };
402
+ }
403
+ default:
404
+ break;
405
+ }
406
+
407
+ if (isPrintable(key)) {
408
+ const text = key.sequence;
409
+ return {
410
+ state: next(state, {
411
+ buffer: buffer.slice(0, cursor) + text + buffer.slice(cursor),
412
+ cursor: cursor + text.length,
413
+ }),
414
+ action: null,
415
+ };
416
+ }
417
+
418
+ return { state: next(state, {}), action: null };
419
+ }
420
+
421
+ export function createInput(options = {}) {
422
+ const {
423
+ stdin,
424
+ stdout,
425
+ history = [],
426
+ onSubmit,
427
+ onCancel,
428
+ onEof,
429
+ onClear,
430
+ onRender,
431
+ } = options;
432
+
433
+ const input = stdin ?? process.stdin;
434
+ const output = stdout ?? process.stdout;
435
+ const decoder = createDecoder();
436
+ const handlers = {
437
+ submit: onSubmit,
438
+ cancel: onCancel,
439
+ eof: onEof,
440
+ clear: onClear,
441
+ };
442
+
443
+ let state = createEditorState({ history });
444
+ let started = false;
445
+ let rawApplied = false;
446
+ let priorRaw = false;
447
+
448
+ const emit = (action) => {
449
+ if (!action) return;
450
+ const handler = handlers[action.type];
451
+ if (typeof handler === "function") handler(action.value, state);
452
+ };
453
+
454
+ const onData = (chunk) => {
455
+ for (const key of decoder.push(chunk)) {
456
+ const result = applyKey(state, key);
457
+ state = result.state;
458
+ emit(result.action);
459
+ if (typeof onRender === "function") onRender(state);
460
+ }
461
+ };
462
+
463
+ return {
464
+ start() {
465
+ if (started) return;
466
+ started = true;
467
+ if (input.isTTY && typeof input.setRawMode === "function") {
468
+ priorRaw = input.isRaw === true;
469
+ input.setRawMode(true);
470
+ rawApplied = true;
471
+ }
472
+ if (typeof input.resume === "function") input.resume();
473
+ input.on("data", onData);
474
+ if (typeof onRender === "function") onRender(state);
475
+ },
476
+ stop() {
477
+ if (!started) return;
478
+ started = false;
479
+ if (typeof input.off === "function") input.off("data", onData);
480
+ else if (typeof input.removeListener === "function") input.removeListener("data", onData);
481
+ if (rawApplied && typeof input.setRawMode === "function") {
482
+ input.setRawMode(priorRaw);
483
+ rawApplied = false;
484
+ }
485
+ if (typeof input.pause === "function") input.pause();
486
+ },
487
+ getState() {
488
+ return state;
489
+ },
490
+ setState(nextState) {
491
+ state = nextState;
492
+ return state;
493
+ },
494
+ write(text) {
495
+ if (output && typeof output.write === "function") output.write(text);
496
+ },
497
+ get isRunning() {
498
+ return started;
499
+ },
500
+ };
501
+ }