@leg3ndy/otto-bridge 0.9.2 → 1.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/README.md +78 -17
- package/dist/agentic_runtime/patch/structured_patch.js +240 -0
- package/dist/agentic_runtime/workspace/manager.js +1044 -0
- package/dist/chat_cli_client.js +91 -0
- package/dist/cli_terminal.js +668 -0
- package/dist/executors/native_macos.js +2778 -115
- package/dist/local_automations.js +33 -11
- package/dist/main.js +25 -3
- package/dist/runtime.js +136 -32
- package/dist/runtime_cli_client.js +18 -0
- package/dist/runtime_contract.js +516 -0
- package/dist/tool_catalog.js +148 -1
- package/dist/types.js +2 -2
- package/package.json +7 -2
- package/scripts/postinstall.mjs +35 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
function normalizeBaseUrl(apiBaseUrl) {
|
|
2
|
+
return String(apiBaseUrl || "").trim().replace(/\/+$/, "");
|
|
3
|
+
}
|
|
4
|
+
function buildDeviceAuthHeaders(deviceToken, headers) {
|
|
5
|
+
const next = new Headers(headers || {});
|
|
6
|
+
if (deviceToken) {
|
|
7
|
+
next.set("Authorization", `Bearer ${deviceToken}`);
|
|
8
|
+
}
|
|
9
|
+
return next;
|
|
10
|
+
}
|
|
11
|
+
function parseApiError(payload, fallbackStatus) {
|
|
12
|
+
if (payload && typeof payload === "object") {
|
|
13
|
+
const detail = "detail" in payload ? payload.detail : null;
|
|
14
|
+
if (typeof detail === "string" && detail.trim()) {
|
|
15
|
+
return new Error(detail.trim());
|
|
16
|
+
}
|
|
17
|
+
if (detail && typeof detail === "object") {
|
|
18
|
+
const message = "message" in detail ? detail.message : null;
|
|
19
|
+
if (typeof message === "string" && message.trim()) {
|
|
20
|
+
return new Error(message.trim());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const error = "error" in payload ? payload.error : null;
|
|
24
|
+
if (typeof error === "string" && error.trim()) {
|
|
25
|
+
return new Error(error.trim());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return new Error(`HTTP ${fallbackStatus}`);
|
|
29
|
+
}
|
|
30
|
+
export async function streamDeviceCliChat(config, request, onEvent) {
|
|
31
|
+
const url = `${normalizeBaseUrl(config.apiBaseUrl)}/v1/devices/cli/chat/completions`;
|
|
32
|
+
let response;
|
|
33
|
+
try {
|
|
34
|
+
response = await fetch(url, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: buildDeviceAuthHeaders(config.deviceToken, {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
}),
|
|
39
|
+
body: JSON.stringify(request),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
44
|
+
throw new Error(`Request failed for ${url}: ${detail}`);
|
|
45
|
+
}
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const payload = await response.json().catch(() => null);
|
|
48
|
+
throw parseApiError(payload, response.status);
|
|
49
|
+
}
|
|
50
|
+
if (!response.body) {
|
|
51
|
+
throw new Error("Empty stream response");
|
|
52
|
+
}
|
|
53
|
+
const reader = response.body.getReader();
|
|
54
|
+
const decoder = new TextDecoder();
|
|
55
|
+
let buffer = "";
|
|
56
|
+
const flushBlock = async (rawBlock) => {
|
|
57
|
+
const lines = rawBlock
|
|
58
|
+
.split("\n")
|
|
59
|
+
.map((line) => line.trimEnd())
|
|
60
|
+
.filter((line) => line.startsWith("data:"));
|
|
61
|
+
if (!lines.length) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const payloadText = lines
|
|
65
|
+
.map((line) => line.slice(5).trimStart())
|
|
66
|
+
.join("\n")
|
|
67
|
+
.trim();
|
|
68
|
+
if (!payloadText) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const payload = JSON.parse(payloadText);
|
|
72
|
+
await onEvent(payload);
|
|
73
|
+
};
|
|
74
|
+
for (;;) {
|
|
75
|
+
const { done, value } = await reader.read();
|
|
76
|
+
buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
|
|
77
|
+
let separatorIndex = buffer.indexOf("\n\n");
|
|
78
|
+
while (separatorIndex >= 0) {
|
|
79
|
+
const block = buffer.slice(0, separatorIndex);
|
|
80
|
+
buffer = buffer.slice(separatorIndex + 2);
|
|
81
|
+
await flushBlock(block);
|
|
82
|
+
separatorIndex = buffer.indexOf("\n\n");
|
|
83
|
+
}
|
|
84
|
+
if (done) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (buffer.trim()) {
|
|
89
|
+
await flushBlock(buffer);
|
|
90
|
+
}
|
|
91
|
+
}
|